You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Three.js/R3F中预计算阴影?编辑器场景阴影烘焙方案

基于R3F/Three.js实现静态阴影预烘焙方案

核心实现逻辑(对标Trackmania的预烘焙机制)

Trackmania的核心思路就是把静态场景的光照和阴影提前计算好,存储为纹理文件,运行时直接将纹理贴在物体上,完全跳过实时阴影计算流程。放到R3F/Three.js的场景里,你可以按以下步骤落地:

1. 先区分静态/动态物体

第一步必须在编辑器里给物体加标记,明确哪些是创建后就不会移动、变形的(静态物体),哪些是会动态变化的(动态物体)。阴影烘焙只针对静态物体,动态物体要么用简化版实时阴影,要么用光照探针补充环境光。

2. 用Three.js的Lightmap系统完成烘焙

Three.js自带成熟的光照贴图烘焙工具,具体操作流程:

  • 给每个静态物体的材质分配空白的lightMap纹理,纹理尺寸按需设置:大物体用1024x1024,小物体用256x256即可,避免无意义的高分辨率浪费内存
  • 配置烘焙专用场景:关闭实时阴影开关,把光源参数调整到和最终展示场景完全一致(否则烘焙出的光影会和实际展示效果脱节)
  • 调用renderer.generateLightMap或LightmapGenerator工具,将阴影和光照信息渲染到对应的lightMap纹理上
  • 烘焙完成后切换到展示模式,静态物体直接使用烘焙好的lightMap,同时关闭它们的receiveShadow属性和光源的castShadow属性,彻底禁用实时阴影计算

3. 编辑器端的烘焙流程优化

  • 不要单个物体单独烘焙,把相邻的静态物体合并为一组,统一烘焙到同一张光照贴图(类似纹理图集),减少纹理数量和Draw Call
  • 烘焙完成后必须对纹理进行压缩,用Basis Universal、ETC2这类浏览器原生支持的压缩格式,显存占用能降到原有的1/4甚至更低
  • 光照贴图不需要Alpha通道,直接用RGB8或RGB565格式存储,进一步压缩内存占用

光照贴图方案的可行性(针对大量物体的场景)

这个方案完全可行,但需要做好针对性优化,才能避免内存爆炸和性能下降:

1. 内存控制技巧

  • 合并纹理与几何体:将小型静态物体合并为单个BufferGeometry,再统一烘焙到一张光照贴图,避免几百个物体对应几百张纹理的内存浪费
  • 分级精度分配:根据物体的大小、视觉优先级分配纹理分辨率——远处的、微小的物体用低分辨率,近处的大物体用高分辨率
  • 分块加载:如果场景规模极大,就将场景拆分为若干区块,每个区块单独烘焙,用户浏览到对应区块时再加载该区块的烘焙纹理,不用一次性全塞进内存

2. 展示阶段的性能保障

  • 静态物体彻底关闭所有实时阴影相关设置,仅给动态物体开启简化版实时阴影(比如把阴影贴图分辨率降到1024x1024,关闭软阴影效果)
  • 合并后的静态物体用单个BufferGeometry渲染,Draw Call能从几百降到个位数,渲染性能会大幅提升
  • 优先使用WebGL2的纹理压缩功能,目前主流浏览器均支持,压缩后的纹理几乎不会占用过多显存

补充方案(混合场景适配)

如果场景中动态物体占比高,纯光照贴图方案不够用,可以结合以下两种方式:

  • 光照探针(Light Probes):为动态物体提供近似的环境光照,无需实时计算全局光照
  • 静态烘焙+动态投影:静态物体的阴影烘焙到光照贴图,动态物体的阴影仅实时投影到静态物体上,同时限制阴影贴图分辨率,平衡视觉效果和性能

简化代码示例(R3F中触发烘焙)

import { useThree } from '@react-three/fiber';
import { LightmapGenerator, WebGLRenderTarget } from 'three';

// 编辑器模式下触发烘焙的函数
const bakeStaticShadows = () => {
  const { scene, renderer, camera } = useThree();
  
  // 筛选所有标记为静态的网格物体
  const staticMeshes = scene.children.filter(obj => obj.userData.isStatic && obj.isMesh);
  
  // 给每个静态物体分配光照贴图
  staticMeshes.forEach(mesh => {
    // 根据物体尺寸选择纹理分辨率,示例用512x512
    const lightMapTarget = new WebGLRenderTarget(512, 512, { type: THREE.HalfFloatType });
    mesh.material.lightMap = lightMapTarget.texture;
    mesh.material.lightMapIntensity = 1.2; // 可根据场景调整光照强度
  });
  
  // 执行光照贴图烘焙
  const lightmapGenerator = new LightmapGenerator(scene);
  lightmapGenerator.computeLightmaps(staticMeshes, camera);
  
  // 切换到展示模式,禁用实时阴影
  scene.traverse(obj => {
    if (obj.isLight) obj.castShadow = false;
    if (obj.userData.isStatic) obj.receiveShadow = false;
  });
};

内容的提问来源于stack exchange,提问作者delorme victor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 19:22:52