three.js 中的 CubeRenderTarget:面向 WebGPU 的立方体贴图渲染目标与等距柱状图转换原理
本文围绕 three.js 官方 API 文档 CubeRenderTarget 展开,系统讲解这个"WebGPU 兼容版"立方体渲染目标的构造方式、配置项、核心属性与两个关键方法 fromEquirectangularTexture() 和 clear(),并结合 源码实现 逐行剖析等距柱状贴图转立方体贴图的完整调用链,帮助你在 WebGPURenderer 场景下正确创建、配置和使用立方体环境贴图渲染目标。
1. CubeRenderTarget 的定位与继承关系
API 文档给出的继承链为:
EventDispatcher → RenderTarget → CubeRenderTarget
官方文档对其定义是:这个类表示一个立方体渲染目标,它是 WebGLCubeRenderTarget 的一个特化版本,兼容 WebGPURenderer。换句话说:
- 在 WebGL 渲染管线中,立方体渲染目标对应 WebGLCubeRenderTarget(由 Three.js 主入口导出);
- 而
CubeRenderTarget是WebGPURenderer使用的通用版本,它直接从 src/Three.WebGPU.js 入口导出,并未包含在 WebGL 主入口中。
从源码结构看,两者共享同一套设计约定:构造函数签名、六面 CubeTexture 纹理结构、以及 isRenderTargetTexture 坐标翻转标志的处理逻辑在 WebGLCubeRenderTarget.js 与 CubeRenderTarget.js 中几乎是同构的,只是底层渲染路径不同。
2. 构造函数:size 与 options
文档定义的构造签名为:
new CubeRenderTarget( size : number, options : RenderTarget~Options )
- size:渲染目标的尺寸(正方形边长),默认值为
1; - options:配置对象,即父类
RenderTarget的RenderTarget~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 为 EquirectangularReflectionMapping 或 EquirectangularRefractionMapping 时,按贴图高度创建 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 提供的立方体渲染目标:
- 构造时以单一
size参数指定正方形边长,配置项完整继承RenderTarget~Options,涵盖过滤、格式、深度/模板缓冲、MSAA 与 MRT 等全部选项; .texture是六面共享描述的CubeTexture,isRenderTargetTexture标志承载了左手系立方体约定到 three.js 右手系的坐标翻转语义;fromEquirectangularTexture()内部通过"5³ 盒子 + TSL 等距柱状采样材质 + CubeCamera 六向渲染"完成格式转换,并妥善处理了极点模糊、MRT 状态保护和临时资源清理;clear()以"保存—逐面切换—恢复"的模式清理全部六个面的颜色/深度/模板缓冲。
当你需要在 WebGPU 场景下构建实时反射环境贴图(配合 CubeCamera 更新渲染目标)、执行等距柱状 HDR 到立方体贴图的离线转换、或为地面投影天空盒提供无极点伪影的采样源时,该类就是对应的标准工具。更多 API 细节可参考官方页面 RenderTarget 与源码 src/renderers/common/CubeRenderTarget.js。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00