1. GLSL语言基础

首先我们系统地梳理一下 Three.js 中的 GLSL 语言基础

GLSL 是 OpenGL Shading Language 的缩写,它是一种类 C 的高级着色器语言,用于在 GPU 上执行的可编程渲染管线中编写着色器。在 Three.js 中,我们通过 ShaderMaterial 或 RawShaderMaterial 使用 GLSL 编写顶点着色器和片段着色器。


2. 1. 核心概念

在 Three.js 的渲染管线中,有两个必须的着色器阶段:

一个最简单的 Three.js 着色器材质结构:

javascript

const material = new THREE.ShaderMaterial({
    vertexShader: vertexShaderSource,
    fragmentShader: fragmentShaderSource,
    uniforms: { /* 传递外部变量 */ }
});

3. 2. GLSL 基础语法(类 C)

数据类型

  vec3 color = vec3(1.0, 0.0, 0.0);
  float r = color.r;
  vec2 uv = color.xy;

变量修饰符

内置变量

常用函数

GLSL 内置了大量优化过的数学函数:


4. 3. Three.js 中的版本与上下文

Three.js 默认使用 WebGL 1.0 和 GLSL 1.00(即较旧的语法)。如果需要使用现代语法(in/out),需明确指定:

javascript

const material = new THREE.RawShaderMaterial({
    glslVersion: THREE.GLSL3, // 使用 GLSL 300 es
    vertexShader: `#version 300 es ...`,
    fragmentShader: `#version 300 es ...`,
    uniforms: {}
});

关键区别示例:

GLSL 100 (旧版本,默认)

glsl

// 顶点着色器
attribute vec3 position;
attribute vec2 uv;
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
varying vec2 vUv;

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

// 片段着色器
varying vec2 vUv;
uniform sampler2D map;

void main() {
    gl_FragColor = texture2D(map, vUv);
}

GLSL 300 es (现代语法)

glsl

#version 300 es
precision highp float;

// 顶点着色器
in vec3 position;
in vec2 uv;
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
out vec2 vUv;

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

// 片段着色器
in vec2 vUv;
uniform sampler2D map;
out vec4 fragColor;

void main() {
    fragColor = texture(map, vUv);
}

5. 4. Three.js 特有的自动 Uniforms 和 Attributes

当使用 ShaderMaterial(而非 RawShaderMaterial)时,Three.js 会自动为你提供一些常用的 uniforms 和 attributes,无需手动声明。

常见自动 Uniforms:

常见自动 Attributes:

这意味着在 ShaderMaterial 的顶点着色器中,你可以直接使用 uniform mat4 projectionMatrix; 而无需在 JavaScript 中定义它。


6. 5. 一个完整的 Three.js 着色器示例

javascript

// JavaScript 部分
const vertexShader = `
    varying vec2 vUv;
    varying vec3 vPosition;

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

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

    void main() {
        // 创建一个基于UV和时间的动态效果
        float wave = sin(vUv.x * 10.0 + time) * 0.5 + 0.5;
        vec3 finalColor = color * wave;

        // 添加一个基于位置的渐变
        float gradient = vPosition.y * 0.5 + 0.5;
        finalColor *= gradient;

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

const material = new THREE.ShaderMaterial({
    vertexShader: vertexShader,
    fragmentShader: fragmentShader,
    uniforms: {
        time: { value: 0.0 },
        color: { value: new THREE.Color(0xff00ff) }
    }
});

// 在动画循环中更新 uniform
function animate() {
    material.uniforms.time.value = performance.now() / 1000;
}

7. 6. 调试与最佳实践

  1. 精度指定: 在片段着色器顶部,通常需要指定浮点数精度 precision mediump float;(或 highp)。在 GLSL 300 es 中,这通常是必须的。

  2. 逐步开发: 从最简单的纯色输出开始,逐步添加复杂计算。

  3. 使用归一化坐标: 尽量在 [0, 1] 范围内处理颜色和 UV。

  4. 向量化运算: GLSL 对向量运算高度优化,尽量使用 vec3 a = b * c; 而非逐分量计算。

  5. 重用计算: 将重复的计算结果存储在局部变量中。

8. 7. 学习资源

掌握 GLSL 是释放 Three.js 和 WebGL 真正潜力的关键。从修改简单示例开始,慢慢理解光线、材质和纹理的编写,你会逐渐能够创造出令人惊叹的视觉特效。