
three.js CubeMapNode 深度解析TSL 中自动完成等距柱状投影到立方体贴图的环境贴图转换【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.jsCubeMapNode是 three.js 节点系统TSLThree Shading Language中的一个实用节点用于在着色器构建阶段自动把等距柱状投影equirectangular格式的环境贴图转换为立方体贴图cube map格式并在每帧渲染时自动更新纹理引用。读完本文你可以掌握CubeMapNode的构造函数、属性与更新时机updateBeforeType理解其基于CubeRenderTarget的转换原理、WeakMap 缓存与 dispose 生命周期管理并知道它在场景背景渲染与非 PBR 材质 IBL 照明中的两处内建调用位置。一、CubeMapNode 是什么定位与继承链官方文档 docs/pages/CubeMapNode.html.md 对它的定义是This node can be used to automatically convert environment maps in the equirectangular format into the cube map format.其继承链为EventDispatcher → Node → TempNode见 src/nodes/utils/CubeMapNode.js。选择TempNode而非Node作为基类并非偶然TempNode提供了临时变量缓存机制——当同一个节点在一次构建中被多处引用时hasDependencies返回usageCount 1构建器会将其求值结果先存入一个临时变量再复用从而避免重复计算相关逻辑见 src/nodes/core/TempNode.js。CubeMapNode本身携带一个内部CubeTexture状态、在构建期切换采样纹理正符合带状态的临时值节点这一形态。为什么需要这种转换立方体贴图天然适合按方向采样TSL 中CubeTextureNode会根据贴图映射类型生成默认采样向量——反射映射CubeReflectionMapping使用reflectVector折射映射CubeRefractionMapping使用refractVector见 src/nodes/accessors/CubeTextureNode.js。而许多环境贴图如全景照片、HDR 全景天然是等距柱状投影格式。CubeMapNode填补的正是这个缺口让节点材质/场景背景可以直接消费等距柱状贴图而无需开发者手动预转立方体贴图。二、API 完整参考构造函数new CubeMapNode( envNode : Node )envNodeNode表示环境贴图的节点。从源码 src/nodes/utils/CubeMapNode.js 看构造函数执行了以下初始化以super( vec3 )声明输出类型为vec3保存this.envNode envNode创建内部状态this._cubeTexture null缓存转换后立方体贴图的引用this._cubeTextureNode cubeTexture( null )一个值为空的CubeTextureNode占位转换完成后其.value会被替换为真实贴图this._defaultTexture一个isRenderTargetTexture true的默认CubeTexture作为等距柱状贴图尚未加载完成时的兜底占位将updateBeforeType设为NodeUpdateType.RENDER。属性.envNode : Node表示环境贴图的节点对应构造参数。.updateBeforeType : string覆盖自TempNode。由于CubeMapNode需要在其updateBefore方法中每渲染一次就检查/转换一次纹理所以该属性被设为NodeUpdateType.RENDER默认值render。NodeUpdateType在 src/nodes/core/constants.js 中定义了四种更新时机值字符串含义NONEnone节点没有更新方法FRAMEframe每帧执行一次RENDERrender每次渲染前执行CubeMapNode采用此模式OBJECTobject每个使用该节点渲染的Object3D各执行一次更新与构建方法文档中.updateBeforeType的说明指向了CubeMapNode#updateBefore完整的两个关键方法如下源码updateBefore( frame ) { const { renderer, material } frame; const envNode this.envNode; if ( envNode.isTextureNode || envNode.isMaterialReferenceNode ) { // 从 TextureNode 或材质引用节点取出真实贴图 const texture ( envNode.isTextureNode ) ? envNode.value : material[ envNode.property ]; if ( texture texture.isTexture ) { const mapping texture.mapping; if ( mapping EquirectangularReflectionMapping || mapping EquirectangularRefractionMapping ) { // 等距柱状贴图查缓存 → 未命中则用 CubeRenderTarget 转换并缓存 // 贴图尚未加载完成时回退到 _defaultTexture 占位 } else { // envNode 本身已是立方体贴图直接透传 this._cubeTextureNode this.envNode; } } } } setup( builder ) { this.updateBefore( builder ); return this._cubeTextureNode; }setup是节点参与着色器构建的入口它先触发一次updateBefore此时拿到当前帧的renderer与material然后把内部_cubeTextureNode作为自身输出交给构建器。这也解释了updateBefore与setup双通道设计——前者由渲染循环按render时机周期性调用后者保证构建阶段也能拿到最新转换结果。源代码实现位于 src/nodes/utils/CubeMapNode.js并通过 src/nodes/Nodes.js 导出CubeMapNode类。三、核心转换流程updateBefore 逐段解析updateBefore的完整决策路径源码 L82-L151可以分为四层1. 节点类型过滤只处理envNode.isTextureNodeTextureNode或envNode.isMaterialReferenceNode材质属性引用节点两种输入并从对应位置取出真实Texture前者直接取.value后者按material[ envNode.property ]读取材质属性。这保证了cubeMapNode既能接texture( envMap )也能接材质引用节点。2. 映射类型判断只有当贴图mapping为EquirectangularReflectionMapping或EquirectangularRefractionMapping时才执行转换否则说明输入本身已经是立方体贴图直接把envNode透传为输出this._cubeTextureNode this.envNode零开销。3. 等距柱状贴图转换带缓存先查模块级WeakMap缓存_cache命中则复用已有立方体贴图并调用mapTextureMapping校正映射标记未命中时检查isEquirectangularMapReady( image )私有函数判定条件仅为image ! null image.height 0L173-L179。就绪则const renderTarget new CubeRenderTarget( image.height ); renderTarget.fromEquirectangularTexture( renderer, texture ); mapTextureMapping( renderTarget.texture, texture.mapping ); this._cubeTexture renderTarget.texture; _cache.set( texture, renderTarget.texture ); texture.addEventListener( dispose, onTextureDispose );目标立方体贴图的边长取原图等距柱状图的高度等距柱状贴图宽高比一般为 2:1取高度正好得到立方体单面尺寸未就绪异步贴图尚未加载完时回退到_defaultTexture占位下一帧渲染时updateBefore会再次检查加载完成后自动切换为真实转换结果——这是该节点对异步加载友好的关键设计。4. 映射标记校正mapTextureMappingL215-L227保证生成的立方体贴图携带与来源一致的语义原映射生成的立方体映射EquirectangularReflectionMappingCubeReflectionMappingEquirectangularRefractionMappingCubeRefractionMapping这一点很关键因为下游CubeTextureNode.getDefaultUV()正是依据CubeReflectionMapping/CubeRefractionMapping决定采样使用反射向量还是折射向量映射标记错会导致采样方向错误。四、底层转换实现CubeRenderTarget.fromEquirectangularTexture真正画图的工作由 src/renderers/common/CubeRenderTarget.js 完成。它是一个兼容WebGPURenderer的RenderTarget子类构造时设置texture.isRenderTargetTexture true以对齐坐标系约定。fromEquirectangularTexture( renderer, texture )的转换手法源码 L71 起保存并临时修改原贴图参数generateMipmaps true并继承type、colorSpace、minFilter、magFilter构造一个BoxGeometry( 5, 5, 5 )的立方体盒子材质为NodeMaterial其颜色节点为material.colorNode TSL_Texture( texture, equirectUV( positionWorldDirection ), 0 );即以盒内表面每一点的世界空间方向计算等距柱状 UVequirectUV( positionWorldDirection )采样原始等距柱状贴图side BackSide、blending NoBlending用CubeCamera( 1, 10, renderTarget )从中心朝六个面各渲染一次把球面全景包裹成六面立方贴图细节优化若原贴图minFilter LinearMipmapLinearFilter转换期间临时改为LinearFilter注释说明是为了避免两极区域模糊Avoid blurred poles转换前保存并临时清空渲染器 MRT 状态renderer.setMRT( null )转换后恢复结束后还原原贴图的minFilter与generateMipmaps并 dispose 临时盒子与材质。由于转换走的是渲染器自身的 TSL 材质路径因此该机制同时适用于 WebGL 与 WebGPU 渲染后端。五、缓存策略与资源生命周期CubeMapNode用模块级WeakMapconst _cache new WeakMap()L9以原始等距柱状纹理为键缓存转换出的CubeRenderTarget.texture同一贴图被多个CubeMapNode多个材质/多个场景使用时只转换一次转换后向原贴图注册dispose监听触发onTextureDisposeL189-L205先摘除监听再从缓存中删除条目并调用renderTarget.dispose()确保派生的立方体渲染目标随源贴图一起被释放不留显存泄漏使用WeakMap意味着源纹理被 GC 回收后缓存条目自动失效无需手动清理。六、TSL 函数式用法与内建调用链导出与 TSL 函数文件末尾导出了 TSL 包装函数L229-L237export const cubeMapNode /*__PURE__*/ nodeProxy( CubeMapNode ).setParameterLength( 1 );nodeProxy使cubeMapNode( envNode )可直接在 TSL 表达式中使用参数个数固定为 1。对应的类型化类CubeMapNode则从 src/nodes/Nodes.js 导出便于高级场景直接new。典型用法import * as THREE from three/webgpu; import { texture, cubeMapNode } from three/tsl; // 加载等距柱状环境贴图并声明映射类型 const envMap new THREE.TextureLoader().load( equirectangular_env.jpg ); envMap.mapping THREE.EquirectangularReflectionMapping; // 包一层 TextureNode 再交给 cubeMapNode // 渲染时若贴图未加载完成会得到占位贴图加载完成后自动切换为转换结果 const cubeEnv cubeMapNode( texture( envMap ) );注意两个前提传入的必须是TextureNodetexture( ... )或材质引用节点其它节点类型会被updateBefore的类型检查过滤掉贴图mapping必须是两种等距柱状映射之一才会触发转换传入已是CubeReflectionMapping的CubeTexture时节点直接透传。仓库内建的两处调用从源码结构看cubeMapNode在仓库中被两个核心路径内建复用说明它是节点渲染管线的标准件场景背景src/renderers/common/nodes/NodeManager.js 中处理scene.background时若背景是等距柱状映射的贴图且backgroundBlurriness 0则构造cubeMapNode( envMap )作为背景节点而backgroundBlurriness 0或CubeUVReflectionMapping时改走pmremTexture路径。也就是说在节点渲染器中把一张等距柱状贴图直接赋给scene.background立方体转换由CubeMapNode在背后自动完成非 PBR 材质的 IBL 照明src/nodes/lighting/BasicEnvironmentNode.js 的setup中执行builder.context.environment cubeMapNode( this.envNode )为MeshBasicNodeMaterial、MeshPhongNodeMaterial等非 PBR 节点材质提供基于图像的环境光照环境贴图同样可以是等距柱状格式。七、小结与适用前提CubeMapNode的契约很简单输入一个等距柱状环境贴图节点输出一个可被方向向量采样的立方体贴图节点转换时机绑定在render更新阶段updateBeforeType render实现上依赖三块基础设施CubeRenderTarget.fromEquirectangularTexture的六面重投影、模块级WeakMap缓存、以及源贴图dispose事件驱动的资源回收对异步加载的贴图节点会先以isRenderTargetTexture true的默认占位贴图渲染加载完成后自动切换无需外部轮询或回调适用前提该节点属于 TSL/节点系统经由节点材质或节点渲染器路径src/renderers/common/nodes/下的NodeManager等生效它不修改原始贴图对象转换产物是独立的CubeRenderTarget。如需进一步阅读可参考 API 页面 docs/pages/CubeMapNode.html、基类 TempNode 的文档以及实现文件 src/nodes/utils/CubeMapNode.js 与 src/renderers/common/CubeRenderTarget.js。【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考