three材质shader分析

three材质shader分析

五月 22, 2022

three材质shader分析

参考资料

  1. https://github.com/mrdoob/three.js
  2. https://juejin.cn/post/6964935853671972878
  3. https://www.cnblogs.com/zzatp/p/9274074.html
  4. https://juejin.cn/post/6977667236576591908

three材质的shader

three的shader位于如下的目录中,简单来说/ShaderLib文件夹为每个材质对应的shader/ShaderChunk内则为各个材质所需要的shader中所需函数、变量与常量。ShaderChunk.js则引用前面两个所有的文件。UniformsLib.jsshader所需要的Uniform变量,UniformsUtils.js则为Uniform工具函数。ShaderLib.js将各个材质所需的shaderUniforms组装后返回的map对象

1
2
3
4
5
6
7
8
9
10
11
12
src/renderers
├── shaders
│ ├── ShaderChunk # 函数、变量与常量
│ │ ├── alphamap_fragment.glsl.js
│ │ ├── ...
│ ├── ShaderChunk.js # shader中所需函数、变量与常量
│ ├── ShaderLib # 材质shader
│ │ ├── background.glsl.js
│ │ ├── ...
│ ├── ShaderLib.js # 包含各个材质所需的shader与uniforms的map对象
│ ├── UniformsLib.js # Uniform变量
│ └── UniformsUtils.js # Uniforms所需工具类

基础网格材质(MeshBasicMaterial)

基础网格材质作为基础材质,包含了网格基础以及颜色和贴图相关shader处理代码,该种材质不受光照的影响,所以不包含光照相关的shader

顶点shader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// vertex shader
// 公共模块、常量
#include <common>
// 处理uv所需要定义
#include <uv_pars_vertex>
#include <uv2_pars_vertex>
// 环境贴图定义
#include <envmap_pars_vertex>
// 颜色定义
#include <color_pars_vertex>
// 包含雾化效果定义
#include <fog_pars_vertex>
// 变形动画定义
#include <morphtarget_pars_vertex>
// 蒙皮动画定义
#include <skinning_pars_vertex>
// 深度处理定义
#include <logdepthbuf_pars_vertex>
// 裁剪平面定义
#include <clipping_planes_pars_vertex>

void main() {

// uv 数据处理
#include <uv_vertex>
#include <uv2_vertex>
// 颜色 数据处理
#include <color_vertex>
// 变形动画法线处理
#include <morphcolor_vertex>

// 如果使用 环境贴图 或者 使用蒙皮
#if defined ( USE_ENVMAP ) || defined ( USE_SKINNING )

#include <beginnormal_vertex>
#include <morphnormal_vertex>
#include <skinbase_vertex>
#include <skinnormal_vertex>
#include <defaultnormal_vertex>

#endif

// 开始顶点位置处理
#include <begin_vertex>
// 变形动画位置处理
#include <morphtarget_vertex>
// 蒙皮顶点处理
#include <skinning_vertex>
// 投影顶点运算
#include <project_vertex>
// logDepth深度运算
#include <logdepthbuf_vertex>
// 裁剪平面运算
#include <clipping_planes_vertex>

// 世界坐标运算
#include <worldpos_vertex>
// 阴影所需要的一些运算
#include <envmap_vertex>
// 雾化所需要的运算
#include <fog_vertex>

}

片段shader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// fragment
// 漫反射颜色
uniform vec3 diffuse;
// 透明度
uniform float opacity;

// 摄像机空间的法线
#ifndef FLAT_SHADED
varying vec3 vNormal;
#endif

// 着色器公共模块
#include <common>
// 抖动处理定义
#include <dithering_pars_fragment>
// 颜色定义
#include <color_pars_fragment>
// uv相关定义
#include <uv_pars_fragment>
#include <uv2_pars_fragment>
// map贴图定义
#include <map_pars_fragment>
// alpha融合贴图定义(灰度纹理,表面不透明度)
#include <alphamap_pars_fragment>
// alpha测试定义
#include <alphatest_pars_fragment>
// aomap贴图定义(环境遮挡贴图)uv2
#include <aomap_pars_fragment>
// lighmap贴图定义(光照贴图)uv2
#include <lightmap_pars_fragment>
// envmap环境贴图定义
#include <envmap_common_pars_fragment>
#include <envmap_pars_fragment>
// cubemap光滑反射贴图定义
#include <cube_uv_reflection_fragment>
// 雾化定义
#include <fog_pars_fragment>
// 镜面反射贴图
#include <specularmap_pars_fragment>
// 对数深度缓冲区定义
#include <logdepthbuf_pars_fragment>
// 裁剪平面定义
#include <clipping_planes_pars_fragment>

void main() {

// 裁剪平面裁剪
#include <clipping_planes_fragment>

// 合成rgba四通道漫反射颜色
vec4 diffuseColor = vec4( diffuse, opacity );

// logdepth运算
#include <logdepthbuf_fragment>
// map通道颜色采样
#include <map_fragment>
// color参与计算
#include <color_fragment>
// alphamap通道颜色采样
#include <alphamap_fragment>
// alpha测试
#include <alphatest_fragment>
// 镜面通道颜色采样
#include <specularmap_fragment>

ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );

// 光照贴图处理
// accumulation (baked indirect lighting only)
#ifdef USE_LIGHTMAP
vec4 lightMapTexel = texture2D( lightMap, vUv2 );
reflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity * RECIPROCAL_PI;
#else
reflectedLight.indirectDiffuse += vec3( 1.0 );
#endif

// modulation 调制
// 根据AO贴图调整反射光照强度
#include <aomap_fragment>

reflectedLight.indirectDiffuse *= diffuseColor.rgb;

vec3 outgoingLight = reflectedLight.indirectDiffuse;

#include <envmap_fragment>

// 输出光照颜色
#include <output_fragment>
// tonemap曝光
#include <tonemapping_fragment>
// 颜色编码
#include <encodings_fragment>
// 雾化颜色运算
#include <fog_fragment>
// 颜色预乘alpha
#include <premultiplied_alpha_fragment>
// 颜色随机抖动
#include <dithering_fragment>

}

Lambert网格材质(MeshLambertMaterial)

一种非光泽表面的材质,没有镜面高光。
该材质使用基于非物理的Lambertian模型来计算反射率。 这可以很好地模拟一些表面(例如未经处理的木材或石材),但不能模拟具有镜面高光的光泽表面(例如涂漆木材)。
使用Gouraud着色模型计算着色。这将计算每个顶点的着色 (即在vertex shader中)并在多边形的面上插入结果。

像较与基础网络材质,Lambert材质多了对光照与阴影的处理,其中的color属性对应在光照下所计算的反射颜色,emissive对应材质本身的颜色

在顶点sahder中lambert对光照和阴影分别做了处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// vertex
#include <lights_lambert_vertex>

vec3 diffuse = vec3( 1.0 );

GeometricContext geometry;
geometry.position = mvPosition.xyz;
geometry.normal = normalize( transformedNormal );
geometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( -mvPosition.xyz );

GeometricContext backGeometry;
backGeometry.position = geometry.position;
backGeometry.normal = -geometry.normal;
backGeometry.viewDir = geometry.viewDir;

vLightFront = vec3( 0.0 );
vIndirectFront = vec3( 0.0 );
#ifdef DOUBLE_SIDED
vLightBack = vec3( 0.0 );
vIndirectBack = vec3( 0.0 );
#endif

IncidentLight directLight;
float dotNL;
vec3 directLightColor_Diffuse;

vIndirectFront += getAmbientLightIrradiance( ambientLightColor );

vIndirectFront += getLightProbeIrradiance( lightProbe, geometry.normal );

// 对应材质中的 side属性
// 判断两面都渲染的情况
#ifdef DOUBLE_SIDED

vIndirectBack += getAmbientLightIrradiance( ambientLightColor );

vIndirectBack += getLightProbeIrradiance( lightProbe, backGeometry.normal );

#endif

// 处理点光源
#if NUM_POINT_LIGHTS > 0

#pragma unroll_loop_start
for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {

getPointLightInfo( pointLights[ i ], geometry, directLight );

dotNL = dot( geometry.normal, directLight.direction );
directLightColor_Diffuse = directLight.color;

vLightFront += saturate( dotNL ) * directLightColor_Diffuse;

#ifdef DOUBLE_SIDED

vLightBack += saturate( - dotNL ) * directLightColor_Diffuse;

#endif

}
#pragma unroll_loop_end

#endif

// 处理聚光灯(SpotLight)
#if NUM_SPOT_LIGHTS > 0

#pragma unroll_loop_start
for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {

getSpotLightInfo( spotLights[ i ], geometry, directLight );

dotNL = dot( geometry.normal, directLight.direction );
directLightColor_Diffuse = directLight.color;

vLightFront += saturate( dotNL ) * directLightColor_Diffuse;

#ifdef DOUBLE_SIDED

vLightBack += saturate( - dotNL ) * directLightColor_Diffuse;

#endif
}
#pragma unroll_loop_end

#endif

#if NUM_DIR_LIGHTS > 0

#pragma unroll_loop_start
for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {

getDirectionalLightInfo( directionalLights[ i ], geometry, directLight );

dotNL = dot( geometry.normal, directLight.direction );
directLightColor_Diffuse = directLight.color;

vLightFront += saturate( dotNL ) * directLightColor_Diffuse;

#ifdef DOUBLE_SIDED

vLightBack += saturate( - dotNL ) * directLightColor_Diffuse;

#endif

}
#pragma unroll_loop_end

#endif

#if NUM_HEMI_LIGHTS > 0

#pragma unroll_loop_start
for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {

vIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal );

#ifdef DOUBLE_SIDED

vIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry.normal );

#endif

}

#pragma unroll_loop_end

#endif
#include <shadowmap_vertex>

1
2
3
4
5

uniform vec3 emissive;

ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
vec3 totalEmissiveRadiance = emissive;

Phong网格材质(MeshPhongMaterial)

该材质使用非物理的Blinn-Phong模型来计算反射率。 与MeshLambertMaterial中使用的Lambertian模型不同,该材质可以模拟具有镜面高光的光泽表面(例如涂漆木材)。

使用Phong着色模型计算着色时,会计算每个像素的阴影(在fragment shader, AKA pixel shader中),与MeshLambertMaterial使用的Gouraud模型相比,该模型的结果更准确,但代价是牺牲一些性能。 MeshStandardMaterial和MeshPhysicalMaterial也使用这个着色模型。

像较与Lambert网格材质,Phong网格材质多了shininess属性,用于计算材质的镜面反射的反射强度,specular属性则为镜面反射所计算的颜色

标准网格材质(MeshStandardMaterial)

MeshPhongMaterial 有一个参数用来设置 shininess(反射强度) 属性。MeshStandardMaterial 有2个参数用来分别设置 roughness(粗糙度) 和 metalness(金属性) 属性。

一种基于物理的标准材质,使用Metallic-Roughness工作流程。

基于物理的渲染(PBR)最近已成为许多3D应用程序的标准,例如Unity, Unreal和 3D Studio Max。

这种方法与旧方法的不同之处在于,不使用近似值来表示光与表面的相互作用,而是使用物理上正确的模型。 我们的想法是,不是在特定照明下调整材质以使其看起来很好,而是可以创建一种材质,能够“正确”地应对所有光照场景。

在实践中,该材质提供了比MeshLambertMaterial 或MeshPhongMaterial 更精确和逼真的结果,代价是计算成本更高。

计算着色的方式与MeshPhongMaterial相同,都使用Phong着色模型, 这会计算每个像素的阴影(即在fragment shader, AKA pixel shader中), 与MeshLambertMaterial使用的Gouraud模型相比,该模型的结果更准确,但代价是牺牲一些性能。

物理网格材质(MeshPhysicalMaterial)

MeshStandardMaterial的扩展,提供了更高级的基于物理的渲染属性:

Clearcoat: 有些类似于车漆,碳纤,被水打湿的表面的材质需要在面上再增加一个透明的,具有一定反光特性的面。而且这个面说不定有一定的起伏与粗糙度。Clearcoat可以在不需要重新创建一个透明的面的情况下做到类似的效果。
基于物理的透明度:.opacity属性有一些限制:在透明度比较高的时候,反射也随之减少。使用基于物理的透光性.transmission属性可以让一些很薄的透明表面,例如玻璃,变得更真实一些。
高级光线反射: 为非金属材质提供了更多更灵活的光线反射。