OpenGL ES 1.1:如何在不失去亮度的情况下改变纹理颜色?

时间:2010-12-05 20:40:15

标签: iphone android opengl-es

我想要能够改变代码颜色的粒子,因此可以使用任何颜色。所以我只有一个基本上具有亮度的纹理。

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

我试过的每一个blendfunc都已经接近工作了,就像下面的最后一张图片一样。我仍然希望保留亮度,就像在中间的图片中一样。 (如果颜色图层位于纹理图层的顶部,则类似于Photoshop中的叠加或柔光滤镜。)

如果没有可编程着色器,如何做到这一点的任何想法?此外,由于这些是粒子,我不想在它后面有一个黑盒子,我想把它添加到场景中。

alt text

2 个答案:

答案 0 :(得分:9)

这是一个可能接近您正在寻找的解决方案:

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)。

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

答案 1 :(得分:2)

无义!您不必使用多纹理。只是预先计算你的alpha。

如果你在加载它之后和之前为图像预加倍alpha,那么你只需要一个纹理单元用于GL_ADD纹理环境模式。

如果你在iOS上,那么Apple的libs可以为你预乘。请参阅示例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通道会增加太多,看起来有点褪色/过亮。

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

有关更多信息,请参阅:

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

搜索“预乘alpha”