本文将引导你入门Three.js中的着色器材质,从基础概念到实际应用。
着色器材质(ShaderMaterial)允许你使用自定义的GLSL(OpenGL Shading Language)代码来控制Three.js中对象的渲染方式。这为你提供了极大的灵活性,可以实现从简单的颜色变化到复杂的视觉效果。
在每个顶点上执行
控制顶点的位置、法线等属性
可以用于变形、波浪效果等
在每个像素上执行
控制像素的最终颜色
可以用于纹理、光照、颜色效果等
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();
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);
}
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);
}
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;
}
`;
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);
}
`;
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);
}
`;
逐步构建:从简单效果开始,逐步添加复杂度
可视化调试:将中间值转换为颜色以便查看
使用Three.js内置功能:可以先使用标准材质验证几何体和场景,再切换到着色器材质
GLSL语法检查:注意GLSL是强类型语言,确保类型匹配
尽可能重用着色器:为类似效果创建可配置的着色器
减少uniform更新:只在必要时更新uniform值
简化计算:在顶点着色器中计算复杂值,然后传递到片元着色器
使用预处理指令:通过#define创建可配置的着色器变体
The Book of Shaders - 优秀的GLSL入门资源
ShaderToy - 查看和学习其他着色器示例
GLSL参考 - 官方GLSL函数参考
Three.js的着色器材质是创建自定义视觉效果的有力工具。开始时可能会有些挑战,但随着实践,你将能够创建出令人惊叹的视觉效果。从简单示例开始,逐步构建更复杂的效果,享受着色器编程带来的创造力吧!