代码之家  ›  专栏  ›  技术社区  ›  Huskie69

对网格上的材质应用颜色渐变-three.js

  •  6
  • Huskie69  · 技术社区  · 6 年前

    我有一个STL文件加载到我的场景中,在一个声音材料上应用了一种颜色。

    我想用一种方法将两种颜色应用到这个网格的材质上,并在Z轴A上应用渐变效果,如下例所示。 Gradient Vase ] 1

    我有一种感觉,我可能不得不介绍shaders,但我还没有对three.js进行深入研究。

    3 回复  |  直到 6 年前
        1
  •  9
  •   prisoner849    6 年前

    基于UV的简单渐变明暗器:

    var scene = new THREE.Scene();
    var camera = new THREE.PerspectiveCamera(60, 1, 1, 1000);
    camera.position.set(13, 25, 38);
    camera.lookAt(scene.position);
    var renderer = new THREE.WebGLRenderer({
      antialias: true
    });
    var canvas = renderer.domElement
    document.body.appendChild(canvas);
    
    var controls = new THREE.OrbitControls(camera, renderer.domElement);
    
    var geometry = new THREE.CylinderBufferGeometry(2, 5, 20, 32, 1, true);
    var material = new THREE.ShaderMaterial({
      uniforms: {
        color1: {
          value: new THREE.Color("red")
        },
        color2: {
          value: new THREE.Color("purple")
        }
      },
      vertexShader: `
        varying vec2 vUv;
    
        void main() {
          vUv = uv;
          gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
        }
      `,
      fragmentShader: `
        uniform vec3 color1;
        uniform vec3 color2;
      
        varying vec2 vUv;
        
        void main() {
          
          gl_FragColor = vec4(mix(color1, color2, vUv.y), 1.0);
        }
      `,
      wireframe: true
    });
    var mesh = new THREE.Mesh(geometry, material);
    scene.add(mesh);
    
    
    
    render();
    
    function resize(renderer) {
      const canvas = renderer.domElement;
      const width = canvas.clientWidth;
      const height = canvas.clientHeight;
      const needResize = canvas.width !== width || canvas.height !== height;
      if (needResize) {
        renderer.setSize(width, height, false);
      }
      return needResize;
    }
    
    function render() {
      if (resize(renderer)) {
        camera.aspect = canvas.clientWidth / canvas.clientHeight;
        camera.updateProjectionMatrix();
      }
      renderer.render(scene, camera);
      requestAnimationFrame(render);
    }
    html,
    body {
      height: 100%;
      margin: 0;
      overflow: hidden;
    }
    
    canvas {
      width: 100%;
      height: 100%;
      display;
      block;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.min.js"></script>
    <script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

    基于坐标的简单渐变明暗器:

    var scene = new THREE.Scene();
    var camera = new THREE.PerspectiveCamera(60, 1, 1, 1000);
    camera.position.set(13, 25, 38);
    camera.lookAt(scene.position);
    var renderer = new THREE.WebGLRenderer({
      antialias: true
    });
    var canvas = renderer.domElement
    document.body.appendChild(canvas);
    
    var controls = new THREE.OrbitControls(camera, renderer.domElement);
    
    var geometry = new THREE.CylinderBufferGeometry(2, 5, 20, 16, 4, true);
    geometry.computeBoundingBox();
    var material = new THREE.ShaderMaterial({
      uniforms: {
        color1: {
          value: new THREE.Color("red")
        },
        color2: {
          value: new THREE.Color("purple")
        },
        bboxMin: {
          value: geometry.boundingBox.min
        },
        bboxMax: {
          value: geometry.boundingBox.max
        }
      },
      vertexShader: `
        uniform vec3 bboxMin;
        uniform vec3 bboxMax;
      
        varying vec2 vUv;
    
        void main() {
          vUv.y = (position.y - bboxMin.y) / (bboxMax.y - bboxMin.y);
          gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
        }
      `,
      fragmentShader: `
        uniform vec3 color1;
        uniform vec3 color2;
      
        varying vec2 vUv;
        
        void main() {
          
          gl_FragColor = vec4(mix(color1, color2, vUv.y), 1.0);
        }
      `,
      wireframe: true
    });
    var mesh = new THREE.Mesh(geometry, material);
    scene.add(mesh);
    
    
    
    render();
    
    function resize(renderer) {
      const canvas = renderer.domElement;
      const width = canvas.clientWidth;
      const height = canvas.clientHeight;
      const needResize = canvas.width !== width || canvas.height !== height;
      if (needResize) {
        renderer.setSize(width, height, false);
      }
      return needResize;
    }
    
    function render() {
      if (resize(renderer)) {
        camera.aspect = canvas.clientWidth / canvas.clientHeight;
        camera.updateProjectionMatrix();
      }
      renderer.render(scene, camera);
      requestAnimationFrame(render);
    }
    html,
    body {
      height: 100%;
      margin: 0;
      overflow: hidden;
    }
    
    canvas {
      width: 100%;
      height: 100%;
      display: block;
    }
    <script src=“https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.min.js”></script>
    <script src=“https://threejs.org/examples/js/controls/orbitcontrols.js”></script>

    顶点颜色渐变:

    var scene = new THREE.Scene();
    var camera = new THREE.PerspectiveCamera(60, 1, 1, 1000);
    camera.position.set(0, 0, 10);
    var renderer = new THREE.WebGLRenderer({
      antialias: true
    });
    var canvas = renderer.domElement
    document.body.appendChild(canvas);
    
    
    var geom = new THREE.TorusKnotGeometry(2.5, .5, 100, 16);
    
    var rev = true;
    
    var cols = [{
      stop: 0,
      color: new THREE.Color(0xf7b000)
    }, {
      stop: .25,
      color: new THREE.Color(0xdd0080)
    }, {
      stop: .5,
      color: new THREE.Color(0x622b85)
    }, {
      stop: .75,
      color: new THREE.Color(0x007dae)
    }, {
      stop: 1,
      color: new THREE.Color(0x77c8db)
    }];
    
    setGradient(geom, cols, 'z', rev);
    
    function setGradient(geometry, colors, axis, reverse) {
    
      geometry.computeBoundingBox();
    
      var bbox = geometry.boundingBox;
      var size = new THREE.Vector3().subVectors(bbox.max, bbox.min);
    
      var vertexIndices = ['a', 'b', 'c'];
      var face, vertex, normalized = new THREE.Vector3(),
        normalizedAxis = 0;
    
      for (var c = 0; c < colors.length - 1; c++) {
    
        var colorDiff = colors[c + 1].stop - colors[c].stop;
    
        for (var i = 0; i < geometry.faces.length; i++) {
          face = geometry.faces[i];
          for (var v = 0; v < 3; v++) {
            vertex = geometry.vertices[face[vertexIndices[v]]];
            normalizedAxis = normalized.subVectors(vertex, bbox.min).divide(size)[axis];
            if (reverse) {
              normalizedAxis = 1 - normalizedAxis;
            }
            if (normalizedAxis >= colors[c].stop && normalizedAxis <= colors[c + 1].stop) {
              var localNormalizedAxis = (normalizedAxis - colors[c].stop) / colorDiff;
              face.vertexColors[v] = colors[c].color.clone().lerp(colors[c + 1].color, localNormalizedAxis);
            }
          }
        }
      }
    }
    
    var mat = new THREE.MeshBasicMaterial({
      vertexColors: THREE.VertexColors,
      wireframe: true
    });
    var obj = new THREE.Mesh(geom, mat);
    scene.add(obj);
    
    
    
    render();
    
    function resize(renderer) {
      const canvas = renderer.domElement;
      const width = canvas.clientWidth;
      const height = canvas.clientHeight;
      const needResize = canvas.width !== width || canvas.height !== height;
      if (needResize) {
        renderer.setSize(width, height, false);
      }
      return needResize;
    }
    
    function render() {
      if (resize(renderer)) {
        camera.aspect = canvas.clientWidth / canvas.clientHeight;
        camera.updateProjectionMatrix();
      }
      renderer.render(scene, camera);
      obj.rotation.y += .01;
      requestAnimationFrame(render);
    }
    HTML
    身体{
    身高:100%;
    保证金:0;
    溢出:隐藏;
    }
    
    画布{
    宽度:100%;
    身高:100%;
    显示;
    街区;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.min.js"></script>

    实际上,使用哪种方法取决于您:材质球、顶点颜色、纹理等。

        2
  •  3
  •   M -    6 年前

    如果希望渐变为静态,可以使用 .map 财产。或者你可以把它分配给 .emissiveMap 属性,如果您希望它在不需要灯光的情况下“发光”。

    但是,如果你想改变你的渐变,并且总是在Z轴上淡入淡出,即使在旋转模型或相机之后,你也必须编写一个自定义的材质球,这需要你学习一些教程。你可以看看 this example 有关如何在three.js中实现自定义着色器的信息,请访问 https://thebookofshaders.com/ 了解如何编写简单的渐变明暗器。

        3
  •  3
  •   pailhead    6 年前

    如果要保留 MeshPhongMaterial 你可以试着延长材料。

    这是一个比较宽泛的主题,有几种方法,您可以深入了解它。 here .

    Phong材质明暗器中有一行类似于

    vec4 diffuseColor = vec4( diffuse, opacity );
    

    因此,在学习了《着色者之书》或其他一些教程之后,您将了解到可以通过使用标准化因子(介于0,1之间的数字)混合两种颜色。

    这意味着你可以把这行改成这样

    vec4 diffuseColor = vec4( mix(diffuse, myColor, vec3(myFactor)), opacity);
    

    可以这样扩展该明暗器

    const myFactor = { value: 0 }
    const myColor = {value: new THREE.Color}
    
    
    myMaterial.onBeforeCompile = shader=>{
      shader.uniforms.myFactor = myFactor
      shader.uniforms.myColor = myColor
      shader.fragmentShader = `
      uniform vec3 myColor;
      uniform float myFactor;
      ${shader.fragmentShader.replace(
        vec4 diffuseColor = vec4( diffuse, opacity );
        vec4 diffuseColor = vec4( mix(diffuse, myColor, vec3(myFactor)), opacity);
      )}
    `
    

    当你改变的时候 myFactor.value 对象的颜色应该从 myMaterial.color myColor.value .

    现在要使它变成一个渐变,你需要替换 myFactor 充满活力。我喜欢囚犯解决方案使用紫外线。它完全是用JavaScript完成的,并且非常简单地连接到这个着色器中。其他方法可能需要更多的着色工作。

    vec4 diffuseColor = vec4( mix(diffuse, myColor, vec3(vUv.y)), opacity);
    

    现在你可能遇到的问题-如果你打电话 new PhongMaterial({color}) 也就是说,如果没有任何纹理提供给它,着色器将编译 vUv . 有许多条件会导致它编译并对您有用,但我不确定它们是否会破坏其他东西:

    #if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )
    

    所以,添加一些类似

    myMaterial.defines = {USE_MAP:''} 
    

    可能会 真空紫外 可用于材质球的变量。这样你就可以得到所有的灯光的声音材料影响的材料,你只需改变基础颜色。