type
Post
status
Published
date
Mar 12, 2026
slug
summary
提高渲染真实性
着色器
tags
Three.js
category
技术学习笔记
icon
password
提高渲染真实性
色调映射Tone Mapping
色调映射,直接作用于
渲染器,意在将低画质的贴图效果模拟成高画质贴图效果,官方文档的描述如下:

通常来说,如果我们使用HDR材质的环境贴图,会导致加载慢等问题,但是使用
LDR(Low Dynamic Range) 的贴图(比如使用 jpg 格式柱状全景图),又会觉得其效果不够好,所以 threejs 贴心的准备了这一选项默认情况下,
webGL 渲染器的色调映射模式为 NoToneMapping ,通过 gui 来控制每一种 toneMapping 的值方便我们测试其效果:
在这里插入图片描述
经过测试大致可以得到以下结论:
- THREE.NoToneMapping
描述:禁用色调映射。
特点:直接输出场景的颜色值,不进行任何调整或压缩。
应用场景:适用于不需要HDR效果的情况。
- THREE.LinearToneMapping
描述:线性色调映射。
特点:简单地将颜色值从线性空间转换到sRGB空间。
应用场景:基础的色彩管理,适合于不需要复杂HDR处理的场合。
- THREE.ReinhardToneMapping
描述:基于Reinhard的色调映射算法。
特点:能够有效地处理非常亮的区域,同时保持暗部细节。
应用场景:广泛应用于游戏和CGI中,因为它能够很好地平衡亮度和对比度。
- THREE.CineonToneMapping
描述:模仿电影胶片的效果。
特点:提供了更柔和的过渡效果,适合追求电影感的画面。
应用场景:电影制作和高端视觉特效项目。
- THREE.ACESFilmicToneMapping
描述:基于Academy Color Encoding System (ACES) 的色调映射。
特点:模拟了现代数字电影摄影机的响应曲线,提供了非常自然的视觉效果。
应用场景:专业级视频制作和要求极高的图形应用。
- THREE.AgXToneMapping
描述:一种模拟胶片感光特性的算法。
特点:提供了类似于传统黑白胶片的效果。
应用场景:艺术创作和特定风格化的项目。
THREE.NeutralToneMapping
描述:中性色调映射。
特点:尽量保持颜色的真实性,减少过度处理带来的失真。
应用场景:需要真实色彩再现的应用。
- THREE.CustomToneMapping
描述:自定义色调映射。
特点:允许开发者实现自己的色调映射逻辑。
应用场景:当上述预设不能满足特定需求时,可以使用此选项来实现个性化的色调处理。
此外,我们还可以使用
toneMappingExposure 来控制色调的曝光程度,画面的体现上来看就是对于亮度的调节而已抗锯齿(Anti-Aliasing)
首先要明白为什么会有锯齿
,计算机的渲染是按像素进行的,像素的最小单位就是1x1的矩形,假如要渲染一个非矩形的形状,计算机就可能会因为计算有误导致某个像素没有出来,进而形成阶梯状的边缘,比如:


在这里插入图片描述
所以,为了提高画质,避免这些边缘的阶梯状效果,就需要抗锯齿技术来对画面进行优化
SSAA
超采样抗锯齿,全名为
Super-Sampling-Anti-Aliasing ,它的原理简单粗暴,就是将原本要渲染的分辨率放大一倍,渲染后再进行缩放;比如要将场景渲染到1920 * 1080的画面上,开启抗锯齿后就是先将场景渲染到3840 * 2160的画面上后再缩放至1920 * 1080,这样子的抗锯齿效果显著,但是性能开销显然就非常大了2X SSAA,就代表渲染到2倍于原始分辨率的场景,每一个像素相当于放大了 2 x 2 = 4倍,渲染的像素总数就是原来的4倍
4X SSAA,则代表每个像素变为原来的 4 * 4 = 16倍,渲染的像素总数就是原来的16倍
优点:
- 提供高质量的抗锯齿效果。
- 能够消除几乎所有的锯齿现象。
缺点:
- 计算成本非常高,渲染时间显著增加。
- 不适用于性能要求较高的场景。
MSAA
Multi-Sample Anti-Aliasing ,多重采样抗锯齿,原理是在每个像素周围进行多次采样来减少锯齿现象,实现上相对简单,但是因为只在像素中心附近进行采样,所以无法完全消除所有锯齿
在这里插入图片描述
引用自https://zhuanlan.zhihu.com/p/133511752
如图,红框框起的绿色像素就是MSAA会额外处理的像素部分

优点:
- 相对简单且易于实现。
- 对于线性和边缘锯齿效果较好。
缺点:
- 对于点状和纹理锯齿效果较差。
- 只能在像素中心附近进行采样,无法完全消除所有锯齿。
FXAA
自适应抗锯齿
Fast Approximate Anti-Aliasing ,一种后处理技术,原理是通过检测边缘并对其进行模糊处理来减少锯齿,它不需要额外的采样,而是在最终图像上进行处理。优点:
- 实现简单且计算成本低。
- 对于大多数情况下的抗锯齿效果较好。
缺点:
- 效果不如 MSAA 和 SSAA 高质量。
- 可能会出现轻微的模糊效果。
阴影投射
添加阴影也是让渲染更真实的一种方案,但是之前说的环境贴图是无法投射阴影的,要实现阴影需要额外的外部光源,下面尝试给这个场景添加阴影:

首先,要通过给渲染器设置
shadowMap.enabled 来开启场景中的阴影效果:的性价比较高,设置阴影时可以优先考虑

然后,需要对场景中能投射阴影的物体和接收阴影的物体进行设置,对于当前场景而言,接收和投射阴影的物体都是导入的模型,模型经过
threejs 的 loader 解析后会转成 threejs 中内置的 Object3D L类,所以可以通过 traverse 快速遍历整个模型的场景,给场景里的所有组成模型的物体设置阴影:接着,往场景中添加一个平行光源,并为其添加辅助器
helper 与 gui 控制面板,方便我们调整光的投射位置、强度等属性现在的效果:

现在场景中添加了两个
helper , DirectionalLightHelper 用于标识平行光的位置(目前它被模型挡住了), THREE.CameraHelper 用于标识阴影投射的方向现在移动灯光的位置,就可以看到灯光的照射方向以及阴影的投射方向了:

在这里插入图片描述
默认情况下
是从上往下照射的,如果要修改光照的方向,按照官方文档中的说法,就得这么操作:

下面是修改了灯光投射的方向后的结果

这里只是演示,进行下一步之前请先把修改灯光位置的代码删掉
阴影的投射也要考虑性能,通常其出发点有两个,一个是修改阴影投射摄影机的远距,使其恰好涵盖物体本身即可;一个是修改投射出的阴影质量

在这里插入图片描述
在
512 * 512 的分辨率下,阴影边缘会比较模糊,把解析度改为 4096 ,效果会好很多:
在这里插入图片描述
最后,根据环境贴图的样子,我们需要把光调整到一个正确的位置,毕竟总不能出现“太阳在左上角,光从左下角打过来”的情况吧
比如在当前场景中,光是从天上打下来的,大概设置光的位置为

在这里插入图片描述
当然,过高的解析度也意味着更大的性能开销,在不同的场景下需要有不同的取舍(通常低解析度的阴影也够用了,因为用户通常不会刻意去观察这些细节)
伪影
Shadow Acne ,伪影是一种在计算机图形学中常见的渲染问题,特别是在使用阴影贴图(shadowmapping)技术时。原因:光源和物体表面之间的采样不精确导致。
导入项目中的另一个汉堡包模型,同时调整模型的缩放比例使其符合画面缩放程度:
乍一看似乎没什么问题:

但是放大看,会发现其表面出现了一些不对劲的噪点和波纹

当把场景亮度调低后,这些问题就更明显了:

大致的原理就是模型本身既能接收阴影,又能投射阴影,所以它的表面也会有覆盖上自己产生的阴影,因此影响了其表面的形状,产生波纹和噪点,别忘了GPU是按照像素去渲染的:

要解决这个问题,
提供了一个最简单的办法,设置
的阴影贴图偏差属性

现在再看,波纹效果明显减少了很多,但多少还存在一些

并且还导致了一个新的问题,芝士的阴影也没了:

这是因为我们给阴影贴图移动了错误的方向,应该把
设置为
,设置成
,芝士的阴影就回来了

就接下来继续解决剩下的可见的“波纹”,除了
bias 外,还有另一个 normalBias 属性可以配合一起调整, normalBias 参数用于定义查询阴影贴图的位置沿物体法线方向的偏移量。增加这个值可以减少 shadow acne ,尤其是在光线以较小角度照射到几何体上的大场景中使用
gui 控制 directionalLight 的 normalBias 属性,直到找到一个合适的值即可:设置到0.007就差不多了:

另外可以看到模型的边缘有锯齿,这就要通过之前所说的抗锯齿来解决了,
threejs 里直接通过给 renderer 开启抗锯齿即可:抗锯齿ON:

抗锯齿OFF:

纹理
一个获取纹理的网址:
https://polyhaven.com ,不过需要注册 patreon 和付费才能下载,但是纹理的质量确实高,而且格式选择上足够丰富视觉疲劳问题
长时间专注于渲染一个场景过久必然会出现视觉疲劳的问题,也许会影响你对于空间中物体的位置、颜色渲染的判断,此时最好是切换场景,去看一下看其他的有色物体来调整自己的视觉,之后再回来调整可能会发现自己原本调色不合理的地方
着色器
在
Web3D 的范畴内来说,着色器是一种程序,由 WebGL 运行,需要使用 GLSL 语言去编写;在
threeJs 中,我们编写好着色器程序之后, threeJs 将其传给 WebGL 解析, WebGL 将他解析成二进制代码给 GPU 进行计算,最后GPU就可以将图像等渲染出来所以,着色器是用来”绘制“(实际上是记录,然后交给GPU去绘制)几何体顶点的位置和每个像素的颜色
在
ThreeJs 着色器的代码中,我们可以向其发送如以下信息:- 顶点位置
- 几何体的变换信息(旋转、缩放等)
- 摄像机的信息
- 颜色、纹理、灯光、雾等
在
WebGL 的范畴内,主要使用的是顶点着色器( Vertex Shaders )和片段着色器( Fragment Shaders )顶点着色器
通过
GLSL 语言,我们可以用顶点位置( position )、几何体的变换( geometry transformation )、摄像机等数据创建出 顶点着色器程序 ,最终GPU运行这个顶点着色器,将这些东西渲染出来:
在这里插入图片描述
在
threejs 里, Data 阶段就是我们通过创建 THREE.Geometry 获取到的几何体的坐标、uv等数据,他们会以 attribute 、 uniforms 的形式传到 顶点着色器 中这个意思是在GLSL语言中,把几何体的坐标等属性用attribute、uniforms等关键字来表示和获取,下文的glsl语法讲解会说到
总结来说就是:顶点着色器用于处理3D模型中的每个“顶点”,可以改变顶点的位置、颜色、纹理坐标等属性。
将顶点着色器交给GPU运行后,GPU就知道了当前的几何体有哪些顶点、线、面是在画布上”可见的“,然后就可以进行下一个阶段——使用
片段着色器 上色了片段着色器
Fragment Shaders ,也称为像素着色器,负责计算屏幕上每个像素的颜色值,针对的是一块区域内的上色(毕竟画布是一个一个小方块组成的,最小片段应该是 1px * 1px )顶点着色器的
无法传给片段着色器,只能传
,但是可以通过
关键字将attribute数据传给片段着色器,如图:

片段着色器通过
uniforms 、 varying 获取到顶点相关的数据后,将他们进行混合(或者不混合,混合是为了实现渐变等效果);然后我们在片段着色器中声明片段的颜色,此时我们就得到了一个可用的片段着色器了将片段着色器、顶点着色器交给
WebGL 编译成二进制代码传给GPU,我们的几何体就被渲染出来了vite-plugin-glsl
https://www.npmjs.com/package/vite-plugin-glslnpm install glslify webpack-glsl-loader --save-dev编写着色器代码
搭建基本场景
首先搭建一个
demo 环境,在页面中画出一个平面几何体
在这里插入图片描述
替换材质
threejs 里有两个着色器材质: ShaderMaterial 和 RawShaderMaterial ,两者的区别仅在于 ShaderMaterial 会自动地将一些内置的 attributes 和 uniforms 添加到着色器程序顶部将代码里的材质替换为
RawShaderMaterial ,然后从头编写着色器代码:至此,页面上就渲染出了一个红色的平面几何体:

在这里插入图片描述
配置开发环境
在
material 里写模板字符串对于我们开发效率和质量都有比较大的影响,不利于我们的开发,所以我们需要类似JS、TS的代码颜色支持VSCode就有这样的插件,名为

着色器的代码是用
glsl 语言编写的,现在分别为顶点着色器和片段着色器创建两个单独的文件,并把着色器代码丢到里面,可以看到语法就有正常的高亮了:
在这里插入图片描述
这里着色器的文件后缀可以是
也可以是
或
等,如下表:

主要是
glsl lint 无法对 .glsl 后缀的文件生效,所以文件命名建议以 .vs 和 .fs 命名除了高亮外,我们还要安装代码格式化和代码检查的插件,来确保代码准确性,所以安装插件
,与

然后来这里:
,下载
程序:

找个位置解压后,打开
设置,搜索
将
定位到bin目录下的
程序即可:

重启
vscode ,以后我们就有了代码自动格式化( glsl-canvas )、代码语法检查( glsl-lint )以及着色器效果预览( glsl-canvas )了最后,语法提示的配置,去这个地址:
,把第一篇
代码全部复制下来:

打开

搜索
glsl.json ,然后代码复制进去,重启 vscode 即可glsl文件引入
按照目前的配置,直接引入是不行的,需要对应的文件解析器来解析对应的文件
在
vite 环境中,需要使用 vite-plugin-glsl ,先安装该插件: npm install vite-plugin-glsl ,然后编辑 vite.config.js :然后我们就可以引入
shader 文件了:如果是
webpack 环境,需要使用 raw-loader 将着色器代码文件当作 string 引入使用,比如在 umi 项目中要这样配置:正常的
webpack 环境则直接配置即可:glsl语言
GLSL:
OpenGL Shading Language ,这是一种类型语言,每个变量、表达式或值都有一个明确的数据类型,并且这些类型在编译或运行时会被严格检查的语言,主要有以下特点:- 无法打印,因为代码是由GPU去运行的;比如对于顶点着色器的代码而言,代码定义了每个顶点的位置计算方法,一个几何体有N个顶点需要渲染,那么GPU就会对应运行N次代码来计算出每个顶点的位置
- 每一行结尾必须加分号
int和float有严格的区分:如int a = 1; float b = 0.002,不能混合运算
- 不能缺少
main函数实现,主入口必定是main函数
- 运算的顺序是从右往左进行的(下文会有案例演示)
- 这个语言没有所谓的官方文档,但有一些民间总结而来的指引网站:
https://thebookofshaders.com/https://shaderific.com/glsl/common_functions.html
- 另外一个好用的学习openGL的英文网站:
https://learnopengl.com/
float类型
即浮点数,和C语言中的
float 类似,可以进行数学运算,但必须加小数点,不能和整形数进行运算int类型
即整形数据,和C语言中的
int 类似,声明时必须为整数, int 和 float 不能混用,如:vec2
二维向量,由
x,y 组成,可以类比为threejs里的 Vector2 ,一些要点如下:vec3
三维向量,在x、y的基础上多一个z,类似
threejs 的 Vector3 ,与 vec2 的操作类似:除了可以使用x、y、z,还可以使用r、g、b获取值,这个对于创建一个
rgb 的颜色比较方便另外,可以快捷通过
vec2 创建一个 vec3vec4
可以理解为四维向量,多出来的你那个记为
w ,也可以通过 a 访问:所以
vec4 通常会用来声明 rgba 颜色(带alpha通道即”透明度”的颜色)
与 vec3 、 vec2 类似, vec4 也可以进行类似的一些运算,这里就不做赘述此外,向量存在一种运算成为
swizzle 操作,它是一种对向量的分量进行操作的运算,支持混合分量与重复分量:function
即函数,类似于c语言里的函数声明:
当然,也可以进行函数传参等操作:
同时,其内置了很多数学函数如
pow 、 max 、 min 等,均与 Math.pow 、 Math.max 的作用一致,这里不做赘述从GLSL代码视角理解顶点着色器
uniform是glsl语言中的关键字,用于定义一个外部传入的不可变的变量,类似于const声明
mat4代表一个4x4的矩阵类型
projectionMatrix代表”投影矩阵“,用于将场景从相机视图转换到标准化设备坐标系中
viewMatrix代表”视图矩阵“,用于将场景从像世界坐标系转换到相机坐标系
modelMatrix代表”模型矩阵“,用于将物体从局部坐标系转换到世界坐标系
attribute也是glsl里的关键字,用于定义一个”属性“,每个顶点调用时position的值会自动设置为顶点的位置(以顶点的局部坐标系为准)
gl_Position是glsl语言里的一个特殊内置变量,用于存储顶点着色器计算后的顶点位置,这个位置会在光栅化阶段(被片段着色器)生成片段,并最终绘制
注意,projectionMatrix、viewMatrix、modelMatrix是threeJs内部声明的变量,而不是glsl本身定义的变量,在其他3D引擎中名称不一定是这些(比如在Unity引擎中,投影矩阵命名为UNITY_MATRIX_P)
所以整个顶点着色器的代码执行流程如下:
首先我们在
threejs 中创建 Geometry 、 ShaderMaterial ,使用这两个要素生成 Mesh 。渲染开始前,
投影矩阵 、 视图矩阵 、 模型矩阵 的值已经由 threejs 内部计算完成并以 uniform mat4 xxxMatrix 变量的形式注入完成矩阵的计算是由threejs通过我们设置的旋转、缩放、位移计算得来的,至于计算公式就请自行查看源码了,这个并不重要
然后渲染开始,
webGL 根据几何体的顶点数量,告诉GPU顶点着色器代码需要执行多少次, position 的值等于 threejs 注入的每个几何体的顶点坐标注意,在
glsl 中,计算是从右往左进行的,也就是说,计算最后的顶点位置时需要:- 首先将顶点的三维坐标转为四维向量,其中第四个分量为1,代表这是一个位置向量
- 进行模型矩阵变换,计算
modelMatrix * vec4,将局部坐标系的顶点坐标转换到世界坐标系的顶点坐标。
- 进行视图矩阵变换,计算
viewMatrix * vec4(上一步计算得来),将世界坐标系的顶点坐标转换为相机坐标系的顶点坐标
- 进行投影矩阵变换,计算
projectionMatrix * vec4(上一步计算得来),将相机坐标系的顶点坐标转换到标准化设备坐标系的顶点坐标
所以实际上代码可以按照执行顺序改写成:
然后
gl_Position 就会被传到片段着色器中进行下一步的上色计算从GLSL代码视角理解片段着色器
片段着色器相对比较简单,
gl_FragColor 和 gl_Position 一样,是内置的变量,设置的是每个片段(像素)的颜色, vec4(1.,0,0,1.) 就代表纯红色,不透明precision 用于声明浮点数的精度,不同的硬件(显卡)对浮点数的处理能力不同,支持的设置有三种:precision lowp float
- 范围:[-2, 2] 精度:至少10位有效数字 通常用于颜色和纹理坐标,适用于大多数移动设备。
precision mediump float
- 范围:[-2^14, 2^14] 精度:至少16位有效数字 适用于大多数颜色和纹理坐标计算,以及一些简单的数学运算。
precision highp float
- 范围:[-2^62, 2^62] 精度:至少23位有效数字 适用于复杂的数学运算和需要高精度的场景,但可能会降低性能。
lowp 通常用于移动端设备,可以有效地优化移动端的运行性能
mediump 则适用于绝大多数桌面和高端移动设备
highp 则适用于需要复杂的数学运算和高精度的场景,比如物理模拟、高级光照计算的场景对于大多数WebGL应用,
mediump 是一个不错的选择,因为它在性能和精度之间取得了良好的平衡,优化性能时才考虑使用 lowp在GLSL里操纵几何体的变换
几何体的位置、形状等可以通过顶点着色器来修改,比如之前在顶点着色器里这样子分开每一步来写了:
移动几何体
通过”模型矩阵”计算出来的
modelPosition 是顶点在世界坐标系中的坐标,即此时可以通过修改 modelPosition 的值来移动物体,比如:就可以把物体往脸上移动0.5个距离:

要记得:每个顶点都会执行一次着色器代码的计算
创建波浪效果
波浪效果用三角函数可以很轻易地实现,比如对每个顶点的x坐标取正弦值赋给z坐标:(需要增大振幅效果才明显)
就可以得到:

但是目前因为振幅太大导致点过于靠近,可以通过在外层缩小整体的值来让画面变缓:

在这里插入图片描述
在glsl里,.0可以缩写为.
随机数控制
曾经我们有个操作是自己创建一个
BufferGeometry 并且自定义顶点坐标绘制图形,当时是通过下面的代码实现的:geometry 里的 attributes 属性都会被 threejs 注入到着色器中,并且以同名变量的方式声明,我们可以打印 geometry.atrributes 查看里面的属性:
在这里插入图片描述
看到上面的图,
BufferAttribute 里有 count 属性,代表顶点的个数,即当前平面几何体的顶点个数是 1089 个,这就意味着顶点着色器代码至少会执行 1089 次;在顶点着色器中,就可以通过 attribute 来访问这些同名的变量,比如:position 以 vec3 的形式创建,意味着顶点着色器计算时会自动去 position.array 中3个3个地取值作为顶点的坐标进行计算;对于 uv、normal 也是同理假设我们需要传一个自定义的值,那么就可以这样写:
我们创建了一个长度为顶点个数的数组,数组值用随机数填充,规定
里取值的步长为1,打印
可以看到我们自己创建的变量
:

在顶点着色器中就需要通过
float 进行取值,而不是vec2或vec3,这取决于 attribute 的步长这样会得到下面的结果,有点”恐怖“

别忘了我们可以通过比例来控制振幅:
这样就得到还行的效果:

顶点着色器与片段着色器通信
在
threejs 的两个着色器间进行数据传递有且仅有一种办法,那就是通过 varying 关键字实现比如我们可以把上一步的
random 值传到片段着色器中控制颜色:然后我们就得到了这样的效果:

threejs与顶点着色器通信
在
threejs 中, ShaderMaterial 与 RawShaderMaterial 可以通过 unifroms 属性向顶点着色器传参,比如我们可以通过 threejs 来控制平面的波浪效果振幅:这样我们就可以在
threejs 修改对应的参数来进行调试或构造动画了;另外可以看到 threeJs 中给 uFrequency 设置的值是20,对于着色器而言是整型,但是却可以用 float 接收,因为 threejs 会根据需要进行对应数据的转换除了传单一的值,还可以传
THREE.Vector2 ,比如同时控制 x、y 方向的振幅就可以这样子写:
在这里插入图片描述
threejs与片段着色器通信
uniform 创建的变量是可以同时在片段着色器、顶点着色器中通信的,所以也可以通过 uniforms 变量来控制平面的颜色,比如:这里可以直接传
THREE.Color ,他在着色器中的数据是 vec3 的形式,所以除了用 vec3 直接生成 vec4 ,也可以访问 uColor.r 、 uColor.g 、 uColor.b 来设置颜色,这里就不赘述了着色器加载纹理
比如我们要给平面加上国旗的纹理:

那么同样的,也是通过
uniforms 将纹理传入到着色器中:在片段着色器中要使用
sample2D 这个数据类型来获取 texture ,并使用 texture2D 函数来将纹理转换成对应的 vec4 颜色:sampler2D 是 glsl 内置的数据类型,主要用于表示2D纹理,他会在着色器中访问纹理的数据,如纹理的引用、采样状态等texture2D 是 glsl 内置的函数,用于从2D纹理中采样颜色值,接收一个 sampler2D 和 一个 二维纹理坐标作为参数,最后返回对应坐标的颜色值 vec4这里的
vUV 来自于顶点着色器,交互的关键代码如下:虽然
不能在片段着色器中读取,但是可以通过
将其传到片段着色器里,而
对应的就是几何体的
坐标,它会被
自动当成
注入,所以可以直接通过
来读取

最后,就成功地将颜色换成了国旗纹理图:

在这里插入图片描述
控制动画
在
threeJs 的逐帧渲染中,可以通过内置的 Clock 获取 从第一帧运行开始到当前帧经过了多少秒 ,如:
在这里插入图片描述
所以,如果把这个值传到顶点着色器中,基本的动画就可以实现了;
创建一个
uniforms 变量,在每一帧的渲染函数中更新它:在顶点着色器中用同名变量获取值,控制z方向的形变:
基本的波浪动画就有了:

在这里插入图片描述
总结
- 顶点着色器的作用是将“顶点坐标”从“局部坐标系”转换到“标准化设备坐标系”来让GPU实现渲染
threejs与glsl的数据交互通过uniforms、attribute、varying关键字来完成,uniform可以同时被两个着色器读取,attribute只能在顶点着色器中读取,然后通过varying传入片段着色器
ShaderMaterial和RawShaderMateral的区别在于后者需要你自己手动声明projectionMatrix、modelMatrix等变量,所以使用后者通常可以节省一定的性能开销(因为不用初始化过多的无用变量)

- 不要使用
Date.now()来触发shaders的变化计算,因为它的值对于shader来说太大了
最后附上一个
shaders 实现案例的社区: https://www.shadertoy.com/补充
理解 modelMatrix
modelMatrix 是 threeJs 内置的变换矩阵,它在默认情况下是 单位矩阵 :我们在顶点着色器里,需要先经过模型矩阵变换:
对于任一矩阵变换的计算公式是:
所以,假如
position 的值为 (1, 0, 1) ,那么计算出来的 modelPosition 就仍然是 (1,0,1)当我们在
threejs 里通过 mesh.rotateX() 、 mesh.position.x = 1 、 mesh.scale.y = 2 等操作修改了物体后,在GPU的渲染阶段, threejs 会自动计算出由 缩放矩阵 、 平移矩阵 、 旋转矩阵 相乘得到的 模型矩阵 ,以此来计算出每个顶点实际的位置