从零开始学习three.js(18):一文详解three.js中的着色器Shader
在WebGL和Three.js的3D图形渲染中,着色器(Shader) 是实现复杂视觉效果的核心工具。通过编写自定义的着色器代码,开发者可以直接操作GPU,实现从基础颜色渲染到动态光照、粒子效果等高级图形技术。本文将深入解析Three.js中着色器的核心概念、实现原理及实战应用,并结合代码示例帮助读者全面掌握这一关键技术。
核心特点:
一、着色器基础与分类
1.1 顶点着色器(Vertex Shader)
顶点着色器负责处理几何体的每个顶点,主要功能包括:
- 顶点位置变换:将模型空间坐标转换为屏幕空间坐标(通过
gl_Position
输出) - 顶点属性计算:如法线变换、纹理坐标传递等
void main() {gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
说明:projectionMatrix
(投影矩阵)、modelViewMatrix
(模型视图矩阵)由Three.js自动注入,position
是顶点的原始坐标。
1.2 片元着色器(Fragment Shader)
片元着色器决定每个像素的最终颜色,通过 gl_FragColor
输出:
void main() {gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // 红色
}
特性:颜色支持动态计算,如基于光照模型、纹理采样等。
1.3 着色器材质类型
Three.js提供两种自定义着色器材质:
-
ShaderMaterial
:简化版材质,内置常用变量(如modelViewMatrix
、projectionMatrix
),适合快速开发。 -
RawShaderMaterial
:需手动声明所有变量(如attribute
、uniform
),灵活性更高,适合深度优化。
示例代码:创建基础着色器材质
const material = new THREE.ShaderMaterial({ vertexShader: vertexShaderCode, fragmentShader: fragmentShaderCode, uniforms: { time: { value: 0 }, color: { value: new THREE.Color(0xff0000) } }
});
二、着色器变量类型
2.1 Uniforms
- 全局常量:所有顶点/片元共享同一值(如时间、光源位置)
- 传递方式:通过
ShaderMaterial
的uniforms
属性设置
const material = new THREE.ShaderMaterial({uniforms: {uTime: { value: 0 },uTexture: { value: new THREE.TextureLoader().load("texture.png") }}
});
在着色器中声明:uniform float uTime;
2.2 Attributes
- 顶点属性:每个顶点独有的数据(如位置、颜色、法线)
- 典型应用:动态顶点动画(如波浪效果)
geometry.setAttribute('displacement', new THREE.BufferAttribute(displacementArray, 1));
在顶点着色器中访问:attribute float displacement;
2.3 Varyings
- 插值变量:从顶点着色器向片元着色器传递数据(如UV坐标、颜色插值)
// 顶点着色器
varying vec2 vUv;
void main() {vUv = uv;// ...
}// 片元着色器
varying vec2 vUv;
void main() {vec4 color = texture2D(uTexture, vUv);
}
注意:变量名需在两者中保持一致。
三、矩阵变换与坐标系统
3.1 核心矩阵解析
- 模型矩阵(modelMatrix) :几何体的旋转、平移、缩放变换
- 视图矩阵(viewMatrix) :相机的观察变换(等同于相机世界矩阵的逆矩阵)
- 投影矩阵(projectionMatrix) :3D到2D的投影(如透视投影)
3.2 矩阵乘法顺序
顶点变换遵循 “右乘”顺序,确保坐标正确转换:
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
原理:从模型空间→世界空间→相机空间→裁剪空间逐步变换。
四、实战案例:动态波纹效果
4.1 着色器代码实现
顶点着色器(传递UV坐标):
varying vec2 vUv;
void main() {vUv = uv;gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
片元着色器(动态波纹计算):
uniform float uTime;
varying vec2 vUv;void main() {float ripple = sin(length(vUv - 0.5) * 10.0 - uTime * 2.0);float opacity = smoothstep(0.0, 0.2, abs(ripple));gl_FragColor = vec4(0.2, 0.5, 1.0, opacity);
}
4.2 JavaScript端配置
const material = new THREE.ShaderMaterial({uniforms: {uTime: { value: 0 }},vertexShader: vertexShaderCode,fragmentShader: fragmentShaderCode,transparent: true
});// 动画循环中更新Uniform
function animate() {material.uniforms.uTime.value += 0.01;requestAnimationFrame(animate);
}
效果:实现以中心向外扩散的蓝色波纹。
五、高级技巧与优化
5.1 性能优化策略
- 减少分支语句:GPU不擅长动态分支,可用
mix()
或step()
替代if-else
- 向量化运算:优先使用
vec3
/vec4
代替多个float
计算 - 纹理压缩:使用Mipmap和纹理图集减少采样开销
5.2 常见问题排查
- 变量未声明:检查Three.js版本是否支持特定语法(如
#version 300 es
) - 精度问题:在移动端明确指定精度(
precision mediump float;
) - 矩阵顺序错误:确保模型→视图→投影的乘法顺序正确
六、扩展应用:后期处理通道
通过 EffectComposer
和 ShaderPass
实现屏幕后处理:
import { EffectComposer, ShaderPass } from 'three/examples/jsm/postprocessing';const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));const customPass = new ShaderPass(customShaderMaterial);
composer.addPass(customPass);
典型效果:模糊、Bloom光效、颜色校正等。
结语
Three.js着色器为开发者打开了高性能图形编程的大门。通过深入理解GLSL语法、矩阵变换和变量传递机制,可以创造出从基础颜色变化到复杂物理模拟的全方位视觉效果。建议通过Shadertoy平台进行实时演练,结合Three.js文档探索更多可能性。