我正在尝试混合从 FBO 获得的两种不同的纹理(场景和云)并将它们绘制在四边形上。
uniform sampler2D u_texture;
uniform sampler2D u_texture2;
uniform vec2 u_res;
void main(void)
{
vec2 texCoord = gl_FragCoord.xy / u_res.xy;
vec4 sceneColor = texture2D(u_texture, texCoord);
vec4 addColor = texture2D(u_texture2, texCoord);
gl_FragColor = sceneColor+addColor;
}
glBlendFunc 是
Gdx.gl20.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
我尝试了glBlendFunc
的所有组合,上面的组合是最好的。
创建 FBO:
fbClouds = new FrameBuffer(Format.RGBA8888, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true);
fbScene = new FrameBuffer(Format.RGB565, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true);
fbMix = new FrameBuffer(Format.RGB565, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true);
创建云:
fbClouds.begin();
Gdx.gl.glClearColor(0, 0, 0, 0); // to make it transparent
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
modelBatch.begin(cam);
for (Integer e : drawOrder) {
if(isVisible(cam, clouds[e])){
drawLightning(e, modelBatch);
modelBatch.render(clouds[e], cloudShader);
modelBatch.flush();
}
}
modelBatch.end();
fbClouds.end();
渲染代码:
Gdx.gl20.glDisable(GL20.GL_BLEND);
//Gdx.gl20.glEnable(GL20.GL_BLEND);
//Gdx.gl20.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
fbMix.begin();
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
mixShader.begin();
fbScene.getColorBufferTexture().bind(1);
mixShader.setUniformi("u_texture", 1);
fbClouds.getColorBufferTexture().bind(0);
mixShader.setUniformi("u_texture2", 0);
mixShader.setUniformf("u_res", Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
quad.render(mixShader, GL20.GL_TRIANGLES);
mixShader.end();
fbMix.end();
所以,我得到了意想不到的结果(云有绝对的白色,尽管它们应该是灰色的):
如果我使用 modelbatch
渲染云,结果应该是:
混合两种纹理而不丢失颜色的正确方法是什么?
最佳答案
用于将两个 FBO 绘制到屏幕上的混合函数应该是无关紧要的,因为它们不会显示任何内容,对吧?因此,在绘制 FBO 之前应该关闭混合,否则您会浪费 GPU 周期将 FBO 与透明颜色混合。
它变成白色的原因是您将灰色添加到蓝色中,而没有先使蓝色变暗。通常,当您在屏幕上绘制透明对象时,您会使用如下混合函数:GL_SRC_ALPHA、GL_ONE_MINUS_SRC_ALPHA。这意味着您将 Sprite 的颜色乘以其透明度(有效地使透明颜色变暗),并将背景乘以 Sprite 的 Alpha 的倒数(从而使将添加到 Sprite 的不透明像素的像素变暗,这样它们就不会太暗)。明亮)。
在您的情况下,您希望在片段着色器中模拟相同的东西,因为您试图在将两个纹理输出到屏幕之前在着色器中混合它们。
因此,如果您的云 FBO 有 Alpha channel ,您可以在片段着色器中执行此操作,然后就可以开始了:
void main()
{
vec2 texCoord = gl_FragCoord.xy / u_res.xy;
vec4 sceneColor = texture2D(u_texture, texCoord);
vec4 addColor = texture2D(u_texture2, texCoord);
gl_FragColor = addColor*addColor.a + sceneColor*(1-addColor.a);
}
但是,您的云的 FBO 没有 Alpha channel ,因此您需要进行一些更改。
您可以做的一件事是让您的 FBO 颜色纹理使用 RGBA4444,以便它具有 Alpha channel ,然后仔细绘制云,以便它们也写入 Alpha channel 。这会有点复杂,因为您必须使用单独的混合函数,在其中分别为 RGB 和 A channel 选择两个不同的混合函数。我以前没有这样做过。虽然这应该是可能的,但我以前从未尝试过这种方法,因为我认为 4 位颜色看起来很糟糕。
或者,如果您的云全部是单色的,您可以将 Alpha 信息编码到其中一个颜色 channel 中。为此,您需要自定义用于将云绘制到 FBO 的片段着色器。它看起来像这样:
vec4 textureColor = texture2D(u_texture, v_texCoord);
gl_FragColor = vec4(textureColor.r * textureColor.a, textureColor.a, 0, textureColor.a);
它的作用是将云的单色放入 R channel 并预乘 alpha,并将 alpha 放入 G channel 。我们想要预乘 alpha,这样我们就可以简单地将编码的云 Sprite 添加到场景中。这是因为,当您在已绘制的 Sprite 前面的已绘制 Sprite 中半透明的区域中绘制某些内容时,您希望增亮 G 编码的 Alpha,以使最终 FBO 图像中的像素更加不透明。由于我们使用预乘 Alpha,因此使用混合函数 GL_ONE、GL_ONE_MINUS_SRC_ALPHA 绘制云。
(这是一个轻微的近似值,因为混合函数的第二部分使目标的 G 编码 alpha 变暗了一点,但我查看了数学,它似乎可以接受。近似值的结果稍微更透明云。)
现在,如果您按原样将云 FBO 绘制到屏幕上,它看起来会像一堆黄色。我们只需要对上面的片段着色器进行轻微调整即可使用编码数据:
void main()
{
vec2 texCoord = gl_FragCoord.xy / u_res.xy;
vec4 sceneColor = texture2D(u_texture, texCoord);
vec4 addColor = texture2D(u_texture2, texCoord);
gl_FragColor = vec4(addColor.r*addColor.g) + sceneColor*(1-addColor.g);
}
如果您想将云着色为纯灰色以外的颜色,您可以添加统一的色调:
void main()
{
vec2 texCoord = gl_FragCoord.xy / u_res.xy;
vec4 sceneColor = texture2D(u_texture, texCoord);
vec4 addColor = texture2D(u_texture2, texCoord);
gl_FragColor = u_cloudTint*vec4(addColor.r*addColor.g) + sceneColor*(1-addColor.g);
}
关于LibGDX - 使用着色器将纹理覆盖在另一个纹理之上,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24480901/