在 OpenGL ES 中混合无法像我认为的那样工作
Blending in OpenGL ES not working like I think it should
我已将我的问题简化为画一个正方形来说明。我想绘制一个纹理,它将 alpha 通道从 0 淡化到 255,然后垂直淡化到 0,以便它与背景融合。现在它使背景变暗,我不明白为什么。这是在 iOS 9.
上使用 OpenGL ES
首先,我启用我认为我想要的混合:
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
然后我用绿色填充缓冲区:
glClearColor(0, 0.5, 0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
我有这张图片用作纹理:
然后我用 GL_TRIANGLES 绘制以下顶点 ({x, y, z},{r, g, b, a},{tx, ty}):
{{0.4, 0.3, 0}, {1, 0, 0, 1}, {0, 0}},
{{0.6, 0.3, 0}, {1, 0, 0, 1}, {1, 0}},
{{0.4, 0.5, 0}, {1, 0, 0, 1}, {0, 1}},
{{0.6, 0.3, 0}, {1, 0, 0, 1}, {1, 0}},
{{0.4, 0.5, 0}, {1, 0, 0, 1}, {0, 1}},
{{0.6, 0.5, 0}, {1, 0, 0, 1}, {1, 1}},
我希望顶点的红色分量能够调制纹理,因为这是我的片段着色器:
varying lowp vec4 DestinationColor;
varying lowp vec2 TexCoordOut;
uniform sampler2D Texture;
void main(void) {
gl_FragColor = DestinationColor * texture2D(Texture, TexCoordOut);
}
确实如此,但结果不仅仅是红绿相间。边缘变暗,我希望只绘制绿色:
类似问题 here 的回答说我应该使用 glTexEnv(GL_BLEND)。但是,我认为那是 OpenGL ES 1 API.
有人知道我怎样才能达到想要的混合效果吗?我想让红色淡出绿色。顶部和底部应该是绿色的,而不是更暗。不应有明显的水平边缘。
编辑: 我正在添加更多图片来说明我的问题。这是 GIMP 中的两个粉红色点:
当我将一个移动到另一个之上时(在 GIMP 中),它们融合得很好。它们还与绿色背景很好地融合在一起:
当我在 OpenGL ES 中重新创建它时,我变暗了,这在覆盖粉红色圆点的地方尤其明显。这次我做了一个很窄的,这样你可以更好地看到效果:
我尝试了 glBlendFunc 和 glBlendFuncSeparate,结果是一样的。请注意,背景 alpha 为 1.
您的问题很可能是显示 alpha 通道本身,而不是颜色。由于您使用的是直接混合函数,因此它会将 alpha 通道设置为低于 1.0 的值,然后您会在显示中看到预乘颜色值:RGB = RGB*A
。您应该单独使用混合函数来单独处理 alpha 通道:
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE)
现在这将按照您的预期混合颜色,但保持您设置为清晰颜色的目标 alpha 值 1.0。
如果您的设备使用默认设置,假设像素数据被预乘,那么您可能会得到与您看到的结果类似的结果。我的建议是将您的 alpha 通道图像转换为简单的灰度,然后将其另存为 PNG 并验证它是灰度而不是 BGRA 格式。然后,以正常方式将每像素 8 位数据加载到纹理中,而不必担心任何预乘问题(灰度不包含和 alpha 通道)。接下来,通过从灰度测试中读取单个字节来在着色器中执行预乘步骤,然后将现有 DestinationColor 的 3 个分量乘以该线性 alpha 值。例如,这个预乘步骤让我的着色器在 iOS 上工作。
我已将我的问题简化为画一个正方形来说明。我想绘制一个纹理,它将 alpha 通道从 0 淡化到 255,然后垂直淡化到 0,以便它与背景融合。现在它使背景变暗,我不明白为什么。这是在 iOS 9.
上使用 OpenGL ES首先,我启用我认为我想要的混合:
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
然后我用绿色填充缓冲区:
glClearColor(0, 0.5, 0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
我有这张图片用作纹理:
然后我用 GL_TRIANGLES 绘制以下顶点 ({x, y, z},{r, g, b, a},{tx, ty}):
{{0.4, 0.3, 0}, {1, 0, 0, 1}, {0, 0}},
{{0.6, 0.3, 0}, {1, 0, 0, 1}, {1, 0}},
{{0.4, 0.5, 0}, {1, 0, 0, 1}, {0, 1}},
{{0.6, 0.3, 0}, {1, 0, 0, 1}, {1, 0}},
{{0.4, 0.5, 0}, {1, 0, 0, 1}, {0, 1}},
{{0.6, 0.5, 0}, {1, 0, 0, 1}, {1, 1}},
我希望顶点的红色分量能够调制纹理,因为这是我的片段着色器:
varying lowp vec4 DestinationColor;
varying lowp vec2 TexCoordOut;
uniform sampler2D Texture;
void main(void) {
gl_FragColor = DestinationColor * texture2D(Texture, TexCoordOut);
}
确实如此,但结果不仅仅是红绿相间。边缘变暗,我希望只绘制绿色:
类似问题 here 的回答说我应该使用 glTexEnv(GL_BLEND)。但是,我认为那是 OpenGL ES 1 API.
有人知道我怎样才能达到想要的混合效果吗?我想让红色淡出绿色。顶部和底部应该是绿色的,而不是更暗。不应有明显的水平边缘。
编辑: 我正在添加更多图片来说明我的问题。这是 GIMP 中的两个粉红色点:
当我将一个移动到另一个之上时(在 GIMP 中),它们融合得很好。它们还与绿色背景很好地融合在一起:
当我在 OpenGL ES 中重新创建它时,我变暗了,这在覆盖粉红色圆点的地方尤其明显。这次我做了一个很窄的,这样你可以更好地看到效果:
我尝试了 glBlendFunc 和 glBlendFuncSeparate,结果是一样的。请注意,背景 alpha 为 1.
您的问题很可能是显示 alpha 通道本身,而不是颜色。由于您使用的是直接混合函数,因此它会将 alpha 通道设置为低于 1.0 的值,然后您会在显示中看到预乘颜色值:RGB = RGB*A
。您应该单独使用混合函数来单独处理 alpha 通道:
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE)
现在这将按照您的预期混合颜色,但保持您设置为清晰颜色的目标 alpha 值 1.0。
如果您的设备使用默认设置,假设像素数据被预乘,那么您可能会得到与您看到的结果类似的结果。我的建议是将您的 alpha 通道图像转换为简单的灰度,然后将其另存为 PNG 并验证它是灰度而不是 BGRA 格式。然后,以正常方式将每像素 8 位数据加载到纹理中,而不必担心任何预乘问题(灰度不包含和 alpha 通道)。接下来,通过从灰度测试中读取单个字节来在着色器中执行预乘步骤,然后将现有 DestinationColor 的 3 个分量乘以该线性 alpha 值。例如,这个预乘步骤让我的着色器在 iOS 上工作。