You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

火山引擎 最新活动