ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

three.js Scene 类深度解析:场景背景、环境贴图、雾效与材质覆盖的完整配置指南

three.js Scene 类深度解析:场景背景、环境贴图、雾效与材质覆盖的完整配置指南 three.js Scene 类深度解析场景背景、环境贴图、雾效与材质覆盖的完整配置指南【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js本篇技术指南以 three.js 官方 API 文档中Scene类的定义为主线逐一讲解background、environment、fog、overrideMaterial等核心属性及其全部可调参数含默认值与取值范围并结合 src/scenes/Scene.js 的构造函数与 src/renderers/webgl/WebGLBackground.js、src/renderers/WebGLRenderer.js 的源码实现说明每个属性在渲染管线中究竟如何生效。读完后你将能够独立完成一个带天空盒背景、IBL 环境光、指数雾和全局材质覆盖的 three.js 场景并理解其底层渲染机制。Scene 的继承体系与构造过程Scene是 three.js 中设置渲染内容与位置的容器所有需要被渲染的 3D 对象——网格Mesh、线条Line、灯光Light——都必须放入某个 Scene 中由渲染器以该 Scene 为根遍历绘制。从继承链看Scene位于EventDispatcher → Object3D → Scene三级体系的最末端它继承了Object3D的完整变换能力position、rotation、scale、matrix 等以及EventDispatcher的事件订阅机制因此 Scene 本身也可以作为节点加入层级树、发出change事件。在 src/scenes/Scene.js 中构造函数做了如下事情class Scene extends Object3D { constructor() { super(); // 类型测试标志 this.isScene true; this.type Scene; // 以下属性全部初始化为 null / 默认值 this.background null; // 场景背景 this.environment null; // 环境贴图IBL this.fog null; // 雾效果 this.backgroundBlurriness 0; // 背景模糊度 this.backgroundIntensity 1; // 背景亮度衰减 this.backgroundRotation new Euler(); // 背景旋转弧度 this.environmentIntensity 1; // 环境贴图亮度衰减 this.environmentRotation new Euler(); // 环境贴图旋转弧度 this.overrideMaterial null; // 全局材质覆盖 if ( typeof __THREE_DEVTOOLS__ ! undefined ) { __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( observe, { detail: this } ) ); } } }两个值得注意的细节new Scene()无参构造所有视觉属性都是惰性赋值的——不设置背景和环境贴图时渲染器会回退到清屏颜色场景本身不产生任何额外绘制开销。构造函数末尾的__THREE_DEVTOOLS__分支src/scenes/Scene.js#L115-L119当页面加载了 three.js DevTools 浏览器扩展时每个 Scene 的创建都会向扩展派发一个observe自定义事件扩展借此把场景树挂到检查面板上扩展源码见 devtools/ 目录。这是一个仅在 DevTools 存在时生效的条件分支不影响库的核心逻辑。Scene通过 src/Three.Core.js#L10 导出export { Scene } from ./scenes/Scene.js因此在 ES 模块或全局THREE命名空间下都可以通过new Scene()/new THREE.Scene()访问。属性总览属性类型默认值作用对象.backgroundColor \| Texturenull整个画面的背景.backgroundBlurrinessnumber0~1 浮点0仅background上的环境贴图.backgroundIntensitynumber1仅背景纹理.backgroundRotationEuler弧度(0,0,0)仅background上的环境贴图.environmentTexturenull场景内所有物理材质PBR.environmentIntensitynumber1仅environment上的环境贴图.environmentRotationEuler弧度(0,0,0)使用environment的物理材质.fogFog \| FogExp2null场景中一切被渲染对象.isSceneboolean只读true类型测试标志.overrideMaterialMaterialnull场景中所有对象可被allowOverride豁免background三种背景形态及其渲染路径.background接受三类输入默认null纯色Color定义均匀的彩色背景普通纹理Texture定义平面纹理背景立方体贴图或等距柱状投影贴图CubeTexture / equirectangular定义天空盒Skybox。从源码看渲染器对这三种输入走了完全不同的分支。src/renderers/webgl/WebGLBackground.js 的render与addToRenderList函数中function getBackground( scene ) { let background scene.isScene true ? scene.background : null; if ( background background.isTexture ) { const usePMREM scene.backgroundBlurriness 0; // use PMREM if the user wants to blur the background background environments.get( background, usePMREM ); } return background; }三个分支的实际实现Color 分支直接把颜色写入 GL 清屏状态setClear( background, 1 )不产生任何几何体是最省开销的路径立方体贴图 / CubeUV 分支懒创建一个BoxGeometry(1,1,1)的反面BackSide盒子材质为BackgroundCubeMaterial片元着色器见 src/renderers/shaders/ShaderLib/backgroundCube.glsl.js每帧把盒子矩阵同步到相机世界位置boxMesh.onBeforeRender并关闭深度测试与雾depthTest: false, fog: false使天空盒永远贴在最远处普通 2D 纹理分支懒创建一个PlaneGeometry(2,2)的平面材质为BackgroundMaterial着色器见 src/renderers/shaders/ShaderLib/background.glsl.js通过uvTransformuniform 应用纹理自身的矩阵变换。一个容易踩坑的点两条几何体分支的材质都显式设置了allowOverride: falsesrc/renderers/webgl/WebGLBackground.js#L107即背景永远不会被scene.overrideMaterial覆盖——调试模式下给场景套上红色 MeshBasicMaterial 时天空盒依然正常显示这正是靠这个标志保证的。等距柱状投影贴图如全景图作为背景时仓库中的官方示例 examples/webgl_panorama_equirectangular.html 展示了典型用法把TextureLoader加载的 equirectangular 贴图赋给scene.background渲染器会自动识别其mapping并走 CubeUV 采样路径。backgroundBlurriness、backgroundIntensity、backgroundRotation背景的三个调参旋钮这三个属性只对环境贴图类背景立方体贴图 / 等距柱状 / CubeUV或部分只对背景纹理生效文档中明确标注了各自的作用域backgroundBlurriness : number默认0背景模糊度合法输入为0到1之间的浮点数。源码中它的作用点有两处在getBackground里backgroundBlurriness 0会触发PMREM 预处理environments.get( background, usePMREM )把原始环境图离线转换为带各向异性模糊的 PMREM 纹理在backgroundCube片元着色器中textureCubeUV( envMap, backgroundRotation * vWorldDirection, backgroundBlurriness )的第三个参数直接控制 Mipmap 级别LOD从而得到物理上正确的环境反射模糊效果而不仅仅是高斯模糊滤镜。设为1时背景完全糊化常用于背景虚化、突出前景物体的布景效果。backgroundIntensity : number默认1衰减背景的颜色亮度只对背景纹理起作用。在片元着色器中其作用是一句乘法// src/renderers/shaders/ShaderLib/backgroundCube.glsl.js uniform float backgroundBlurriness; uniform float backgroundIntensity; uniform mat3 backgroundRotation; void main() { vec4 texColor textureCube( envMap, backgroundRotation * vWorldDirection ); // ...CubeUV 分支同理 texColor.rgb * backgroundIntensity; }渲染器每帧把scene.backgroundIntensity写入 uniformsrc/renderers/webgl/WebGLBackground.js#L138因此动态修改该值即时生效。backgroundRotation : Euler默认(0,0,0)背景绕原点的旋转单位为弧度同样只影响环境贴图类背景。渲染器把Euler转成Matrix3旋转矩阵后传入着色器// src/renderers/webgl/WebGLBackground.js节选 boxMesh.material.uniforms.backgroundRotation.value .setFromMatrix4( _m1.makeRotationFromEuler( scene.backgroundRotation ) ).transpose();注意源码中的注释矩阵是正交矩阵所以这里用更高效的transpose()代替invert()来得到逆变换。另外对于非渲染目标纹理的立方体贴图还会额外左乘一个水平翻转矩阵_m以补偿立方体贴图与 CubeUV 贴图在 UV 方向上的差异。environment为全部 PBR 材质提供 IBL.environment设置一个环境贴图供场景中所有物理材质物理渲染 PBR 材质共享默认null。文档特别强调了一条限制它无法覆盖材质上已显式指派的envMap纹理——材质自带的envMap优先级更高。src/renderers/WebGLRenderer.js 中的实现印证了这一点// L2198只有这三类材质才会从场景取环境贴图 materialProperties.environment ( material.isMeshStandardMaterial || material.isMeshLambertMaterial || material.isMeshPhongMaterial ) ? scene.environment : null; // L2729-L2731仅当材质没有自己的 envMap 时才回退到 scene.environment if ( ( material.isMeshStandardMaterial || material.isMeshLambertMaterial || material.isMeshPhongMaterial ) material.envMap null scene.environment ! null ) { m_uniforms.envMapIntensity.value scene.environmentIntensity; }也就是说scene.environment实质上是场景级环境贴图的缺省值fallbackMeshStandardMaterial、MeshLambertMaterial、MeshPhongMaterial在envMap为null时自动继承它这省去了逐材质设置 IBL 的样板代码。典型工作流是先用PMREMGenerator把 HDR 全景图预处理成 PMREM 纹理再赋给scene.environment相关示例可参考 examples/webgl_materials_envmaps.html 与 examples/webgl_lights_physical.html。environmentIntensity : number默认1衰减环境贴图的颜色写入材质的envMapIntensityuniform仅影响经scene.environment生效的环境贴图。environmentRotation : Euler默认(0,0,0)环境贴图旋转弧度。对应源码// src/renderers/WebGLRenderer.js L2203 materialProperties.envMapRotation ( materialProperties.environment ! null material.envMap null ) ? scene.environmentRotation : material.envMapRotation;只有当材质真正吃到了scene.environment即自身envMap为null时才会使用场景级的environmentRotation否则仍用材质自己的envMapRotation。fog线性雾与指数雾.fog接受Fog或FogExp2实例默认null。设置后雾会作用于场景中一切被渲染的物体背景几何体因材质fog: false而豁免。Fog线性雾src/scenes/Fog.js 定义了随距离线性变浓的雾class Fog { constructor( color, near 1, far 1000 ) { this.isFog true; this.color new Color( color ); this.near near; // 最小生效距离近于此值的物体不受雾影响 this.far far; // 最大距离超过此值完全被雾吞没 } }near距离相机小于该值的物体不受雾影响默认1far距离相机大于该值的物体完全被雾覆盖默认1000。文档给出的官方用法示例const scene new THREE.Scene(); scene.fog new THREE.Fog( 0xcccccc, 10, 15 );FogExp2指数平方雾src/scenes/FogExp2.js 提供比线性雾更自然的近处清晰、远处快速变浓的指数平方衰减const scene new THREE.Scene(); scene.fog new THREE.FogExp2( 0xcccccc, 0.002 );其唯一参数density密度默认0.00025决定雾变浓的速度不需要指定 near/far更适合开放世界场景。overrideMaterial 与 allowOverride全局材质覆盖.overrideMaterial默认null强制场景中一切物体用指定材质渲染是排查模型、调试线框/点云、做全白场景截图时的利器scene.overrideMaterial new THREE.MeshBasicMaterial( { color: 0xff0000 } );但文档同时指出可以豁免个别材质把Material#allowOverride设为false即可排除。该标志定义在 src/materials/Material.js#L439默认true渲染器在两个渲染后端中都做了检查// src/renderers/WebGLRenderer.js L2132 if ( material.allowOverride true overrideMaterial ! null ) { // 使用 overrideMaterial 替换原始材质 }背景盒子/平面材质allowOverride: false正是利用这一机制免于被调试材质覆盖的见上一节的WebGLBackground.js源码。copy() 与 toJSON()场景的复制与序列化Scene重写了两个核心方法值得了解其行为边界copy( source, recursive )src/scenes/Scene.js#L123-L144copy( source, recursive ) { super.copy( source, recursive ); if ( source.background ! null ) this.background source.background.clone(); if ( source.environment ! null ) this.environment source.environment.clone(); if ( source.fog ! null ) this.fog source.fog.clone(); this.backgroundBlurriness source.backgroundBlurriness; this.backgroundIntensity source.backgroundIntensity; this.backgroundRotation.copy( source.backgroundRotation ); this.environmentIntensity source.environmentIntensity; this.environmentRotation.copy( source.environmentRotation ); if ( source.overrideMaterial ! null ) this.overrideMaterial source.overrideMaterial.clone(); this.matrixAutoUpdate source.matrixAutoUpdate; return this; }要点纹理类属性background / environment / overrideMaterial与雾都是深克隆clone()标量与 Euler 直接拷贝源为null的属性则保持目标自身的null值不变。toJSON( meta )src/scenes/Scene.js#L146-L161在父类序列化的基础上额外写入fog、backgroundBlurriness、backgroundIntensity、backgroundRotation转为数组、environmentIntensity、environmentRotation。这是 glTF / 编辑器场景文件能还原场景雾效与背景调参的来源。综合实战一个带天空盒、IBL、雾与调试覆盖的完整场景把上述全部属性组合起来一个典型的 three.js 场景初始化如下import * as THREE from three; import { OrbitControls } from three/addons/controls/OrbitControls.js; import { RoomEnvironment } from three/addons/environments/RoomEnvironment.js; import { PMREMGenerator } from three; const scene new THREE.Scene(); // 1. 背景环境贴图 模糊 降亮 旋转 const pmremGenerator new THREE.PMREMGenerator( renderer ); scene.background pmremGenerator.fromScene( new RoomEnvironment() ).texture; scene.backgroundBlurriness 0.4; // 0~1越大越糊触发 PMREM 路径 scene.backgroundIntensity 0.8; // 背景整体压暗 scene.backgroundRotation.set( 0, Math.PI / 4, 0 ); // 偏航旋转 45° // 2. 环境光同一张 PMREM 图喂给所有 PBR 材质 scene.environment scene.background; scene.environmentIntensity 1.2; // 3. 雾近处清晰、远处指数变浓 scene.fog new THREE.FogExp2( 0xcccccc, 0.002 ); // 4. 调试用材质覆盖发布前删除 // scene.overrideMaterial new THREE.MeshBasicMaterial( { wireframe: true } ); const camera new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.1, 100 ); camera.position.set( 0, 1.6, 4 ); const renderer new THREE.WebGLRenderer( { antialias: true } ); renderer.setSize( window.innerWidth, window.innerHeight ); document.body.appendChild( renderer.domElement ); const controls new OrbitControls( camera, renderer.domElement ); const cube new THREE.Mesh( new THREE.BoxGeometry( 1, 1, 1 ), new THREE.MeshStandardMaterial( { color: 0x88aacc, roughness: 0.3, metalness: 0.4 } ) ); scene.add( cube ); // Scene 继承自 Object3D用法与普通节点完全一致 renderer.setAnimationLoop( () { controls.update(); renderer.render( scene, camera ); } );运行效果上天空盒随backgroundRotation偏航 45°、整体压暗 20% 并带模糊MeshStandardMaterial在未显式指定envMap的情况下自动继承scene.environment得到 IBL 反射远处物体被FogExp2逐渐吞没。若临时打开overrideMaterial除背景外的所有物体会变成红色/线框材质——这正是调试场景拓扑时的标准手法。小结Scene是 three.js 渲染体系中最轻量的全局配置面板background三态纯色 / 平面纹理 / 天空盒对应三种渲染路径backgroundBlurriness0~1、backgroundIntensity、backgroundRotation弧度是其专属调参旋钮environment是场景级 IBL 缺省源仅对envMap为null的 Standard/Lambert/Phong 材质生效environmentIntensity/environmentRotation配套调节fog支持Fog(color, near1, far1000)线性雾与FogExp2(color, density0.00025)指数雾两种形态overrideMaterial配合Material#allowOverride实现全局覆盖 局部豁免的调试能力copy()对纹理类属性做深克隆toJSON()序列化雾与背景/环境调参支撑场景文件还原。所有行为的最终实现均可在 src/scenes/Scene.js、src/scenes/Fog.js、src/scenes/FogExp2.js、src/renderers/webgl/WebGLBackground.js 与 src/renderers/WebGLRenderer.js 中逐行查证。【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进