Three.js onBeforeCompile 对材质的修改

842 阅读2分钟

标准材质

image.png

  • 可以看到图中的 蓝色框中 水壶 使用的是标准的 材质 进行渲染的效果

  • 中间的 水壶 底部有火焰叠加起来的 红色的燃烧效果 (也就是本文的重点讲解内容)对标准的材质进行shader的改进( onBeforeCompile 函数的使用)

// 创建不锈钢材质
const material = new THREE.MeshStandardMaterial({
    color: 0xaaaaaa, // 灰色基调
    metalness: 0.8, // 高金属性
    roughness: 0.2, // 低粗糙度
    envMapIntensity: 1 // 环境贴图强度
});

onBeforeCompile

  • onBeforeCompile 函数存在 material 中 每一个材质都有属于自己的 onBeforeCompile 函数 , 该函数 会传入 一个 shader 对象,以比喻成 material
  • shader 包含 uniforms 参数信息 以及 fragmentShader || vertexShader 我们 可以同对其内容的修改 来达到我们 想要的 效果
  • 修改 uniforms fragmentShader vertexShader 都是写在 一个 onBeforeCompile 中

image.png

image.png

image.png

uniforms

  • 传递 自定义的 参数
material.onBeforeCompile = (shader) => {
    // 自定义的 uColor 颜色值
    shader.uniforms.uColor = {
       value: new THREE.Color("#492781"),
    };
    sader.uniforms.combustionNumber = {
        value:13.0
    }
}

vertexShader

  • 传递 自定义的 参数
material.onBeforeCompile = (shader) => {
    // 替换 vertexShader 中的获取自定义参数的值
    shader.vertexShader = shader.vertexShader.replace(
        "#include <common>",
        `
            #include <common>
            uniform vec3 uColor;
            varying vec3 vPosition;
        `
    );
    // 设置 vPosition 传递给 片元着色器的 值 vPosition;
    shader.vertexShader = shader.vertexShader.replace(
        "#include <begin_vertex>",
        `
            #include <begin_vertex>
            vPosition = position;
        `
    );
}

fragmentShader

material.onBeforeCompile = (shader) => {
    // 获取传递给 fragmentShader 和自定义参数的值
    shader.fragmentShader = shader.fragmentShader.replace(
        "#include <common>",
        `
            #include <common>
            uniform vec3 uColor;
            uniform float combustionNumber;
            varying vec3 vPosition;
        `
    );
    // 在最终 渲染片元着色器 后加上自己的 shader
    shader.fragmentShader = shader.fragmentShader.replace(
        "#include <dithering_fragment>",
        `
            #include <dithering_fragment>
            if(vPosition.y<combustionNumber){
                gl_FragColor = mix(
                    gl_FragColor,vec4(1.0,0.0,0.0,1.0),
                    smoothstep( combustionNumber , combustionNumber - 20.0 ,vPosition.y)
                );
          }
        `
    );
    
}

注 (这是我在开发中遇到的问题)

  • 如果使用 以下方式设置的着色器 效果会有变化 可能会导致达不到我们理性的效果
shader.fragmentShader = shader.fragmentShader.replace(
        "#include <dithering_fragment>",
        `
            #include <dithering_fragment>
            gl_FragColor = mix(vec4(1.0,0.0,0.0,1.0),gl_FragColor,vPosition.y/4.0);
        `
    );

image.png

  • 所以为了解决这个问题 采用了 if 判断的模式 去 阻止 vPosition.y 的颜色变化 达到最终的理想效果
shader.fragmentShader = shader.fragmentShader.replace(
        "#include <dithering_fragment>",
        `
            #include <dithering_fragment>
            if(vPosition.y<combustionNumber){
                gl_FragColor = mix(
                    gl_FragColor,vec4(1.0,0.0,0.0,1.0),
                    smoothstep( combustionNumber , combustionNumber - 20.0 ,vPosition.y)
                );
          }
        `
    );

shader 处理后的标准材质效果

image.png image.png

整合的代码

material.onBeforeCompile = (shader) => {
    // 自定义的 uColor 颜色值
    shader.uniforms.uColor = {
       value: new THREE.Color("#492781"),
    };
    sader.uniforms.combustionNumber = {
        value:13.0
    }
    // 替换 vertexShader 中的获取自定义参数的值
    shader.vertexShader = shader.vertexShader.replace(
        "#include <common>",
        `
            #include <common>
            uniform vec3 uColor;
            varying vec3 vPosition;
        `
    );
    // 设置 vPosition 传递给 片元着色器的 值 vPosition;
    shader.vertexShader = shader.vertexShader.replace(
        "#include <begin_vertex>",
        `
            #include <begin_vertex>
            vPosition = position;
        `
    );
    // 获取传递给 fragmentShader 和自定义参数的值
    shader.fragmentShader = shader.fragmentShader.replace(
        "#include <common>",
        `
            #include <common>
            uniform vec3 uColor;
            uniform float combustionNumber;
            varying vec3 vPosition;
        `
    );
    // 在最终 渲染片元着色器 后加上自己的 shader
    shader.fragmentShader = shader.fragmentShader.replace(
        "#include <dithering_fragment>",
        `
            #include <dithering_fragment>
            if(vPosition.y<combustionNumber){
                gl_FragColor = mix(
                    gl_FragColor,vec4(1.0,0.0,0.0,1.0),
                    smoothstep( combustionNumber , combustionNumber - 20.0 ,vPosition.y)
                );
          }
        `
    );
}