Three.js 着色器 #
着色器(Shader)是运行在 GPU 上的小程序,用于控制 3D 物体的渲染方式。通过自定义着色器,可以实现各种独特的视觉效果。
着色器概述 #
text
┌─────────────────────────────────────────────────────────────┐
│ 着色器渲染流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ │
│ │ 顶点数据 │ │
│ │ (Position) │ │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ 顶点着色器 │ │
│ │ Vertex │ │
│ │ Shader │ │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ 光栅化 │ │
│ │ Rasterization│ │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ 片元着色器 │ │
│ │ Fragment │ │
│ │ Shader │ │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ 像素输出 │ │
│ │ (Color) │ │
│ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
GLSL 基础 #
数据类型 #
glsl
float value = 1.0;
int count = 10;
bool visible = true;
vec2 position = vec2(1.0, 2.0);
vec3 color = vec3(1.0, 0.0, 0.0);
vec4 rgba = vec4(1.0, 0.0, 0.0, 1.0);
mat2 m2 = mat2(1.0, 0.0, 0.0, 1.0);
mat3 m3 = mat3(1.0);
mat4 m4 = mat4(1.0);
sampler2D texture;
samplerCube cubeMap;
向量操作 #
glsl
vec3 color = vec3(1.0, 0.5, 0.0);
float r = color.r;
float g = color.g;
float b = color.b;
float x = color.x;
float y = color.y;
float z = color.z;
vec2 rg = color.rg;
vec3 rgb = color.rgb;
color.r = 0.0;
color.gb = vec2(0.5, 0.5);
内置函数 #
glsl
float a = abs(-1.0);
float b = min(1.0, 2.0);
float c = max(1.0, 2.0);
float d = clamp(1.5, 0.0, 1.0);
float e = sin(3.14159);
float f = cos(3.14159);
float g = tan(3.14159);
float h = pow(2.0, 3.0);
float i = sqrt(4.0);
float j = exp(1.0);
float k = log(2.718);
float l = floor(1.5);
float m = ceil(1.5);
float n = fract(1.5);
float o = mod(5.0, 3.0);
float p = mix(0.0, 1.0, 0.5);
vec3 q = mix(vec3(1.0), vec3(0.0), 0.5);
float r = smoothstep(0.0, 1.0, 0.5);
float s = length(vec3(1.0, 1.0, 1.0));
float t = distance(vec3(0.0), vec3(1.0));
float u = dot(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));
vec3 v = cross(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));
vec3 w = normalize(vec3(1.0, 1.0, 1.0));
ShaderMaterial #
基本着色器 #
javascript
const material = new THREE.ShaderMaterial({
vertexShader: `
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`
});
传递属性 #
javascript
const material = new THREE.ShaderMaterial({
uniforms: {
uTime: { value: 0.0 },
uColor: { value: new THREE.Color(0xff0000) },
uTexture: { value: texture }
},
vertexShader: `
uniform float uTime;
varying vec2 vUv;
varying vec3 vPosition;
void main() {
vUv = uv;
vPosition = position;
vec3 pos = position;
pos.y += sin(pos.x * 2.0 + uTime) * 0.1;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
`,
fragmentShader: `
uniform vec3 uColor;
uniform sampler2D uTexture;
varying vec2 vUv;
varying vec3 vPosition;
void main() {
vec4 texColor = texture2D(uTexture, vUv);
gl_FragColor = vec4(uColor, 1.0) * texColor;
}
`
});
更新 Uniforms #
javascript
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const elapsedTime = clock.getElapsedTime();
material.uniforms.uTime.value = elapsedTime;
renderer.render(scene, camera);
}
animate();
内置变量 #
顶点着色器 #
| 变量 | 类型 | 描述 |
|---|---|---|
| position | vec3 | 顶点位置 |
| normal | vec3 | 顶点法线 |
| uv | vec2 | UV 坐标 |
| color | vec3 | 顶点颜色 |
| projectionMatrix | mat4 | 投影矩阵 |
| modelViewMatrix | mat4 | 模型视图矩阵 |
| modelMatrix | mat4 | 模型矩阵 |
| viewMatrix | mat4 | 视图矩阵 |
| normalMatrix | mat3 | 法线矩阵 |
| cameraPosition | vec3 | 相机位置 |
片元着色器 #
| 变量 | 类型 | 描述 |
|---|---|---|
| gl_FragColor | vec4 | 输出颜色 |
| gl_FragCoord | vec4 | 片元坐标 |
| gl_FrontFacing | bool | 是否正面 |
| vUv | vec2 | UV 坐标(需声明) |
| vNormal | vec3 | 法线(需声明) |
| vPosition | vec3 | 位置(需声明) |
常见效果 #
波浪效果 #
javascript
const material = new THREE.ShaderMaterial({
uniforms: {
uTime: { value: 0.0 },
uAmplitude: { value: 0.1 },
uFrequency: { value: 2.0 }
},
vertexShader: `
uniform float uTime;
uniform float uAmplitude;
uniform float uFrequency;
varying vec2 vUv;
void main() {
vUv = uv;
vec3 pos = position;
pos.y += sin(pos.x * uFrequency + uTime) * uAmplitude;
pos.y += cos(pos.z * uFrequency + uTime) * uAmplitude;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
`,
fragmentShader: `
varying vec2 vUv;
void main() {
gl_FragColor = vec4(vUv, 1.0, 1.0);
}
`
});
渐变颜色 #
javascript
const material = new THREE.ShaderMaterial({
uniforms: {
uColorA: { value: new THREE.Color(0xff0000) },
uColorB: { value: new THREE.Color(0x0000ff) }
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform vec3 uColorA;
uniform vec3 uColorB;
varying vec2 vUv;
void main() {
vec3 color = mix(uColorA, uColorB, vUv.y);
gl_FragColor = vec4(color, 1.0);
}
`
});
噪声效果 #
javascript
const material = new THREE.ShaderMaterial({
uniforms: {
uTime: { value: 0.0 }
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform float uTime;
varying vec2 vUv;
float random(vec2 st) {
return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
}
float noise(vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
float a = random(i);
float b = random(i + vec2(1.0, 0.0));
float c = random(i + vec2(0.0, 1.0));
float d = random(i + vec2(1.0, 1.0));
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
}
void main() {
float n = noise(vUv * 10.0 + uTime);
gl_FragColor = vec4(vec3(n), 1.0);
}
`
});
菲涅尔效果 #
javascript
const material = new THREE.ShaderMaterial({
uniforms: {
uColor: { value: new THREE.Color(0x00ffff) }
},
vertexShader: `
varying vec3 vNormal;
varying vec3 vViewPosition;
void main() {
vNormal = normalize(normalMatrix * normal);
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
vViewPosition = -mvPosition.xyz;
gl_Position = projectionMatrix * mvPosition;
}
`,
fragmentShader: `
uniform vec3 uColor;
varying vec3 vNormal;
varying vec3 vViewPosition;
void main() {
vec3 viewDir = normalize(vViewPosition);
float fresnel = pow(1.0 - abs(dot(viewDir, vNormal)), 3.0);
gl_FragColor = vec4(uColor * fresnel, 1.0);
}
`
});
全息效果 #
javascript
const material = new THREE.ShaderMaterial({
uniforms: {
uTime: { value: 0.0 }
},
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;
varying vec2 vUv;
varying vec3 vPosition;
void main() {
float scanline = sin(vPosition.y * 100.0 + uTime * 5.0) * 0.5 + 0.5;
float flicker = sin(uTime * 10.0) * 0.1 + 0.9;
vec3 color = vec3(0.0, 1.0, 1.0);
float alpha = scanline * flicker;
gl_FragColor = vec4(color, alpha);
}
`,
transparent: true,
side: THREE.DoubleSide
});
RawShaderMaterial #
RawShaderMaterial 不自动添加内置属性和 uniform:
javascript
const material = new THREE.RawShaderMaterial({
uniforms: {
projectionMatrix: { value: camera.projectionMatrix },
modelViewMatrix: { value: new THREE.Matrix4() }
},
vertexShader: `
attribute vec3 position;
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`
});
后期处理着色器 #
自定义后期处理 #
javascript
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';
const composer = new EffectComposer(renderer);
const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);
const customShader = {
uniforms: {
tDiffuse: { value: null },
uTime: { value: 0.0 }
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform sampler2D tDiffuse;
uniform float uTime;
varying vec2 vUv;
void main() {
vec2 uv = vUv;
uv.x += sin(uv.y * 10.0 + uTime) * 0.01;
vec4 color = texture2D(tDiffuse, uv);
gl_FragColor = color;
}
`
};
const customPass = new ShaderPass(customShader);
composer.addPass(customPass);
function animate() {
requestAnimationFrame(animate);
customPass.uniforms.uTime.value += 0.01;
composer.render();
}
animate();
着色器最佳实践 #
性能优化 #
glsl
// 避免在片元着色器中计算
// 不好的做法
fragmentShader: `
void main() {
float value = sin(position.x * 100.0);
}
`
// 好的做法
vertexShader: `
varying float vValue;
void main() {
vValue = sin(position.x * 100.0);
}
`
fragmentShader: `
varying float vValue;
void main() {
float value = vValue;
}
`
精度设置 #
glsl
precision highp float;
precision mediump float;
precision lowp float;
调试技巧 #
glsl
// 输出法线颜色
gl_FragColor = vec4(normal * 0.5 + 0.5, 1.0);
// 输出 UV 坐标
gl_FragColor = vec4(uv, 0.0, 1.0);
// 输出深度
float depth = gl_FragCoord.z;
gl_FragColor = vec4(vec3(depth), 1.0);
完整示例 #
javascript
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x1a1a2e);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(3, 3, 3);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
const vertexShader = `
uniform float uTime;
varying vec2 vUv;
varying vec3 vNormal;
varying vec3 vPosition;
void main() {
vUv = uv;
vNormal = normalize(normalMatrix * normal);
vPosition = position;
vec3 pos = position;
float displacement = sin(pos.x * 5.0 + uTime) * sin(pos.y * 5.0 + uTime) * 0.1;
pos += normal * displacement;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
`;
const fragmentShader = `
uniform float uTime;
uniform vec3 uColorA;
uniform vec3 uColorB;
varying vec2 vUv;
varying vec3 vNormal;
varying vec3 vPosition;
void main() {
float fresnel = pow(1.0 - abs(dot(normalize(vNormal), vec3(0.0, 0.0, 1.0))), 2.0);
vec3 color = mix(uColorA, uColorB, vUv.y);
color += fresnel * vec3(0.5);
float scanline = sin(vPosition.y * 50.0 + uTime * 5.0) * 0.1 + 0.9;
color *= scanline;
gl_FragColor = vec4(color, 1.0);
}
`;
const material = new THREE.ShaderMaterial({
uniforms: {
uTime: { value: 0.0 },
uColorA: { value: new THREE.Color(0xff6b6b) },
uColorB: { value: new THREE.Color(0x6bffb8) }
},
vertexShader,
fragmentShader,
side: THREE.DoubleSide
});
const geometry = new THREE.IcosahedronGeometry(1, 4);
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const elapsedTime = clock.getElapsedTime();
material.uniforms.uTime.value = elapsedTime;
mesh.rotation.y = elapsedTime * 0.2;
controls.update();
renderer.render(scene, camera);
}
animate();
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
下一步 #
现在你已经掌握了着色器编程,接下来学习 高级主题,探索更多 Three.js 高级功能!
最后更新:2026-03-28