OpenGL 4.5中如何从单个cube map texture创建立方体贴图数组纹理?
如何创建并使用Cube Map Array Texture(OpenGL)
嘿,我完全懂你的困惑——Cube Map Array在OpenGL Wiki上的文档确实不够直观,我当初折腾这个的时候也查了好久,现在把完整流程和Shader端的用法给你梳理清楚:
一、创建Cube Map Array纹理(单Mipmap层级)
首先,我们需要创建一个Cube Map Array纹理对象,这里推荐用现代OpenGL的DSA(Direct State Access)函数,比传统的绑定方式更清晰:
// 假设你有numCubemaps个Cube Map,每个的分辨率是cubeSize x cubeSize GLuint cubeMapArray; glCreateTextures(GL_TEXTURE_CUBE_MAP_ARRAY, 1, &cubeMapArray); // 设置纹理参数(根据你的需求调整过滤、环绕方式) glTextureParameteri(cubeMapArray, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTextureParameteri(cubeMapArray, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTextureParameteri(cubeMapArray, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTextureParameteri(cubeMapArray, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTextureParameteri(cubeMapArray, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); // 分配纹理存储:width, height, depth(depth是Cube Map的数量 × 6,因为每个Cube有6个面) glTextureStorage3D(cubeMapArray, 1, GL_RGBA8, cubeSize, cubeSize, numCubemaps * 6);
如果你用的是传统非DSA方式,流程是绑定纹理到GL_TEXTURE_CUBE_MAP_ARRAY目标后调用glTexStorage3D,不过DSA的写法更简洁清晰。
二、将单个Cube Map数据上传到Array中
假设你已经有了存储单个Cube Map ID的数组GLuint cubeMapIDs[],这里有两种高效的方式把数据复制到Array里:
方式1:使用glCopyImageSubData(推荐,无需回读CPU)
这个函数可以直接在GPU内存间复制纹理数据,避免CPU-GPU的额外数据传输开销:
// 遍历每个Cube Map for (int arrayIdx = 0; arrayIdx < numCubemaps; arrayIdx++) { GLuint srcCubeMap = cubeMapIDs[arrayIdx]; // 遍历Cube的6个面 for (int faceIdx = 0; faceIdx < 6; faceIdx++) { GLenum faceTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + faceIdx; // 计算目标Array中的z偏移:arrayIdx * 6 + faceIdx GLint dstZOffset = arrayIdx * 6 + faceIdx; // 复制单个面的数据到Cube Map Array glCopyImageSubData( srcCubeMap, faceTarget, 0, 0, 0, 0, cubeMapArray, GL_TEXTURE_CUBE_MAP_ARRAY, 0, 0, 0, dstZOffset, cubeSize, cubeSize, 1 ); } }
方式2:读取单个Cube Map数据到CPU,再上传到Array
如果你的Cube Map数据本来就在CPU内存里(比如刚从文件加载),可以直接用glTextureSubImage3D上传:
// 假设每个Cube Map的6个面数据存储在二维数组中:unsigned char* cubeData[numCubemaps][6] for (int arrayIdx = 0; arrayIdx < numCubemaps; arrayIdx++) { for (int faceIdx = 0; faceIdx < 6; faceIdx++) { GLint dstZOffset = arrayIdx * 6 + faceIdx; glTextureSubImage3D( cubeMapArray, 0, 0, 0, dstZOffset, cubeSize, cubeSize, 1, GL_RGBA, GL_UNSIGNED_BYTE, cubeData[arrayIdx][faceIdx] ); } }
三、Shader端接收并使用Cube Map Array
在Shader中,Cube Map Array对应的采样器类型是samplerCubeArray,你需要把它声明为uniform变量,然后用特殊的采样方式:
顶点着色器(可选,传递必要参数)
#version 450 core layout(location = 0) in vec3 inPosition; layout(location = 1) in vec3 inCubeTexCoord; layout(location = 2) in int inArrayIndex; out vec3 cubeTexCoord; out float arrayIndex; void main() { gl_Position = vec4(inPosition, 1.0); cubeTexCoord = inCubeTexCoord; arrayIndex = float(inArrayIndex); }
片段着色器
#version 450 core uniform samplerCubeArray u_CubeMapArray; in vec3 cubeTexCoord; in float arrayIndex; out vec4 FragColor; void main() { // 采样Cube Map Array:前三个分量是Cube Map的方向坐标,第四个是数组索引(需为整数) FragColor = texture(u_CubeMapArray, vec4(cubeTexCoord, arrayIndex)); }
绑定纹理时,记得把Cube Map Array绑定到对应的纹理单元,再给Shader的uniform赋值:
glBindTextureUnit(0, cubeMapArray); glUniform1i(glGetUniformLocation(shaderProgram, "u_CubeMapArray"), 0);
一些注意事项
- 所有Cube Map的分辨率必须一致,否则无法放入同一个Array中
- Cube Map Array支持Mipmap,需要的话只需在
glTextureStorage3D中把第二个参数(mip层级数)设为大于1的值,再调用glGenerateTextureMipmap即可 - 若使用OpenGL 4.3以下版本,需启用扩展
GL_ARB_texture_cube_map_array,不过现在大部分设备都支持核心版本4.3+了
内容的提问来源于stack exchange,提问作者Makogan




