Three.js 着色器构成剖析 & MeshStandardMaterial 结构分析

550 阅读2分钟

​        哈哈!今天要分享的是开发中碰到的难题,大场景中的模型需要通过Instance方式进行渲染优化,主要是减少drawcall次数和顶点数量。模型材质使用的是MeshStandardMaterial,这种材质是一种基于物理的标准材质(PBR),然而Three.js并没有将Instance封装到着色器中,需要我们在代码里重写THREE.ShaderLib.physical并将Instance的模型矩阵,逆转置矩阵等信息传入着色器实现需求,然后我就开始了我的MeshStandardMaterial  shader探索之旅 。

       首先我们剖析一下Three.js的着色器部分代码的构成,我们先来看一下Three.js的着色器源码构成(src目录下)。

                          

        那我们可以看见Three.js将代码分为两个部分ShaderLib和ShaderChunk两部分,看了ShaderLib中的任意一组顶点和片元着色器可以知道,ShaderLib通过组合SharderChunk来构建顶点着色器和片元着色器,下面简略分析下meshphysical_vert.glsl和meshphysical_frag.glsl两个文件,下面是基于Three.js r102版本分析。

export default /* glsl */`
#define PHYSICAL                 //定义物理材质的宏
varying vec3 vViewPosition;      //相机空间顶点位置
#ifndef FLAT_SHADED              //flat着色
	varying vec3 vNormal;
	#ifdef USE_TANGENT
		varying vec3 vTangent;
		varying vec3 vBitangent;
	#endif
#endif
#include <common>               //着色器公用常量和函数(例如常量π,函数pow2)
#include <uv_pars_vertex>       //UV顶点处理的变量声明(处理漫反射纹理、凹凸纹理、法线纹理等)
#include <uv2_pars_vertex>      //UV2顶点处理的变量声明(AO贴图、光照贴图处理)
#include <displacementmap_pars_vertex> //位移贴图处理的变量声明
#include <color_pars_vertex>        //颜色处理的变量声明(自定义颜色)
#include <fog_pars_vertex>          //雾化处理的变量声明
#include <morphtarget_pars_vertex>  //变形动画的变量声明
#include <skinning_pars_vertex>     //蒙皮动画的变量声明 
#include <shadowmap_pars_vertex>    //阴影纹理生成的变量声明 
#include <logdepthbuf_pars_vertex>  //深度纹理的变量声明 
#include <clipping_planes_pars_vertex> //剪裁平面处理的声明
void main() {                    //主函数 
	#include <uv_vertex>         //uv(漫反射纹理、凹凸纹理、法线纹理等)处理
	#include <uv2_vertex>        //uv2(AO纹理、光照贴图)
	#include <color_vertex>      //顶点颜色处理(所有顶点漫反射颜色都会乘上这个自定义颜色)
	#include <beginnormal_vertex>//开始法线处理
	#include <morphnormal_vertex>//变形动画法线处理
	#include <skinbase_vertex>   //骨骼蒙皮基本运算
	#include <skinnormal_vertex> //骨骼蒙皮法线计算 
	#include <defaultnormal_vertex>//默认的法线处理
#ifndef FLAT_SHADED // Normal computed with derivatives when FLAT_SHADED
	vNormal = normalize( transformedNormal );
	#ifdef USE_TANGENT
		vTangent = normalize( transformedTangent );
		vBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );
	#endif
#endif
	#include <begin_vertex>       //开始顶点位置处理
	#include <morphtarget_vertex> //变形动画位置处理 
	#include <skinning_vertex>    //蒙皮顶点处理
	#include <displacementmap_vertex>//位移贴图顶点处理
	#include <project_vertex>     //投影处理
	#include <logdepthbuf_vertex> //对数深度处理
	#include <clipping_planes_vertex> //剪裁平面顶点处理
	vViewPosition = - mvPosition.xyz;
	#include <worldpos_vertex>    //世界坐标系顶点计算  
	#include <shadowmap_vertex>   //阴影贴图计算 
	#include <fog_vertex>         //雾化计算
}
`;

         好哈,以上就是meshphysical_vert的简略分析,那meshphysical_frag其实也就进行光照逐片元处理和接受顶点着色器传过来的参数进行处理,就不做分析了,感兴趣可以自己翻翻源代码看看,挺有意思的。后面有时间会对每个顶点和片元着色器的ShaderChunk进行分析,哈哈,今天就到这里了哈。

        最后呢,发下广告啦啦啦!这里有WebGL、Vulkan、OpenGL,也有Three.js、Unity、UE4,还有前端框架Vue等!当然也有图形图像处理大佬哈!我们在这里期待你的加入!   

                              ​