Opengl:使用单通道纹理作为alpha通道以显示文本

5
我要做的是从单通道数据数组中将纹理加载到硬件中,并使用其alpha通道在对象上绘制文本。我正在使用OpenGL 4。
如果我尝试使用四通道RGBA纹理进行此操作,则完全正常,但出于某种原因,当我尝试仅加载单个通道时,我会得到一个混乱的图像,而我无法弄清原因。 我通过以下代码将一系列字形的纹理位图数据结合成单个纹理来创建纹理:
int texture_height = max_height * new_font->num_glyphs;
int texture_width = max_width;

new_texture->datasize = texture_width * texture_height;
unsigned char* full_texture = new unsigned char[new_texture->datasize];

// prefill texture as transparent
for (unsigned int j = 0; j < new_texture->datasize; j++)
    full_texture[j] = 0;

for (unsigned int i = 0; i < glyph_textures.size(); i++) {
    // set height offset for glyph
    new_font->glyphs[i].height_offset = max_height * i;
    for (unsigned int j = 0; j < new_font->glyphs[i].height; j++) {
        int full_disp = (new_font->glyphs[i].height_offset + j) * texture_width;
        int bit_disp = j * new_font->glyphs[i].width;
        for (unsigned int k = 0; k < new_font->glyphs[i].width; k++) {
            full_texture[(full_disp + k)] =
                    glyph_textures[i][bit_disp + k];
        }
    }
}

然后我调用以下代码来加载纹理数据:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->x, texture->y, 0, GL_RED, GL_UNSIGNED_BYTE, reinterpret_cast<void*>(full_texture));

我的片段着色器执行以下代码:

#version 330

uniform sampler2D texture;

in vec2 texcoord;
in vec4 pass_colour;

out vec4 out_colour;

void main()
{
float temp = texture2D(texture, texcoord).r;
out_colour = vec4(pass_colour[0], pass_colour[1], pass_colour[2], temp);
}

我得到了一张图像,可以看出它是由纹理生成的,但是它非常扭曲,我不确定原因。顺便说一句,我使用GL_RED,因为Opengl 4中删除了GL_ALPHA。

真正让我困惑的是,当我从字形生成一个4 RGBA纹理,然后使用它的alpha通道时,为什么它可以正常工作?


最终,我通过在调用glTexImage2D之前将数据转换为float来使其正常工作。我相信我需要使用一个是4字节倍数的数据格式。这就是为什么RGBA到RGBA可以工作(即使它是4倍字节)以及为什么浮点数据也可以工作的原因。似乎除非输入和输出都是4通道数据,否则我无法使用GL_UNSIGNED_BYTE。也许这是ATI驱动程序中的一个错误? - Kacper Kuryllo
2
好的,我已经解决了。我的代码没有问题,驱动程序也没有错误。GL_UNPACK_ALINGMENT需要设置为1才能正常工作。默认情况下它被设置为4。运行“glPixelStorei(GL_UNPACK_ALIGNMENT, 1);”解决了我所有的问题。我不确定为什么它被设置为4,可能是出于性能考虑。我会进行调查。 - Kacper Kuryllo
2个回答

15
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->x, texture->y, 0, GL_RED, GL_UNSIGNED_BYTE, reinterpret_cast<void*>(full_texture));

这在技术上是合法的,但绝不是一个好主意。

首先,你需要了解glTexImage2D的第三个参数是什么。它是纹理的实际图像格式。你不是创建一个只有一个通道的纹理;你是创建一个有四个通道的纹理。

接下来,你需要了解最后三个参数的作用。它们是像素传输参数;它们描述了你要提供给 OpenGL 的像素数据的样子。

这个命令的意思是,“创建一个4通道的纹理,然后将一些数据上传到仅仅红色通道中。这些数据被存储为无符号字节数组。”上传数据到纹理的部分通道在技术上是合法的,但几乎从来都不是一个好主意。如果你正在创建一个单通道纹理,你应该使用单通道纹理。也就是说,使用适当的图像格式。

接下来,情况变得更加混乱:

new_texture->datasize = texture_width * texture_height*4;

你使用 "*4" 的做法,强烈暗示了你正在创建四通道的像素数据。 但是你只上传了单通道的数据。 其余的计算与此相符; 你似乎从未填充任何数据通过 full_texture[texture_width * texture_height]。 因此,你可能分配了比你需要的更多的内存。

最后一件事: 一定要使用具有大小的内部格式。 永远不要只使用 GL_RGBA; 使用GL_RGBA8GL_RGBA4或其他格式。 别让驱动程序自行选择并希望它会给你好的结果。

所以,正确的上传方式是:

glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, texture->x, texture->y, 0, GL_RED, GL_UNSIGNED_BYTE, full_texture);

提醒:在C++中,即使不使用reinterpret_cast,指针也可以隐式地转换为void*


嗨,感谢提供的信息。4 是我在切换加载 4 个通道和 1 个通道时打错的。我发布时搞砸了,我会编辑帖子。我已经尝试了您所有的建议,但不幸的是我仍然得到相同的效果。 - Kacper Kuryllo
当我从4通道数据创建4通道纹理时,我得到了正确的效果,但如果我尝试从4通道数据创建1通道纹理,它仍然会出现问题,这非常奇怪!至少这告诉我问题不在输入数据上,而必须在下游,可能是在从一种数据类型转换为另一种数据类型或者我的着色器代码中。 - Kacper Kuryllo

4

我认为您交换了glTexImage2d()函数中“内部格式”和“格式”参数的位置。也就是说,您告诉它您想要纹理对象中的RGBA格式,但文件数据中只有红色而不是反过来。

请尝试使用以下调用替换:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, texture->x, texture->y, 0, GL_RGBA, GL_UNSIGNED_BYTE, reinterpret_cast<void*>(full_texture));

我的文件数据是红色的,内部数据是RGBA格式的。如果我交换它们,情况会更糟。我还尝试将内部文件数据都设置为红色,但显示仍然相同...我甚至尝试了接收两个通道,但也不起作用... - Kacper Kuryllo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接