在OpenGL中绘制纹理时忽略其alpha通道

3

我已经将一张带有不同透明度的RGBA格式纹理加载到内存中。

图片的加载方式如下:

 GLuint texture = 0;
 glGenTextures(1, &texture);
 glBindTexture(GL_TEXTURE_2D, texture);
 self.texNum = texture;

 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR); 
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR); 

 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, self.imageWidth, self.imageHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, [self.imageData bytes]);

我想知道如何绘制这个纹理,使得图像中的 alpha 通道被视为全为1,并且该纹理像 RGB 图像一样绘制。
考虑基础图像: http://www.ldeo.columbia.edu/~jcoplan/alpha/base.png
这个图像从0到255的alpha值呈现出渐变效果,而RGB值始终为255,0,0。
然而,如果我禁用混合将其绘制出来,我会得到一个像这样的图像: www.ldeo.columbia.edu/~jcoplan/alpha/no_alpha.png
而我真正想要的是这样的图像: www.ldeo.columbia.edu/~jcoplan/alpha/correct.png
我很感激有关忽略 alpha 通道的指点。请注意,我不能仅最初将图像加载为 RGB,因为我在其他时间需要 alpha 通道。
编辑:我尝试使用 GL_COMBINE 来解决我的问题,如下所示:
glColorf(1,1,1,1);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);

glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); 
[self drawTexture];

但是仍然没有运气,它仍然将黑色绘制为红色。

建议的两种解决方案都看起来是正确的。在调试器的内存视图中查看[self.imageData bytes]返回的指针。这些值是否符合您的预期?您得到的结果表明它们可能不会。 - please delete me
1
你是否使用了预乘alpha?如果是的话,那么淡出到黑色就是你的纹理RGB值所包含的内容。你的glBlendFunc是什么? - Alan
在查看字节后,这是正确的,图像通过XCode优化进行了预乘,内部字节不符合我的预期。所以答案是我没有正确地加载图像。谢谢! - jcoplan
4个回答

3
我有一个以RGBA格式加载到内存的纹理,其中包含各种alpha值。
禁用混合(glDisable(GL_BLEND))后,如果我使用该纹理进行绘制,我会得到一个看起来像这样的图像:www.ldeo.columbia.edu/~jcoplan/alpha/no_alpha.png
这是因为在您的源图像中,所有透明像素都是黑色。这是您的纹理/图像的问题,或者可能是加载程序的问题,但这不是OpenGL的问题。
您可以尝试使用glTexEnv(GL_COMBINE...)(即根据alpha通道将纹理颜色与底层颜色混合)来修复它,但由于我没有做过这样的事情,所以我不完全确定,并且无法给出确切的操作数。在Direct3D9中可以使用D3DTOP_MODULATEALPHA_ADDCOLOR实现这一点,因此很可能在OpenGL中也有方法。

我不太明白你所说的所有透明像素都是黑色的意思。我尝试了GL_COMBINE,但没有成功,它仍然将黑色绘制为红色... - jcoplan
@jcoplan:透明度只是另一种通道。如果像素具有R255、G0、B0、A255,则为不透明的红色。如果它具有R255、G0、B0、A0,则为完全透明的红色。如果它具有R0、G0、B0、A0,则为完全透明的黑色。在您的纹理中,颜色是一个渐变,从透明的黑色(R0,G0,B0,A0)到不透明的红色(R255G0B0A255)线性插值。这就是为什么您会看到黑色部分。如果您想要另一个结果,您需要另一张图片。尽管如此,我建议使用两个不同的图像。当渐变同时存在于A和RGB中时,可能在白色背景上看起来不太好。 - SigTerm
你说得对,我没有正确地加载图片。我遇到了意外的 alpha 预乘问题。 - jcoplan

1

您不应该禁用混合,而是使用适当的参数glBlendFunc

glBlendFunc(GL_ONE, GL_ZERO);

我想尝试一下,但图像仍然看起来像这样: www.ldeo.columbia.edu/~jcoplan/alpha/no_alpha.png - jcoplan

0

或者你可以告诉OpenGL仅上传图像的RGB通道,使用:

glPixelStorei(GL_UNPACK_ALIGNMENT, 4)

在调用 glTexImage2D 并将格式设置为 GL_RGB 之前,需要注意它会跳过每个像素的第四个字节,即 alpha 通道。

GL_UNPACK_ALIGNMENT 影响的是 的对齐方式,而不是单个像素。请参见 http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml。 - Václav Slavík

0

我遇到了类似的问题,并发现原因是因为iOS图像加载正在对RBG值进行预乘(正如其他答案和评论中所讨论的那样)。我想知道是否有一种禁用预乘的方法,但在此期间,我正在使用从this threadthis thread派生的代码“反预乘”。

    // un pre-multiply
    uint8_t *imageBytes = (uint8_t *)imageData ;
    int byteCount = width*height*4 ;
    for (int i=0; i < byteCount; i+= 4) {
        uint8_t a = imageBytes[i+3] ;
        if (a!=255 && a!=0 ){
            float alphaFactor = 255.0/a ;
            imageBytes[i] *= alphaFactor ; 
            imageBytes[i+1] *= alphaFactor ; 
            imageBytes[i+2] *= alphaFactor ; 
        }
    }

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