WebGL中实现立方体贴图天空盒,核心是用6张正交图像构建TEXTURE_CUBE_MAP,并通过相机不动、反向旋转视图矩阵的方式使场景“套”在静止立方体内模拟无限远天空背景。

WebGL 中实现立方体贴图天空盒,核心是用 6 张正交方向的图像(+X, -X, +Y, -Y, +Z, -Z)构建一个 TEXTURE_CUBE_MAP,再通过相机不动、只旋转视图矩阵反向的方式,让场景“套”在静止的立方体内部,从而模拟无限远的天空背景。
准备六张天空盒图像
确保你有按标准顺序命名或组织的 6 张 2D 图片,通常对应:
- right.jpg(+X,右)
- left.jpg(-X,左)
- top.jpg(+Y,上)
- bottom.jpg(-Y,下)
- front.jpg(+Z,前,朝摄像机方向)
- back.jpg(-Z,后)
注意:WebGL 要求所有 6 面纹理尺寸必须相同(如 1024×1024),且为幂次方尺寸;推荐使用 RGB 或 RGBA 格式,避免压缩格式(如 KTX2)初学时增加复杂度。
创建并加载立方体贴图
不用逐张 bind 到不同纹理单元,而是创建一个 TEXTURE_CUBE_MAP,然后对每个面调用 texImage2D,指定目标为 gl.TEXTURE_CUBE_MAP_POSITIVE_X 等常量:
立即学习“Java免费学习笔记(深入)”;
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
<p>// 对每张图调用一次(伪代码示意)
['right', 'left', 'top', 'bottom', 'front', 'back'].forEach((face, i) => {
const img = document.getElementById(<code>${face}-sky</code>);
gl.texImage2D(
gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, // 目标面
0, // mipmap 层级
gl.RGBA, // 内部格式
gl.RGBA, // 源格式
gl.UNSIGNED_BYTE,
img
);
});</p><p>// 设置采样参数(重要!立方体贴图必须用 CLAMP_TO_EDGE)
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
编写天空盒着色器(关键逻辑)
顶点着色器只需把顶点位置传给片元着色器,并用 世界空间位置(不是裁剪空间)作为采样方向:
// 顶点着色器
attribute vec3 aPosition;
uniform mat4 uView; // 注意:只传 view,不传 projection
uniform mat4 uModel; // 天空盒 model 应为单位矩阵(缩放=1,无平移)
void main() {
vec4 worldPos = uModel * vec4(aPosition, 1.0);
gl_Position = uView * worldPos; // 不乘 projection —— 天空盒无限远
}
片元着色器用归一化的世界坐标(即视线方向)直接采样立方体贴图:
// 片元着色器
precision mediump float;
uniform samplerCube uSkybox;
varying vec3 vWorldPos;
<p>void main() {
vec3 dir = normalize(vWorldPos);
gl_FragColor = textureCube(uSkybox, dir);
}
⚠️ 关键点:
– 天空盒模型应是一个中心在原点、边长为 2 的正方体(顶点范围 [-1,1]);
– 渲染时禁用深度写入(gl.depthMask(false)),但保留深度测试(gl.enable(gl.DEPTH_TEST)),避免遮挡其他物体;
– 绘制天空盒必须在所有其他物体之前(或用 glDepthFunc(gl.LEQUAL) 兼容 Z=1 边界)。
渲染时的注意事项
实际绘制前要临时调整状态:
- 关闭背面剔除(如果天空盒顶点顺序不一致,可能漏面)
- 将天空盒的 model 矩阵设为单位阵(它不随相机移动)
- view 矩阵用当前相机的 view,但 projection 矩阵不参与天空盒顶点变换
- 绘制完立即恢复深度写入:
gl.depthMask(true)
这样,无论相机怎么转,采样方向始终相对于世界坐标系,天空盒就“牢牢锁死”在场景外围,形成稳定背景。


















