Skip to content

ShaderMaterial 着色器材质

1、基本概念

在 Three.js 中,ShaderMaterial(着色器材质) 是一种允许你使用 自定义 GLSL 着色器代码 来完全控制材质外观的方式。它是 Three.js 提供的最底层、最灵活的材质类型,适合需要实现复杂视觉效果或非标准渲染的场景。

很多酷炫的效果都是使用它来完成的,例如渐变的立方体等效果。关于该材质的更多介绍将在后续文章中做详细说明,此处只了解基本概念即可。

2、示例效果

通过ShaderMaterial材质创建一个渐变的立方体。

image-20250803224740957

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();
});