ThreeJS 着色器高级应用 - 孔明灯特效

0 阅读4分钟

本文档涵盖Three.js中高级着色器应用,以孔明灯特效为例,展示复杂着色器的实际应用。

最终效果如图: Title

1. 高级着色器应用概述

1.1 环境贴图与光照

import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import gsap from "gsap";
import * as dat from "dat.gui";
import vertexShader from "../shaders/flylight/vertex.glsl";
import fragmentShader from "../shaders/flylight/fragment.glsl";
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";

// 初始化场景
const scene = new THREE.Scene();

// 加载HDR环境贴图
const rgbeLoader = new RGBELoader();
rgbeLoader.loadAsync("./assets/2k.hdr").then((texture) => {
  texture.mapping = THREE.EquirectangularReflectionMapping;
  scene.background = texture;  // 设置背景贴图
  scene.environment = texture;  // 设置环境贴图
});

1.2 渲染器高级设置

const renderer = new THREE.WebGLRenderer({ alpha: true });

// 启用色调映射以获得更好的视觉效果
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 0.2;

2. 孔明灯模型加载与着色器应用

2.1 GLTF模型加载

const gltfLoader = new GLTFLoader();
let LightBox = null;

gltfLoader.load("./assets/model/flyLight.glb", (gltf) => {
  console.log(gltf);

  // 获取孔明灯网格
  LightBox = gltf.scene.children[1];
  
  // 应用自定义着色器材质
  LightBox.material = shaderMaterial;

  // 批量创建孔明灯实例
  for (let i = 0; i < 150; i++) {
    let flyLight = gltf.scene.clone(true);  // 克隆模型
    let x = (Math.random() - 0.5) * 300;  // 随机X位置
    let z = (Math.random() - 0.5) * 300;  // 随机Z位置
    let y = Math.random() * 60 + 25;      // 随机Y位置
    
    flyLight.position.set(x, y, z);
    
    // 添加旋转动画
    gsap.to(flyLight.rotation, {
      y: 2 * Math.PI,
      duration: 10 + Math.random() * 30,  // 随机持续时间
      repeat: -1,                         // 无限重复
    });
    
    // 添加位置动画
    gsap.to(flyLight.position, {
      x: "+=" + Math.random() * 5,        // X方向随机移动
      y: "+=" + Math.random() * 20,       // Y方向随机移动
      yoyo: true,                         // 往返运动
      duration: 5 + Math.random() * 10,   // 随机持续时间
      repeat: -1,                         // 无限重复
    });
    
    scene.add(flyLight);
  }
});

3. 孔明灯顶点着色器详解

3.1 顶点着色器代码

precision lowp float;

varying vec4 vPosition;
varying vec4 gPosition;

void main(){
    vec4 modelPosition = modelMatrix * vec4( position, 1.0 );

    vPosition = modelPosition;
    gPosition = vec4( position, 1.0 );
    gl_Position =  projectionMatrix * viewMatrix * modelPosition;
}

3.2 变量传递机制

  • vPosition:传递模型空间中的位置信息
  • gPosition:传递原始几何位置信息
  • 这些变量将在片元着色器中使用

4. 孔明灯片元着色器详解

4.1 片元着色器代码

precision lowp float;
varying vec4 vPosition;
varying vec4 gPosition;

void main(){
    vec4 redColor = vec4(1,0,0,1);      // 红色
    vec4 yellowColor = vec4(1,1,0.5,1); // 黄色
    vec4 mixColor = mix(yellowColor,redColor,gPosition.y/3.0); // 基于Y坐标的颜色混合

    // 根据面朝向设置不同颜色
    if(gl_FrontFacing){
        // 正面朝向相机的像素,添加高度影响和亮度调整
        gl_FragColor = vec4(mixColor.xyz-(vPosition.y-20.0)/80.0-0.1,1);
    }else{
        // 背面像素使用基础颜色
        gl_FragColor = vec4(mixColor.xyz,1);
    }
}

4.2 颜色混合技术

// 基于Y坐标的颜色插值
vec4 mixColor = mix(yellowColor,redColor,gPosition.y/3.0);

4.3 面朝向判断

// 使用gl_FrontFacing区分正面和背面
if(gl_FrontFacing){
    // 正面:添加高度影响和亮度调整
    gl_FragColor = vec4(mixColor.xyz-(vPosition.y-20.0)/80.0-0.1,1);
}else{
    // 背面:使用基础颜色
    gl_FrontFacing = vec4(mixColor.xyz,1);
}

5. 自定义着色器材质创建

5.1 着色器材质初始化

// 创建孔明灯专用着色器材质
const shaderMaterial = new THREE.ShaderMaterial({
  vertexShader: vertexShader,      // 顶点着色器
  fragmentShader: fragmentShader,  // 片元着色器
  uniforms: {},                   // uniform变量
  side: THREE.DoubleSide,         // 双面渲染
  // transparent: true,           // 透明渲染(可选)
});

6. 相机控制与视角设置

6.1 高级相机控制

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;           // 启用阻尼效果
controls.autoRotate = true;              // 自动旋转
controls.autoRotateSpeed = 0.1;          // 自动旋转速度
controls.maxPolarAngle = (Math.PI / 3) * 2;  // 最大极角限制
controls.minPolarAngle = (Math.PI / 3) * 2;  // 最小极角限制

6.2 相机角度限制

// 限制相機垂直旋转范围,防止过度俯仰
controls.maxPolarAngle = (Math.PI / 3) * 2;
controls.minPolarAngle = (Math.PI / 3) * 2;

7. 动画系统集成

7.1 GSAP动画控制

// 旋转动画
gsap.to(flyLight.rotation, {
  y: 2 * Math.PI,                    // 旋转一圈
  duration: 10 + Math.random() * 30, // 随机持续时间
  repeat: -1,                        // 无限重复
});

// 位置动画
gsap.to(flyLight.position, {
  x: "+=" + Math.random() * 5,       // X方向随机移动
  y: "+=" + Math.random() * 20,      // Y方向随机移动
  yoyo: true,                        // 往返运动
  duration: 5 + Math.random() * 10,  // 随机持续时间
  repeat: -1,                        // 无限重复
});

8. 性能优化策略

8.1 批量渲染优化

// 使用克隆技术批量创建对象
for (let i = 0; i < 150; i++) {
  let flyLight = gltf.scene.clone(true);
  // ...
  scene.add(flyLight);
}

8.2 减少Draw Call

  • 使用相同的着色器材质
  • 合理组织渲染批次
  • 利用实例化渲染技术

9. 着色器参数化设计

9.1 Uniform变量扩展

虽然当前示例中uniforms为空,但在实际应用中可以扩展:

const shaderMaterial = new THREE.ShaderMaterial({
  vertexShader: vertexShader,
  fragmentShader: fragmentShader,
  uniforms: {
    uTime: { value: 0 },              // 时间
    uBrightness: { value: 1.0 },      // 亮度
    uColorMix: { value: 0.5 },        // 颜色混合系数
  },
  side: THREE.DoubleSide,
});

10. 故障排除与调试

10.1 常见问题

  1. 着色器编译错误:检查GLSL语法
  2. 性能问题:简化着色器计算
  3. 光照问题:检查环境贴图设置
  4. 动画卡顿:检查动画循环和更新频率

10.2 调试技巧

  1. 逐行注释:定位问题代码
  2. 变量输出:将中间值输出到颜色
  3. 简化模型:使用简单几何体测试着色器

总结

本章展示了Three.js中高级着色器的实际应用,以孔明灯特效为例:

  1. 环境贴图和高级渲染设置
  2. GLTF模型加载和着色器应用
  3. 复杂顶点和片元着色器的编写
  4. 动画系统与着色器的集成
  5. 相机控制和性能优化
  6. 批量对象管理和渲染优化

通过这种综合应用,可以创建出具有专业品质的3D视觉效果。