【问题标题】:Applying color gradient to material by extending three.js Material class with onBeforeCompile通过使用 onBeforeCompile 扩展 three.js Material 类将颜色渐变应用于材质
【发布时间】:2021-02-10 01:39:53
【问题描述】:

我的 three.js 场景中有一个用 THREE.OBJLoader 加载的 .obj 文件。

我想在此对象上应用 z 轴上的线性颜色渐变,同时保留 MeshStandardMaterial 着色器。下面是应用于对象的 2 色线性渐变的示例。

我需要这样做,同时保留 MeshStandardMaterial 类附带的着色器。我认为要做到这一点,我需要用一些代码注入 MeshStandardMaterial 的 vertexShader 和 materialShaders。

这在question 的答案中部分实现。这些答案创建了一个没有我需要的 MeshStandardMaterial 属性的新着色器材质。

我尝试使用onBeforeCompile,但我不知道在哪里注入上面提到的question 答案中概述的代码。我对着色器只有基本的了解。

我的对象的材质由 THREE.MeshStandardMaterial 类定义。我设置了类的以下属性:金属度、粗糙度、透明和不透明度。我使用纹理设置地图和 envmap 属性。

【问题讨论】:

    标签: javascript three.js geometry vertex-shader


    【解决方案1】:

    MeshStandardMaterial 着色器编译成数百行代码,因此很难准确指出在何处更改颜色,同时保持环境反射、光照、金属度、粗糙度和所有这些行为。你可能想多了。如果您只需要MeshStandardMaterial 上的渐变,为什么不使用渐变创建纹理,然后将其分配给Material.map 属性?如果你加一个白色的AmbientLight,你应该可以毫无问题地看到渐变。

    或者,如果您希望渐变不受灯光影响,您可以将纹理分配给材质的 .emissive 属性。

    最后,如果你想避免纹理,你可以为每个顶点指定颜色,然后将Material.vertexColors 设置为true。

    【讨论】:

    • 感谢您的回答。我没有考虑使用画布,因为我已经有一个 map 属性并且不知道如何在语法上叠加 2 个映射,但是现在尝试一下画布纹理,我知道它是如何非常有用的走那条路。
    【解决方案2】:

    很难意识到着色器块中的内容是什么,但并非不可能。

    body{
      overflow: hidden;
      margin: 0;
    }
    <script type="module">
    import * as THREE from "https://cdn.jsdelivr.net/npm/three@0.121.1/build/three.module.js";
    import {OrbitControls} from "https://cdn.jsdelivr.net/npm/three@0.121.1/examples/jsm/controls/OrbitControls.js";
    
    let scene = new THREE.Scene();
    let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
    camera.position.set(0, 10, 20);
    let renderer = new THREE.WebGLRenderer();
    renderer.setSize(innerWidth, innerHeight);
    document.body.appendChild(renderer.domElement);
    
    let controls = new OrbitControls(camera, renderer.domElement);
    
    let light = new THREE.DirectionalLight(0xffffff, 1);
    light.position.setScalar(10);
    scene.add(light);
    scene.add(new THREE.AmbientLight(0xffffff, 1));
    
    let g = new THREE.TorusKnotBufferGeometry(5, 1, 128, 16);
    g.rotateX(-Math.PI * 0.5);
    g.computeBoundingBox();
    
    let uniforms = {
        bbMin: {value: g.boundingBox.min},
      bbMax: {value: g.boundingBox.max},
      color1: {value: new THREE.Color(0xff0000)},
      color2: {value: new THREE.Color(0xffff00)}
    }
    console.log(g);
    let m = new THREE.MeshStandardMaterial({
        roughness: 0.25,
      metalness: 0.75,
      map: new THREE.TextureLoader().load("https://threejs.org/examples/textures/floors/FloorsCheckerboard_S_Diffuse.jpg", tex => {
        tex.wrapS = THREE.RepeatWrapping;
        tex.wrapT = THREE.RepeatWrapping;
        tex.repeat.set( 16, 1 );
      }),
      onBeforeCompile: shader => {
        shader.uniforms.bbMin = uniforms.bbMin;
        shader.uniforms.bbMax = uniforms.bbMax;
        shader.uniforms.color1 = uniforms.color1;
        shader.uniforms.color2 = uniforms.color2;
        shader.vertexShader = `
            varying vec3 vPos;
          ${shader.vertexShader}
        `.replace(
        `#include <begin_vertex>`,
        `#include <begin_vertex>
        vPos = transformed;
        `
        );
        shader.fragmentShader = `
            uniform vec3 bbMin;
          uniform vec3 bbMax;
          uniform vec3 color1;
          uniform vec3 color2;
          varying vec3 vPos;
          ${shader.fragmentShader}
        `.replace(
            `vec4 diffuseColor = vec4( diffuse, opacity );`,
          `
          float f = clamp((vPos.z - bbMin.z) / (bbMax.z - bbMin.z), 0., 1.);
          vec3 col = mix(color1, color2, f);
          vec4 diffuseColor = vec4( col, opacity );`
        );
        console.log(shader.vertexShader);
        console.log(shader.fragmentShader);
      }
    });
    
    let o = new THREE.Mesh(g, m);
    scene.add(o);
    
    
    renderer.setAnimationLoop(()=>{
        renderer.render(scene, camera);
    });
    </script>

    【讨论】:

    • 感谢您的回答。你真的是一个有着色器的向导。请问我需要通过哪些资源来了解着色器才能完成您在此答案中所做的事情?
    • 一个后续问题,是否可以编辑此代码,使渐变应用于 emissiveMap 而不是 map 属性?
    • @LankyPanky 如果你想要那样,为什么需要 MeshStandardMaterial?因为您想将该渐变应用为自发光颜色。有MeshBasicMaterial就够了。
    • 我不知道这两者之间的区别,除了一个基于 PBR 并且存在计算权衡。我担心我可能没有完美地提出我的问题。为了提供更多背景信息,我创建了这个问题,我希望你能回答:stackoverflow.com/questions/64615943/…
    猜你喜欢
    • 2015-04-17
    • 2021-05-16
    • 1970-01-01
    • 2018-12-01
    • 1970-01-01
    • 2021-05-29
    • 2019-05-30
    • 2019-02-14
    • 2017-11-03
    相关资源
    最近更新 更多