ShaderMaterial 着色器材质
1、基本概念
在 Three.js 中,ShaderMaterial(着色器材质) 是一种允许你使用 自定义 GLSL 着色器代码 来完全控制材质外观的方式。它是 Three.js 提供的最底层、最灵活的材质类型,适合需要实现复杂视觉效果或非标准渲染的场景。
很多酷炫的效果都是使用它来完成的,例如渐变的立方体等效果。关于该材质的更多介绍将在后续文章中做详细说明,此处只了解基本概念即可。
2、示例效果
通过ShaderMaterial材质创建一个渐变的立方体。

javascript
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
// 场景对象
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000);
// 渲染器
const canvas = document.querySelector("#canvas") as HTMLCanvasElement;
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
// 相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(200, 200, 0);
camera.lookAt(0, 0, 0);
// 轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 创建网格
const size = 100;
const divisions = 10;
const gridHelper = new THREE.GridHelper(size, divisions);
scene.add(gridHelper);
// 创建立方体
const geometry = new THREE.BoxGeometry(20, 60, 20);
// 创建渐变材质
const material = new THREE.ShaderMaterial({
uniforms: {
topColor: { value: new THREE.Color(0.5, 0.8, 1.0) },
bottomColor: { value: new THREE.Color(0.1, 0.1, 0.5) },
},
vertexShader: `
varying vec2 vUv;
varying vec3 vNormal;
void main() {
vUv = uv;
vNormal = normal;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform vec3 topColor;
uniform vec3 bottomColor;
varying vec2 vUv;
varying vec3 vNormal;
void main() {
vec3 color;
if (vNormal.y > 0.99) {
color = bottomColor; // 底部颜色
} else if (vNormal.y < -0.99) {
color = topColor; // 顶部颜色
} else {
color = mix(topColor, bottomColor, vUv.y);
}
gl_FragColor = vec4(color, 1.0);
}
`,
});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.y += 30;
scene.add(mesh);
// 渲染循环
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
animate();
// 窗口大小改变时,更新渲染器和相机
window.addEventListener("resize", () => {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
});