1. Three.js 着色器材质入门指南

本文将引导你入门Three.js中的着色器材质,从基础概念到实际应用。

1.1. 什么是着色器材质?

着色器材质(ShaderMaterial)允许你使用自定义的GLSL(OpenGL Shading Language)代码来控制Three.js中对象的渲染方式。这为你提供了极大的灵活性,可以实现从简单的颜色变化到复杂的视觉效果。

1.2. 两种着色器

1.2.1. 顶点着色器(Vertex Shader)

1.2.2. 片元着色器(Fragment Shader)

1.3. 基础示例

1.3.1. 创建基础着色器材质

javascript

// 1. 引入Three.js
import * as THREE from 'three';

// 2. 创建场景、相机、渲染器等基础设置
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 3. 定义着色器代码
const vertexShader = `
  varying vec2 vUv;

  void main() {
    vUv = uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
  }
`;

const fragmentShader = `
  varying vec2 vUv;

  void main() {
    // 创建简单的渐变效果
    vec3 color = mix(vec3(1.0, 0.0, 0.0), vec3(0.0, 0.0, 1.0), vUv.x);
    gl_FragColor = vec4(color, 1.0);
  }
`;

// 4. 创建着色器材质
const material = new THREE.ShaderMaterial({
  vertexShader: vertexShader,
  fragmentShader: fragmentShader
});

// 5. 创建几何体并应用材质
const geometry = new THREE.BoxGeometry(1, 1, 1);
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

camera.position.z = 3;

// 6. 动画循环
function animate() {
  requestAnimationFrame(animate);

  cube.rotation.x += 0.01;
  cube.rotation.y += 0.01;

  renderer.render(scene, camera);
}

animate();

1.4. 常用着色器技术

1.4.1. 传递变量(Varyings)

glsl

// 顶点着色器
varying vec2 vUv;
varying vec3 vPosition;

void main() {
  vUv = uv;
  vPosition = position;
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

// 片元着色器
varying vec2 vUv;
varying vec3 vPosition;

void main() {
  // 使用传递过来的变量
  float colorValue = (vPosition.y + 1.0) / 2.0; // 将Y坐标从[-1,1]映射到[0,1]
  gl_FragColor = vec4(vec3(colorValue), 1.0);
}

1.4.2. 使用Uniforms传递数据

javascript

// 在JavaScript中定义uniforms
const material = new THREE.ShaderMaterial({
  vertexShader: vertexShader,
  fragmentShader: fragmentShader,
  uniforms: {
    time: { value: 0.0 },
    color1: { value: new THREE.Color(0xff0000) },
    color2: { value: new THREE.Color(0x0000ff) }
  }
});

// 在着色器中使用uniforms
const fragmentShader = `
  uniform float time;
  uniform vec3 color1;
  uniform vec3 color2;
  varying vec2 vUv;

  void main() {
    // 使用time创建动画效果
    float mixValue = (sin(time) + 1.0) / 2.0;
    vec3 color = mix(color1, color2, mix(vUv.x, vUv.y, mixValue));
    gl_FragColor = vec4(color, 1.0);
  }
`;

// 在动画循环中更新uniforms
function animate() {
  requestAnimationFrame(animate);

  material.uniforms.time.value += 0.01;

  renderer.render(scene, camera);
}

1.4.3. 纹理采样

javascript

// 加载纹理
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load('texture.jpg');

const material = new THREE.ShaderMaterial({
  vertexShader: vertexShader,
  fragmentShader: fragmentShader,
  uniforms: {
    uTexture: { value: texture }
  }
});

// 着色器中的纹理采样
const fragmentShader = `
  uniform sampler2D uTexture;
  varying vec2 vUv;

  void main() {
    vec4 texColor = texture2D(uTexture, vUv);
    gl_FragColor = texColor;
  }
`;

1.5. 实用着色器示例

1.5.1. 波浪效果

javascript

const vertexShader = `
  uniform float time;
  varying vec2 vUv;
  varying vec3 vPosition;

  void main() {
    vUv = uv;
    vPosition = position;

    // 创建波浪效果
    float wave = sin(position.x * 5.0 + time * 2.0) * 0.1;
    vec3 newPosition = position;
    newPosition.y += wave;

    gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
  }
`;

const fragmentShader = `
  uniform float time;
  varying vec2 vUv;
  varying vec3 vPosition;

  void main() {
    // 基于位置和时间的颜色
    float r = sin(vPosition.x * 2.0 + time) * 0.5 + 0.5;
    float g = cos(vPosition.y * 2.0 + time * 1.5) * 0.5 + 0.5;
    float b = sin(vPosition.z * 2.0 + time * 0.5) * 0.5 + 0.5;

    gl_FragColor = vec4(r, g, b, 1.0);
  }
`;

1.5.2. 边缘发光效果

javascript

const vertexShader = `
  uniform float time;
  varying vec3 vNormal;
  varying vec3 vPosition;

  void main() {
    vNormal = normalize(normalMatrix * normal);
    vPosition = position;

    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
  }
`;

const fragmentShader = `
  uniform float time;
  varying vec3 vNormal;
  varying vec3 vPosition;

  void main() {
    // 基础颜色
    vec3 baseColor = vec3(0.2, 0.4, 0.8);

    // 计算边缘强度(法线与视线夹角)
    vec3 viewDirection = normalize(-vPosition);
    float edge = 1.0 - abs(dot(vNormal, viewDirection));
    edge = pow(edge, 3.0); // 增强效果

    // 添加脉动效果
    float pulse = sin(time * 2.0) * 0.5 + 0.5;
    edge *= pulse;

    // 组合颜色
    vec3 finalColor = baseColor + edge * vec3(1.0, 0.5, 0.2);

    gl_FragColor = vec4(finalColor, 1.0);
  }
`;

1.6. 调试技巧

  1. 逐步构建:从简单效果开始,逐步添加复杂度

  2. 可视化调试:将中间值转换为颜色以便查看

  3. 使用Three.js内置功能:可以先使用标准材质验证几何体和场景,再切换到着色器材质

  4. GLSL语法检查:注意GLSL是强类型语言,确保类型匹配

1.7. 性能优化建议

  1. 尽可能重用着色器:为类似效果创建可配置的着色器

  2. 减少uniform更新:只在必要时更新uniform值

  3. 简化计算:在顶点着色器中计算复杂值,然后传递到片元着色器

  4. 使用预处理指令:通过#define创建可配置的着色器变体

1.8. 学习资源

  1. Three.js官方文档

  2. The Book of Shaders - 优秀的GLSL入门资源

  3. ShaderToy - 查看和学习其他着色器示例

  4. GLSL参考 - 官方GLSL函数参考

1.9. 结语

Three.js的着色器材质是创建自定义视觉效果的有力工具。开始时可能会有些挑战,但随着实践,你将能够创建出令人惊叹的视觉效果。从简单示例开始,逐步构建更复杂的效果,享受着色器编程带来的创造力吧!