首页
/ three.js 中的 CubeRenderTarget:面向 WebGPU 的立方体贴图渲染目标与等距柱状图转换原理

three.js 中的 CubeRenderTarget:面向 WebGPU 的立方体贴图渲染目标与等距柱状图转换原理

2026-09-06 16:25:20作者:申梦珏Efrain

本文围绕 three.js 官方 API 文档 CubeRenderTarget 展开,系统讲解这个"WebGPU 兼容版"立方体渲染目标的构造方式、配置项、核心属性与两个关键方法 fromEquirectangularTexture()clear(),并结合 源码实现 逐行剖析等距柱状贴图转立方体贴图的完整调用链,帮助你在 WebGPURenderer 场景下正确创建、配置和使用立方体环境贴图渲染目标。

1. CubeRenderTarget 的定位与继承关系

API 文档给出的继承链为:

EventDispatcher → RenderTarget → CubeRenderTarget

官方文档对其定义是:这个类表示一个立方体渲染目标,它是 WebGLCubeRenderTarget 的一个特化版本,兼容 WebGPURenderer。换句话说:

  • 在 WebGL 渲染管线中,立方体渲染目标对应 WebGLCubeRenderTarget(由 Three.js 主入口导出);
  • CubeRenderTargetWebGPURenderer 使用的通用版本,它直接从 src/Three.WebGPU.js 入口导出,并未包含在 WebGL 主入口中。

从源码结构看,两者共享同一套设计约定:构造函数签名、六面 CubeTexture 纹理结构、以及 isRenderTargetTexture 坐标翻转标志的处理逻辑在 WebGLCubeRenderTarget.jsCubeRenderTarget.js 中几乎是同构的,只是底层渲染路径不同。

2. 构造函数:size 与 options

文档定义的构造签名为:

new CubeRenderTarget( size : number, options : RenderTarget~Options )
  • size:渲染目标的尺寸(正方形边长),默认值为 1
  • options:配置对象,即父类 RenderTargetRenderTarget~Options

源码实现中,size 被同时作为宽、高传给父类构造函数:

constructor( size = 1, options = {} ) {
    super( size, size, options );   // 宽度 = 高度 = size
    this.isCubeRenderTarget = true;
    const image = { width: size, height: size, depth: 1 };
    const images = [ image, image, image, image, image, image ];
    this.texture = new CubeTexture( images );
    this._setTextureOptions( options );
    this.texture.isRenderTargetTexture = true;
}

(见 src/renderers/common/CubeRenderTarget.js#L28-L62

关键点:内部创建了一个 6 面共享同一 image 描述的 CubeTexture。文档将 .texture 标注为 DataArrayTexture 类型,与源码注释一致——CubeTexture 在纹理继承体系中属于 DataArrayTexture 的子类,六面数据等价于深度为 6 的数组纹理布局。

2.1 options 配置项说明

options 的完整定义继承自 RenderTarget 构造函数 中的 RenderTarget~Options typedef,主要配置项与默认值如下:

配置项 类型 默认值 说明
generateMipmaps boolean false 是否生成 mipmap
magFilter number LinearFilter 放大过滤器
minFilter number LinearFilter 缩小过滤器
format number RGBAFormat 纹理格式
type number UnsignedByteType 纹理数据类型(如 HalfFloatType
internalFormat ?string null 纹理内部格式
wrapS / wrapT number ClampToEdgeWrapping UV 环绕模式
anisotropy number 1 各向异性过滤值
colorSpace string NoColorSpace 纹理色彩空间
depthBuffer boolean true 是否分配深度缓冲
stencilBuffer boolean false 是否分配模板缓冲
resolveColorBuffer boolean true 是否解析多重采样颜色缓冲(仅 MSAA 相关)
resolveDepthBuffer boolean true 是否解析深度缓冲(仅 MSAA 相关)
resolveStencilBuffer boolean true 是否解析模板缓冲(仅 MSAA 相关)
storeMultisampledColorBuffer boolean true 渲染通道结束后是否保留多重采样颜色数据
storeMultisampledDepthBuffer boolean true 渲染通道结束后是否保留多重采样深度数据
storeMultisampledStencilBuffer boolean true 渲染通道结束后是否保留多重采样模板数据
depthTexture ?Texture null 深度纹理引用
samples number 0 MSAA 采样数,0 表示禁用
count number 1 颜色附加贴图数量(MRT),至少为 1
depth number 1 纹理深度
multiview boolean false 是否用于 multiview 渲染
useArrayDepthTexture boolean false 是否将深度纹理创建为数组纹理以支持按层深度测试

其中 storeMultisampled*Buffer 一组参数值得注意:将其设为 false 可以在渲染通道结束后立即丢弃多重采样数据以节省显存带宽,适合每帧完整重绘、且输出只通过解析后纹理访问的场景(例如后处理链中的场景 pass);但若需要在不清除的情况下继续向目标渲染,或场景包含需要中途 framebuffer 拷贝的透射物体,则必须保持 true

3. 属性详解

3.1 isCubeRenderTarget

this.isCubeRenderTarget = true;   // readonly,默认 true

用于类型测试的标志位,判断逻辑与 three.js 中其他 isXXX 标志一致,例如 WebGL 侧的对应物为 isWebGLCubeRenderTarget

3.2 texture 与坐标约定

.texture 被覆写为立方体纹理类型。构造函数末尾有一个容易忽略但语义重要的赋值:

this.texture.isRenderTargetTexture = true;

源码注释解释了其存在的原因(见 src/renderers/common/CubeRenderTarget.js#L52-L60):立方体贴图沿袭了 WebGL 规范中的左手系约定(正 x 方向在看向正 z 轴时位于右侧),而 three.js 使用右手系。因此环境贴图在 three.js 中看起来像是 px 与 nx 面互换的——isRenderTargetTexture 标志正是渲染器据此做面翻转的标志。当你把该纹理作为立方体纹理直接使用(即渲染目标纹理)时,则不需要再做翻转。

4. fromEquirectangularTexture:等距柱状图到立方体贴图的转换

API 文档描述:

将给定的等距柱状(equirectangular)纹理转换为立方体贴图。

参数:renderer(渲染器)、texture(等距柱状纹理);返回:该立方体渲染目标的引用。

完整实现见 src/renderers/common/CubeRenderTarget.js#L71-L119,其执行流程可拆解为以下几步:

第一步:同步源纹理的属性。

const currentMinFilter = texture.minFilter;
const currentGenerateMipmaps = texture.generateMipmaps;

texture.generateMipmaps = true;

this.texture.type = texture.type;
this.texture.colorSpace = texture.colorSpace;
this.texture.generateMipmaps = texture.generateMipmaps;
this.texture.minFilter = texture.minFilter;
this.texture.magFilter = texture.magFilter;

目标立方体纹理会继承源纹理的数据类型、色彩空间与过滤方式,同时临时强制开启 mipmap 生成,保证转换后的立方体贴图具备完整的 mipmap 链。

第二步:构造"内部球"采样网格。

const geometry = new BoxGeometry( 5, 5, 5 );
const uvNode = equirectUV( positionWorldDirection );

const material = new NodeMaterial();
material.colorNode = TSL_Texture( texture, uvNode, 0 );
material.side = BackSide;
material.blending = NoBlending;

const mesh = new Mesh( geometry, material );
const scene = new Scene();
scene.add( mesh );

与 WebGL 版使用 ShaderMaterial + 手写 GLSL 不同,WebGPU 版直接使用 TSL(节点着色语言) 表达同样的逻辑:一个边长为 5 的盒子,材质从背面(BackSide)渲染、关闭混合(NoBlending),片元颜色通过对世界空间方向求等距柱状 UV 再采样源纹理得到。核心的 UV 计算在 EquirectUV.js 中:

const u = direction.z.atan( direction.x ).mul( 1 / ( Math.PI * 2 ) ).add( 0.5 );
const v = direction.y.clamp( - 1.0, 1.0 ).asin().mul( 1 / Math.PI ).add( 0.5 );

即经度取 atan(z, x) 归一化,纬度取 asin(y) 归一化——这正是等距柱状投影的逆映射。

第三步:规避极点模糊问题。

// Avoid blurred poles
if ( texture.minFilter === LinearMipmapLinearFilter ) texture.minFilter = LinearFilter;

等距柱状图在两极方向 UV 被强烈压缩,若使用三线性 mipmap 过滤会在极点产生明显模糊,因此转换期间临时把 LinearMipmapLinearFilter 降级为 LinearFilter

第四步:用 CubeCamera 完成六面渲染。

const camera = new CubeCamera( 1, 10, this );

const currentMRT = renderer.getMRT();
renderer.setMRT( null );

camera.update( renderer, scene );

renderer.setMRT( currentMRT );

CubeCamera 内部持有 6 台朝向 ±x/±y/±z 的 PerspectiveCamera(fov 为 -90 度),update() 会依次把内部盒子场景渲染到渲染目标的 6 个面。注意渲染前会先保存并清空当前 MRT(多渲染目标)状态,渲染完成后再恢复,避免污染外部的多目标渲染上下文。

第五步:清理与还原。

texture.minFilter = currentMinFilter;
texture.generateMipmaps = currentGenerateMipmaps;

mesh.geometry.dispose();
mesh.material.dispose();

return this;

源纹理的过滤参数被还原到调用前的值,临时盒子网格的几何与材质立即释放;方法返回 this,支持链式调用。

5. clear:清理六面缓冲

API 文档定义:

.clear( renderer : Renderer, color : boolean, depth : boolean, stencil : boolean )

三个布尔参数默认值均为 true,分别控制颜色、深度、模板缓冲是否被清除。源码实现(src/renderers/common/CubeRenderTarget.js#L129-L143)的逻辑是:

clear( renderer, color = true, depth = true, stencil = true ) {

    const currentRenderTarget = renderer.getRenderTarget();

    for ( let i = 0; i < 6; i ++ ) {

        renderer.setRenderTarget( this, i );

        renderer.clear( color, depth, stencil );

    }

    renderer.setRenderTarget( currentRenderTarget );

}

即先记住当前渲染目标,然后循环 6 次,通过 renderer.setRenderTarget( this, i ) 逐个切换到立方体的第 0~5 面并调用 renderer.clear(),最后恢复原来的渲染目标。这一"保存—切换—恢复"的模式在 three.js 的渲染目标清理逻辑中是标准做法,与 WebGLCubeRenderTarget.clear() 的实现逐行对应。

6. 实战用法示例

仓库中的官方示例 webgpu_materials_envmaps_groundprojected.html 展示了 CubeRenderTarget 的典型用法——把等距柱状环境贴图转成立方体贴图,用于地面投影天空盒(grounded skybox),注释中明确说明了动机:"use a cube map to avoid visual artifacts at the skybox's poles"(用立方体贴图避免天空盒极点处的视觉伪影):

// use a cube map to avoid visual artifacts at the skybox's poles

const size = envMap.image.height;
const cubeRenderTarget = new THREE.CubeRenderTarget( size );
cubeRenderTarget.fromEquirectangularTexture( renderer, envMap );
const cubeMap = cubeRenderTarget.texture;

const geometry = new THREE.IcosahedronGeometry( 1, 16 );
const material = new THREE.MeshBasicNodeMaterial( { side: THREE.DoubleSide } );
material.colorNode = cubeTexture( cubeMap, getGroundProjectedNormal( float( params.radius ), float( params.height ) ) );

另一个配置化用法来自光照探针网格实现 LightProbeGrid.js,展示了带 options 的构造方式——用半浮点类型存储立方体贴图并关闭 mipmap:

_cubeRenderTarget = new CubeRenderTarget( cubemapSize, { type: HalfFloatType, generateMipmaps: false } );

此外,CubeMapNode 在节点材质管线中会自动完成"等距柱状 → 立方体贴图"的转换:当检测到环境贴图的 mapping 为 EquirectangularReflectionMappingEquirectangularRefractionMapping 时,按贴图高度创建 new CubeRenderTarget( image.height ) 并调用 fromEquirectangularTexture(),结果用 WeakMap 按纹理缓存,并在源纹理 dispose 时清理缓存(见 src/nodes/utils/CubeMapNode.js#L115-L123)。

7. CubeRenderTarget 与 WebGLCubeRenderTarget 的对比

维度 CubeRenderTarget WebGLCubeRenderTarget
所属管线 WebGPURenderer WebGLRenderer
入口 src/Three.WebGPU.js src/Three.js
类型标志 isCubeRenderTarget isWebGLCubeRenderTarget
基类 RenderTarget WebGLRenderTarget
equirect 转换实现 NodeMaterial + TSL 节点(equirectUV ShaderMaterial + GLSL(equirectUv
fromEquirectangularTexture 额外行为 保存/恢复渲染器 MRT 状态 直接 camera.update( renderer, mesh )

两者在六面纹理结构、clear() 的逐面清理逻辑、isRenderTargetTexture 坐标翻转约定上完全一致,可以理解为同一设计在两条渲染路径下的实现。

8. 小结

CubeRenderTarget 是 three.js 面向 WebGPURenderer 提供的立方体渲染目标:

  1. 构造时以单一 size 参数指定正方形边长,配置项完整继承 RenderTarget~Options,涵盖过滤、格式、深度/模板缓冲、MSAA 与 MRT 等全部选项;
  2. .texture 是六面共享描述的 CubeTextureisRenderTargetTexture 标志承载了左手系立方体约定到 three.js 右手系的坐标翻转语义;
  3. fromEquirectangularTexture() 内部通过"5³ 盒子 + TSL 等距柱状采样材质 + CubeCamera 六向渲染"完成格式转换,并妥善处理了极点模糊、MRT 状态保护和临时资源清理;
  4. clear() 以"保存—逐面切换—恢复"的模式清理全部六个面的颜色/深度/模板缓冲。

当你需要在 WebGPU 场景下构建实时反射环境贴图(配合 CubeCamera 更新渲染目标)、执行等距柱状 HDR 到立方体贴图的离线转换、或为地面投影天空盒提供无极点伪影的采样源时,该类就是对应的标准工具。更多 API 细节可参考官方页面 RenderTarget 与源码 src/renderers/common/CubeRenderTarget.js

登录后查看全文
热门项目推荐
相关项目推荐