第十七节:高级材质 - ShaderMaterial揭秘
第十七节:高级材质 - ShaderMaterial揭秘
用GLSL编写自定义着色器实现高级视觉效果
1. 核心概念解析
1.1 ShaderMaterial vs 标准材质
特性 | MeshStandardMaterial | ShaderMaterial |
---|---|---|
灵活性 | 低(预设参数) | 高(完全自定义) |
性能 | 优化好 | 依赖着色器复杂度 |
学习曲线 | 简单 | 陡峭(需GLSL知识) |
适用场景 | 常规渲染 | 特效/自定义光照/后处理 |
可控性 | 有限 | 完全控制渲染管线 |
1.2 GLSL着色器工作流程
2. GLSL语法精要
2.1 数据类型与精度
// 精度限定符(必需)
precision highp float; // 高精度
precision mediump float; // 中精度
precision lowp float; // 低精度// 基本数据类型
float value = 1.0; // 浮点数
int count = 5; // 整数
bool isActive = true; // 布尔值// 向量与矩阵
vec2 uv = vec2(1.0, 0.5); // 二维向量
vec3 position; // 三维向量
vec4 color = vec4(1.0); // 四维向量(RGBA)
mat4 matrix; // 4x4矩阵
2.2 变量传递机制
// Uniforms(CPU→GPU传递,所有顶点/片元相同)
uniform float uTime; // 时间
uniform vec3 uColor; // 颜色
uniform sampler2D uTexture; // 纹理// Attributes(每个顶点独有)
attribute vec3 position; // 顶点位置
attribute vec2 uv; // UV坐标// Varyings(顶点着色器→片元着色器)
varying vec2 vUv; // 传递UV坐标
varying vec3 vPosition; // 传递位置
3. 创建自定义着色器
3.1 基础ShaderMaterial创建
// 创建着色器材质
const material = new THREE.ShaderMaterial({// Uniforms定义uniforms: {uTime: { value: 0 },uColor: { value: new THREE.Color(0xff0000) },uTexture: { value: new THREE.TextureLoader().load('/textures/noise.jpg') }},// 顶点着色器vertexShader: `uniform float uTime;varying vec2 vUv;varying vec3 vPosition;void main() {vUv = uv;vPosition = position;// 简单的波浪效果float wave = sin(position.x * 3.0 + uTime) * 0.1;vec3 newPosition = position + normal * wave;gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);}`,// 片元着色器fragmentShader: `uniform vec3 uColor;uniform sampler2D uTexture;varying vec2 vUv;varying vec3 vPosition;void main() {// 纹理采样vec4 texColor = texture2D(uTexture, vUv);// 颜色混合vec3 finalColor = uColor * texColor.rgb;// 基于位置的颜色渐变float gradient = vPosition.y * 0.5 + 0.5;finalColor *= gradient;gl_FragColor = vec4(finalColor, 1.0);}`,// 材质配置side: THREE.DoubleSide,wireframe: false
});
3.2 动态Uniform更新
// 在渲染循环中更新uniform
function animate() {requestAnimationFrame(animate);// 更新时间uniformmaterial.uniforms.uTime.value = performance.now() * 0.001;renderer.render(scene, camera);
}
animate();
4. 实战案例:动态波浪水面
4.1 完整代码实现
// 顶点着色器代码
const vertexShader = `uniform float uTime;uniform float uWaveHeight;uniform float uWaveFrequency;varying vec2 vUv;varying float vElevation;void main() {vUv = uv;// 计算波浪位移float elevation = sin(position.x * uWaveFrequency + uTime) *cos(position.z * uWaveFrequency + uTime) *uWaveHeight;vElevation = elevation;vec3 newPosition = position + normal * elevation;gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);}
`;// 片元着色器代码
const fragmentShader = `uniform vec3 uWaterColor;uniform vec3 uFoamColor;uniform sampler2D uNormalMap;varying vec2 vUv;varying float vElevation;void main() {// 法线贴图vec3 normalMap = texture2D(uNormalMap, vUv + uTime * 0.1).rgb;// 基于高度的颜色混合vec3 baseColor = mix(uWaterColor, uFoamColor, vElevation * 2.0);// 添加光照效果(简单版)vec3 lightDir = normalize(vec3(0.5, 1.0, 0.5));float diffuse = max(dot(normalMap, lightDir), 0.2);vec3 finalColor = baseColor * diffuse;gl_FragColor = vec4(finalColor, 0.9);}
`;// 创建水面材质
const waterMaterial = new THREE.ShaderMaterial({uniforms: {uTime: { value: 0 },uWaveHeight: { value: 0.2 },uWaveFrequency: { value: 2.0 },uWaterColor: { value: new THREE.Color(0x1e90ff) },uFoamColor: { value: new THREE.Color(0xffffff) },uNormalMap: { value: new THREE.TextureLoader().load('/textures/water-normals.jpg') }},vertexShader,fragmentShader,transparent: true,side: THREE.DoubleSide
});// 创建水面几何体
const waterGeometry = new THREE.PlaneGeometry(20, 20, 128, 128);
const waterMesh = new THREE.Mesh(waterGeometry, waterMaterial);
waterMesh.rotation.x = -Math.PI / 2;
scene.add(waterMesh);
4.2 交互控制界面
// 使用dat.GUI添加控制
import GUI from 'lil-gui';const gui = new GUI();
const waveFolder = gui.addFolder('波浪参数');waveFolder.add(waterMaterial.uniforms.uWaveHeight, 'value', 0, 1).name('波浪高度');
waveFolder.add(waterMaterial.uniforms.uWaveFrequency, 'value', 0, 5).name('波浪频率');
waveFolder.addColor({ color: 0x1e90ff }, 'color').onChange((value) => {waterMaterial.uniforms.uWaterColor.value.set(value);
}).name('水体颜色');waveFolder.open();
5. 重点核心与注意事项
5.1 性能优化要点
优化策略 | 实现方法 | 效果 |
---|---|---|
精度选择 | 根据需求使用lowp/mediump | 性能提升20-30% |
纹理优化 | 使用压缩纹理格式(KTX2) | 内存占用降低70% |
循环优化 | 避免循环中的复杂计算 | 帧率稳定 |
条件语句 | 尽量在顶点着色器中使用 | 避免GPU分支惩罚 |
5.2 常见问题与解决方案
问题1:着色器编译错误
- 现象:屏幕黑屏或控制台报错
- 排查步骤:
- 检查GLSL语法错误(缺少分号等)
- 确认uniform变量名称一致
- 验证精度限定符已声明
问题2:性能突然下降
- 原因:片元着色器过于复杂
- 解决方案:
// 优化前(每像素多次采样) for(int i = 0; i < 10; i++) {color += texture2D(texture, uv + offset * i); }// 优化后(预计算或减少采样) color = texture2D(texture, uv) * 0.5 +texture2D(texture, uv + offset) * 0.3 +texture2D(texture, uv - offset) * 0.2;
问题3:移动端兼容性问题
- 限制:最多16个纹理单元
- 应对策略:
- 合并纹理(纹理图集)
- 减少同时使用的纹理数量
- 使用
WEBGL_lose_context
检测资源释放
5.3 调试技巧
// 1. 颜色调试法
gl_FragColor = vec4(vUv, 0.0, 1.0); // 可视化UV坐标
gl_FragColor = vec4(vec3(vElevation), 1.0); // 可视化高度// 2. 边界检测
if(vUv.x > 0.99 || vUv.y > 0.99) {gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // 红色边界
}// 3. 数值可视化
float value = someComplexCalculation();
gl_FragColor = vec4(value, value, value, 1.0);
6. 完整案例:全息投影效果
// 全息效果着色器材质
const hologramMaterial = new THREE.ShaderMaterial({uniforms: {uTime: { value: 0 },uScanSpeed: { value: 2.0 },uScanColor: { value: new THREE.Color(0x00ffff) },uBaseColor: { value: new THREE.Color(0x0088ff) }},vertexShader: `varying vec2 vUv;varying vec3 vPosition;void main() {vUv = uv;vPosition = position;gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);}`,fragmentShader: `uniform float uTime;uniform float uScanSpeed;uniform vec3 uScanColor;uniform vec3 uBaseColor;varying vec2 vUv;varying vec3 vPosition;void main() {// 扫描线效果float scanLine = sin(vPosition.y * 20.0 - uTime * uScanSpeed) * 0.5 + 0.5;scanLine = pow(scanLine, 3.0); // 增强对比度// 边缘光效果float edgeGlow = 1.0 - smoothstep(0.0, 0.1, abs(vUv.x - 0.5) * 2.0);edgeGlow *= 1.0 - smoothstep(0.0, 0.1, abs(vUv.y - 0.5) * 2.0);// 颜色合成vec3 color = mix(uBaseColor, uScanColor, scanLine);color += edgeGlow * uScanColor * 0.5;// 透明度渐变float alpha = 0.6 + scanLine * 0.4;gl_FragColor = vec4(color, alpha);}`,transparent: true,side: THREE.DoubleSide,blending: THREE.AdditiveBlending
});// 应用到的模型
const model = new THREE.Mesh(new THREE.TorusGeometry(2, 0.5, 16, 100),hologramMaterial
);
scene.add(model);
下一节预告:骨骼动画 - 角色动画控制
第十八节:GLTF动画剪辑与状态混合技术
核心内容:
-
骨骼系统原理
- 骨骼树结构与权重分配
- 矩阵变换层级传递
-
动画控制四部曲:
-
高级技术:
- 动画混合(行走→奔跑平滑过渡)
- 动画遮罩(上半身攻击+下半身行走)
- 根运动处理(角色位移匹配动画)
-
性能优化:
- 动画压缩技术
- GPU骨骼动画加速
重点掌握:实现可交互的3D角色控制系统,为游戏开发奠定基础。