three.js Scene 类深度解析:场景背景、环境贴图、雾效与材质覆盖的完整配置指南
2026/9/8 16:32:41 网站建设 项目流程

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

【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js

本篇技术指南以 three.js 官方 API 文档中Scene类的定义为主线,逐一讲解backgroundenvironmentfogoverrideMaterial等核心属性及其全部可调参数(含默认值与取值范围),并结合 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 } ) ); } } }

两个值得注意的细节:

  1. new Scene()无参构造,所有视觉属性都是惰性赋值的——不设置背景和环境贴图时,渲染器会回退到清屏颜色,场景本身不产生任何额外绘制开销。
  2. 构造函数末尾的__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整个画面的背景
.backgroundBlurrinessnumber(0~1 浮点)0background上的环境贴图
.backgroundIntensitynumber1仅背景纹理
.backgroundRotationEuler(弧度)(0,0,0)background上的环境贴图
.environmentTexturenull场景内所有物理材质(PBR)
.environmentIntensitynumber1environment上的环境贴图
.environmentRotationEuler(弧度)(0,0,0)使用environment的物理材质
.fogFog \| FogExp2null场景中一切被渲染对象
.isSceneboolean(只读)true类型测试标志
.overrideMaterialMaterialnull场景中所有对象(可被allowOverride豁免)

background:三种背景形态及其渲染路径

.background接受三类输入,默认null

  • 纯色(Color):定义均匀的彩色背景;
  • 普通纹理(Texture):定义平面纹理背景;
  • 立方体贴图或等距柱状投影贴图(CubeTexture / equirectangular):定义天空盒(Skybox)。

从源码看,渲染器对这三种输入走了完全不同的分支。src/renderers/webgl/WebGLBackground.js 的renderaddToRenderList函数中:

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

三个分支的实际实现:

  1. Color 分支:直接把颜色写入 GL 清屏状态(setClear( background, 1 )),不产生任何几何体,是最省开销的路径;
  2. 立方体贴图 / CubeUV 分支:懒创建一个BoxGeometry(1,1,1)的反面(BackSide)盒子,材质为BackgroundCubeMaterial(片元着色器见 src/renderers/shaders/ShaderLib/backgroundCube.glsl.js),每帧把盒子矩阵同步到相机世界位置(boxMesh.onBeforeRender),并关闭深度测试与雾(depthTest: false, fog: false),使天空盒永远贴在最远处;
  3. 普通 2D 纹理分支:懒创建一个PlaneGeometry(2,2)的平面,材质为BackgroundMaterial(着色器见 src/renderers/shaders/ShaderLib/background.glsl.js),通过uvTransformuniform 应用纹理自身的矩阵变换。

一个容易踩坑的点:两条几何体分支的材质都显式设置了allowOverride: false(src/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

背景模糊度,合法输入为01之间的浮点数。源码中它的作用点有两处:

  • 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写入 uniform(src/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实质上是"场景级环境贴图的缺省值(fallback)":MeshStandardMaterialMeshLambertMaterialMeshPhongMaterialenvMapnull时自动继承它,这省去了逐材质设置 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(即自身envMapnull)时,才会使用场景级的environmentRotation;否则仍用材质自己的envMapRotation

fog:线性雾与指数雾

.fog接受FogFogExp2实例,默认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:距离相机小于该值的物体不受雾影响(默认1);
  • far:距离相机大于该值的物体完全被雾覆盖(默认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-L144):

copy( 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):在父类序列化的基础上,额外写入fogbackgroundBlurrinessbackgroundIntensitybackgroundRotation(转为数组)、environmentIntensityenvironmentRotation。这是 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三态(纯色 / 平面纹理 / 天空盒)对应三种渲染路径,backgroundBlurriness(0~1)、backgroundIntensitybackgroundRotation(弧度)是其专属调参旋钮;
  • environment是场景级 IBL 缺省源,仅对envMapnull的 Standard/Lambert/Phong 材质生效,environmentIntensity/environmentRotation配套调节;
  • fog支持Fog(color, near=1, far=1000)线性雾与FogExp2(color, density=0.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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询