如何正确处理行像素宽度为奇数的NPOT 3D LUT纹理对齐问题?
我在使用.cube格式的LUT文件做图像后期处理,.cube文件的布局示例如下:
TITLE LUT_3D_SIZE 2 1.000000 1.000000 1.000000 -> takes black pix. from input image and transfers to white output pix 1.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 1.000000 1.000000 1.000000 0.000000 1.000000 0.000000 1.000000 0.000000 1.000000 1.000000 0.000000 0.000000 0.000000 -> takes white pix. from input image and transfers to black output pix.
我已将文件原始数据正确加载到数据向量中,随后将其加载到GL_TEXTURE_3D,并在片段着色器中作为sampler3D使用,通过GLSL的texture(...)函数实现插值。加载纹理的代码如下:
glGenTextures(1, &texID); glBindTexture(GL_TEXTURE_3D, texID); constexpr int MIPMAP_LEVEL = 0; // I do not need any mipmapping for now. //glPixelStorei(GL_UNPACK_ALIGNMENT, 1); -> should I use this??? glTexImage3D( GL_TEXTURE_3D, MIPMAP_LEVEL, GL_RGB, size.x, // Size of the LUT 2, 33 etc., x == y == z. size.y, size.z, 0, GL_RGB, GL_FLOAT, // I assume this is correct regarding how are stored the data in the LUT .cube file. data ); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAX_LEVEL, MIPMAP_LEVEL); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); glBindTexture(GL_TEXTURE_3D, 0);
输入图像仅考虑RGB8或RGB16格式,当LUT_3D_SIZE为2(或任何2的幂尺寸)时流程正常,但当LUT尺寸为非2的幂(比如33这类奇数)时,输出结果异常,推测是未对齐的纹理填充了随机数据导致。我需要解决这个问题并理解背后的原理,而非盲目尝试参数。
补充:使用桌面版OpenGL 3.3+,NVIDIA显卡。
1. 强制像素数据无对齐读取
在调用glTexImage3D之前添加如下代码:
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
原理:
GL_UNPACK_ALIGNMENT是OpenGL控制像素数据内存读取规则的参数,默认值为4——意味着OpenGL会假设每一行像素数据的起始地址是4字节的倍数,且每行字节数会被向上取整到4的倍数。
虽然33x33x33的RGB浮点纹理每行字节数(3334=396)是4的倍数,但部分驱动对非2的幂(NPOT)纹理的内存处理可能存在特殊逻辑,或者数据向量在内存中的起始/行地址未严格符合4字节对齐要求,导致OpenGL读取到超出有效数据范围的内存(随机值)。设置GL_UNPACK_ALIGNMENT为1后,OpenGL会完全按照提供的连续数据读取,不做任何对齐填充或地址偏移,避免错位读取的问题。
2. 使用明确的内部纹理格式
将glTexImage3D的内部格式从GL_RGB改为GL_RGB32F,明确指定存储3个32位浮点分量:
glTexImage3D( GL_TEXTURE_3D, MIPMAP_LEVEL, GL_RGB32F, // 明确指定32位浮点RGB格式 size.x, size.y, size.z, 0, GL_RGB, GL_FLOAT, data );
原理:
GL_RGB是通用格式,不同驱动可能会将其映射到不同的内部存储方式(比如自动添加alpha通道或压缩存储)。使用GL_RGB32F可以确保纹理内部存储与加载的浮点数据完全匹配,避免格式转换带来的潜在问题。
3. 确认LUT数据顺序匹配要求
.cube格式的3D LUT数据顺序为:B通道(z轴)→ G通道(y轴)→ R通道(x轴),即先遍历所有B值,每个B值下遍历所有G值,每个G值下遍历所有R值。OpenGL的3D纹理数据顺序是x(width,对应R)→ y(height,对应G)→ z(depth,对应B),两者顺序一致,当前加载逻辑没问题,但后续更换LUT文件时需确保数据顺序未颠倒。
内容的提问来源于stack exchange,提问作者CJ_Notned

