【问题标题】:OpenGL ES 1.1: How to change texture color without losing luminance?OpenGL ES 1.1:如何在不损失亮度的情况下更改纹理颜色?
【发布时间】:2011-03-21 02:24:45
【问题描述】:

我希望能够在代码中更改颜色的粒子,因此可以使用任何颜色。所以我只有一个基本上有亮度的纹理。

我一直在使用glColor4f(1f, 0f, 0f, 1f); 来应用颜色。

我尝试过的每一个接近工作的 blendfunc 最终都会像下面的最后一张图片一样。我仍然想保持亮度,就像中间的图片一样。 (这就像 Photoshop 中的叠加或柔光滤镜,如果颜色层位于纹理层之上。)

关于如何在没有可编程着色器的情况下执行此操作的任何想法?另外,由于这些是粒子,我不希望它后面有一个黑盒子,我希望它添加到场景中。

【问题讨论】:

  • 我不相信固定管道可以实现,你需要一个片段着色器来实现它。

标签: iphone android opengl-es


【解决方案1】:

这里有一个可能与您正在寻找的解决方案相近的解决方案:

glColor4f(1.0f, 0.0f, 0.0f, 1.0f);

glActiveTexture( GL_TEXTURE0 );
glEnable( GL_TEXTURE_2D );
glBindTexture(GL_TEXTURE_2D, spriteTexture);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

glActiveTexture( GL_TEXTURE1 );
glEnable( GL_TEXTURE_2D );
glBindTexture(GL_TEXTURE_2D, spriteTexture);    
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD );

它所做的是将原始纹理乘以指定的颜色,然后将原始纹理的像素值添加到顶部:

final_color.rgba = original_color.rgba * color.rgba + original_color.rgba;

这将产生比您要求的更亮的图像,但通过一些调整可能就足够了。

如果您想保留纹理的 alpha 值,则需要使用 GL_COMBINE 而不是 GL_ADD(+ 正确设置 GL_COMBINE_RGB 和 GL_COMBINE_ALPHA)。

以下是在纹理上使用此技术的一些结果。

【讨论】:

  • 顺便说一句,不要忘记为纹理单元 1 设置纹理坐标指针:glClientActiveTexture(GL_TEXTURE1); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glTexCoordPointer(2, GL_SHORT, 0, spriteTexcoords);
  • 谢谢!这看起来不错。不幸的是,我在 Android 上尝试这个时发现,Android 仅部分支持 1.1,并且多纹理已经过时了。但看起来这是唯一可行的方法。
【解决方案2】:

废话!您不必使用多重纹理。只需预乘你的 alpha。

如果您在加载图像之后和为其创建 GL 纹理之前在图像上预乘 alpha,那么您只需要一个纹理单元用于 GL_ADD 纹理环境模式。

如果您使用的是 iOS,那么 Apple 的库可以为您预乘。查看示例 Texture2D 类并查找 kCGImageAlphaPremultipliedLast 标志。

如果您没有使用支持预乘的图像加载器,那么您必须在加载图像后手动执行此操作。伪代码:

uint8* LoadRGBAImage(const char* pImageFileName) {
    Image* pImage = LoadImageData(pImageFileName);
    if (pImage->eFormat != FORMAT_RGBA)
        return NULL;

    // allocate a buffer to store the pre-multiply result
    // NOTE that in a real scenario you'll want to pad pDstData to a power-of-2
    uint8* pDstData = (uint8*)malloc(pImage->rows * pImage->cols * 4);
    uint8* pSrcData = pImage->pBitmapBytes;
    uint32 bytesPerRow = pImage->cols * 4;

    for (uint32 y = 0; y < pImage->rows; ++y) {
        byte* pSrc = pSrcData + y * bytesPerRow;
        byte* pDst = pDstData + y * bytesPerRow;
        for (uint32 x = 0; x < pImage->cols; ++x) {
            // modulate src rgb channels with alpha channel
            // store result in dst rgb channels
            uint8 srcAlpha = pSrc[3];
            *pDst++ = Modulate(*pSrc++, srcAlpha);
            *pDst++ = Modulate(*pSrc++, srcAlpha);
            *pDst++ = Modulate(*pSrc++, srcAlpha);
            // copy src alpha channel directly to dst alpha channel
            *pDst++ = *pSrc++;
        }
    }

    // don't forget to free() the pointer!
    return pDstData;
}

uint8 Modulate(uint8 u, uint8 uControl) {
    // fixed-point multiply the value u with uControl and return the result
    return ((uint16)u * ((uint16)uControl + 1)) >> 8;
}

就个人而言,我正在使用 libpng 并手动进行预乘。

无论如何,在预乘之后,只需将字节数据绑定为 RGBA OpenGL 纹理即可。使用 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);之后你只需要一个纹理单元就可以了。您应该完全(或非常接近)您想要的。您可能必须使用 glBlendFunc(GL_SRC_ALPHA, GL_ONE);顺便说一句,如果你真的想让它看起来有光泽。

这与 Ozirus 方法略有不同。他从不通过预乘来“减少”纹理的 RGB 值,因此 RGB 通道被添加太多,看起来有点褪色/过亮。

我认为 premultiply 方法更类似于 Overlay,而 Ozirus 方法是 Soft Light。

更多信息,请参见:

http://en.wikipedia.org/wiki/Alpha_compositing

搜索“预乘 alpha”

【讨论】:

    猜你喜欢
    • 2012-04-04
    • 2011-12-25
    • 1970-01-01
    • 1970-01-01
    • 2020-05-04
    • 2012-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多