本文档涵盖Three.js中着色器编程的基础概念和实现方法,基于实际代码示例进行讲解。
最终效果如图:
1. 着色器基础概念
着色器(Shader)是运行在GPU上的小程序,用于计算3D场景中每个像素的颜色。在Three.js中,有两种主要的着色器:
- 顶点着色器(Vertex Shader):处理每个顶点的位置变换
- 片元着色器(Fragment Shader):确定每个像素的最终颜色
1.1 着色器导入和初始化
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import gsap from "gsap";
import * as dat from "dat.gui";
// 顶点着色器
import basicVertexShader from "../shader/raw/vertex.glsl";
// 片元着色器
import basicFragmentShader from "../shader/raw/fragment.glsl";
2. 着色器材质创建
2.1 RawShaderMaterial vs ShaderMaterial
RawShaderMaterial直接使用GLSL代码,不会自动添加默认的uniforms和attributes:
// 创建原始着色器材质
const rawShaderMaterial = new THREE.RawShaderMaterial({
vertexShader: basicVertexShader,
fragmentShader: basicFragmentShader,
side: THREE.DoubleSide,
uniforms: {
uTime: {
value: 0,
},
uTexture: {
value: texture,
},
},
});
2.2 基础着色器材质
// 创建着色器材质
const shaderMaterial = new THREE.ShaderMaterial({
vertexShader: `
void main(){
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ) ;
}
`,
fragmentShader: `
void main(){
gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
}
`,
});
3. 顶点着色器详解
顶点着色器负责处理3D空间中的顶点位置,以下是一个包含动画效果的顶点着色器:
precision lowp float;
attribute vec3 position;
attribute vec2 uv;
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
// 获取时间
uniform float uTime;
varying vec2 vUv;
varying float vElevation;
void main(){
vUv = uv;
vec4 modelPosition = modelMatrix * vec4( position, 1.0 );
// 添加基于时间的波浪动画
modelPosition.z = sin((modelPosition.x+uTime) * 10.0)*0.05 ;
modelPosition.z += sin((modelPosition.y+uTime) * 10.0)*0.05 ;
vElevation = modelPosition.z;
gl_Position = projectionMatrix * viewMatrix * modelPosition ;
}
4. 片元着色器详解
片元着色器负责确定每个像素的颜色,以下是一个处理纹理和高度的片元着色器:
precision lowp float;
varying vec2 vUv;
varying float vElevation;
uniform sampler2D uTexture;
void main(){
// 根据UV,取出对应的颜色
float height = vElevation + 0.05 * 20.0;
vec4 textureColor = texture2D(uTexture,vUv);
textureColor.rgb*=height;
gl_FragColor = textureColor;
}
5. Uniforms统一变量
Uniforms是在JavaScript代码和着色器之间传递数据的变量:
const rawShaderMaterial = new THREE.RawShaderMaterial({
vertexShader: basicVertexShader,
fragmentShader: basicFragmentShader,
side: THREE.DoubleSide,
uniforms: {
uTime: {
value: 0, // 时间变量,用于动画
},
uTexture: {
value: texture, // 纹理变量
},
},
});
在动画循环中更新uniform值:
const clock = new THREE.Clock();
function animate(t) {
const elapsedTime = clock.getElapsedTime();
// 更新着色器中的时间uniform
rawShaderMaterial.uniforms.uTime.value = elapsedTime;
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
6. 几何体与着色器结合
使用平面几何体展示着色器效果:
// 创建平面
const floor = new THREE.Mesh(
new THREE.PlaneBufferGeometry(1, 1, 64, 64), // 细分更多,波浪效果更明显
rawShaderMaterial
);
scene.add(floor);
7. 基础着色器示例
创建一个简单的黄色平面着色器:
// 创建基础着色器材质
const shaderMaterial = new THREE.ShaderMaterial({
vertexShader: `
void main(){
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ) ;
}
`,
fragmentShader: `
void main(){
gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0); // 黄色
}
`,
});
8. 着色器开发最佳实践
-
精度声明:在片元着色器中声明精度
precision lowp float; // 低精度 precision mediump float; // 中等精度 precision highp float; // 高精度 -
变量类型:
attribute:每个顶点独有的数据(如位置、UV坐标)uniform:所有顶点共享的数据(如时间、纹理)varying:在顶点着色器和片元着色器之间传递的数据
-
性能优化:避免在着色器中使用复杂运算,尽可能在CPU端预计算
-
调试技巧:通过将中间计算结果输出到颜色来调试着色器
总结
本章介绍了Three.js中着色器编程的基础知识,包括:
- 着色器的基本概念和类型
- 如何创建和使用着色器材质
- 顶点着色器和片元着色器的编写
- 如何通过uniforms在JavaScript和着色器间传递数据
- 基础的着色器动画实现
通过掌握这些基础知识,可以进一步探索更复杂的着色器效果。