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