盒子
盒子

Three.js扩展材质

#科学地扩展 Three.js 原生材质
不知道怎么用中文描述这个话题,如果是英文的话,大概是”How to customize three.js’s native materials.”。

###Why
现在 PBR 的工作流非常流行,对实时渲染质量要求比较高的一般用一套 StandardMaterial 就可以走天下了。但随着开发深入,对材质难免有修改的地方。我们项目改的地方比较多,为了调试方便,直接把原来的 shader copy 了一遍再尽情魔改。整体仍然是遵循 PBR 流程,贴图 uniforms 也只是在原来的基础上加了一些。
如果把这套新的 shader 直接塞进 ShaderMaterial 中,work 是肯定没问题,但是传变量就会从 native 材质的material.map变成material.uniforms.map.value这样非常不痛快的形式。native 材质之所以可以直接用 map,是因为 WebGLRenderer 有帮它做操作。具体可以查看我在 Three.js 的 forum 上问的这个问题里 Mugen87 的回答:
https://discourse.threejs.org/t/where-do-materials-properties-come/10197

###How
核心即 material 的onBeforeCompile方法,这个函数让你可以在材质 shader 编译前对其暴改。
Three.js 的 shader 只有三个属性,uniformsvertexShaderfragmentShader
vertexShaderfragmentShader都是字符串,可以用任意字符串操作尽情修改。一般如果只需要改一两行的话用 replace 或者前后加些语句修改即可,比如 EliasHastle 提供的清晰易懂的 fiddle 示例:https://jsfiddle.net/EliasHasle/btz6jq83/32/
因为我们是整套 copy 下来改,所以直接给这俩字符串重新赋值了。自定义的 shader 可以写在其它文件中导出常量。
uniforms是对象形式,如果要添加的uniforms不多,也是照 fiddle 示例添加即可。如果是在自定义 shader 里写好了整块的uniforms想要直接赋值,要注意引用问题,多个材质实例引用同一个uniforms对象就糟糕了,UniformsUtils提供clone方法,我是直接在继承的自定义材质类里定义了。
由于场景整体的一些渲染操作比如OIT后处理等可能也需要改动材质,所以有需要的话最好把onBeforeCompile写成单独的 public 函数 bind 上去,方便其它操作给onBeforecompile传函数时可以在函数体内调用原修改函数。

刚开始写博客,超级流水账,赋段代码总结:

1
2
3
4
5
6
7
8
9
10
11
12
class myStandardMaterial extends THREE.MeshStandardMaterial {
uniforms = {...};
myOnBeforeCompile = (shader: THREE.Shader) => {
shader.uniforms = this.uniforms;
shader.vertexShader = myShader.vertexShader;
shader.fragmentShader = myShader.fragmentShader;
};
constructor(){
super();
this.onBeforeCompile = this.myOnBeforeCompile.bind(this);
}
}
支持一下
扫一扫,支持ToniFF
  • 微信扫一扫
  • 支付宝扫一扫