【问题标题】:Strange blending when rendering self-transparent texture to the framebuffer将自透明纹理渲染到帧缓冲区时出现奇怪的混合
【发布时间】:2015-06-30 17:04:09
【问题描述】:

我正在尝试将自透明纹理渲染到帧缓冲区,但我得到的不是我所猜测的:之前在帧缓冲区上渲染的所有内容都会被忽略,并且此纹理与我清理主画布的颜色混合。

这就是我想要得到的,但不使用帧缓冲区:

package test;

import com.badlogic.gdx.*;
import com.badlogic.gdx.graphics.*;
import com.badlogic.gdx.graphics.g2d.*;

public class GdxTest extends ApplicationAdapter {
    SpriteBatch batch;
    Texture img;


    @Override
    public void create () {
        batch = new SpriteBatch();
        Pixmap pixmap = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
        pixmap.setColor(1, 1, 1, 1);
        pixmap.fillRectangle(0, 0, 1, 1);
        // Generating a simple 1x1 white texture
        img = new Texture(pixmap);
        pixmap.dispose();
    }

    @Override
    public void render () {
        Gdx.gl.glClearColor(1, 0, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        batch.begin();
        batch.setColor(1, 1, 1, 1);
        batch.draw(img, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        batch.setColor(0, 0, 0, 0.5f);
        batch.draw(img, 0, 0, 300, 300);
        batch.end();
    }
}

而且它的工作原理应该是完美的:

http://i.stack.imgur.com/wpFNg.png

这就是我使用帧缓冲区得到的结果(我不明白为什么第二个渲染的纹理不与前一个混合,因为它没有帧缓冲区):

package test;

import com.badlogic.gdx.*;
import com.badlogic.gdx.graphics.*;
import com.badlogic.gdx.graphics.g2d.*;
import com.badlogic.gdx.graphics.glutils.*;

public class GdxTest extends ApplicationAdapter {
    SpriteBatch batch;
    Texture img;

    FrameBuffer buffer;
    TextureRegion region;

    @Override
    public void create () {
        batch = new SpriteBatch();
        Pixmap pixmap = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
        pixmap.setColor(1, 1, 1, 1);
        pixmap.fillRectangle(0, 0, 1, 1);
        // Generating a simple 1x1 white texture
        img = new Texture(pixmap);
        pixmap.dispose();
        // Generating a framebuffer
        buffer = new FrameBuffer(Pixmap.Format.RGBA8888, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), false);
        region = new TextureRegion(buffer.getColorBufferTexture());
        region.flip(false, true);
    }

    @Override
    public void render () {
        // Filling with red shows the problem
        Gdx.gl.glClearColor(1, 0, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        buffer.begin();
        batch.begin();
        Gdx.gl.glClearColor(1, 1, 1, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        batch.setColor(1, 1, 1, 1);
        batch.draw(img, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        batch.setColor(0, 0, 0, 0.5f);
        batch.draw(img, 0, 0, 300, 300);
        batch.end();
        buffer.end();

        batch.begin();
        batch.setColor(1, 1, 1, 1);
        batch.draw(region, 0, 0);
        batch.end();
    }
}

还有一个不可预知的结果:

http://i.stack.imgur.com/UdDKD.png

那么我怎样才能让帧缓冲版本像第一个版本一样工作呢? ;)

【问题讨论】:

    标签: opengl opengl-es libgdx


    【解决方案1】:

    简单的答案是在渲染到屏幕时禁用混合。

    但我认为如果您想使用 FBO,最好了解为什么会发生这种情况。所以让我们来看看实际发生了什么。

    首先确保了解纹理颜色和批次颜色(顶点颜色)的作用:they are multiplied。因此,当将批处理颜色设置为0,0,0,0.5 并且纹理像素(texel)为1,1,1,1 时,这将导致值1*0,1*0,1*0,1*0.5 = 0,0,0,0.5

    接下来确保了解blending 的工作原理。默认情况下启用混合并将使用SRC_ALPHAONE_MINUS_SRC_ALPHA 函数。这意味着源值(纹素)乘以源 alpha,目标值(屏幕像素)乘以 1 减去源 alpha。因此,如果您的屏幕像素的值为1,1,1,1,而您的纹素的值为0,0,0,0.5,那么屏幕像素将设置为:(0.5*0, 0.5*0, 0.5*0, 0.5*0.5) + ((1-0.5)*1, (1-0.5)*1, (1-0.5)*1, (1-0.5)*1),即(0,0,0,0.25) + (0.5, 0.5, 0.5, 0.5) = (0.5, 0.5, 0.5, 0.75)

    那么让我们看看它如何在您的第一个代码中为您工作:

    1. 您使用1, 0, 0, 1 清除屏幕,换句话说:屏幕的每个像素都包含值1, 0, 0, 1
    2. 然后你渲染一个完整的矩形,每个像素值1,1,1,1,屏幕的每个像素现在都包含值1, 1, 1, 1
    3. 然后渲染一个较小的矩形,每个像素值 0,0,0,0.5屏幕的该部分上的每个像素现在都包含值 0.5, 0.5, 0.5, 0.75

    已经对这个问题有所了解了吗?让我们看看你的第二个代码会发生什么:

    1. 您使用1, 0, 0, 1 清除屏幕:屏幕的每个像素都包含值1, 0, 0, 1
    2. 您绑定 FBO 并使用 1, 1, 1, 1 清除它:FBO 的每个像素都包含值 1, 1, 1, 1
    3. 您向 FBO 渲染具有每个纹素值 1,1,1,1 的完整矩形:FBO 的每个像素现在都包含值 1,1,1,1
    4. 您使用每个像素值 0,0,0,0.5 渲染一个较小的矩形,FBO 的该部分上的每个像素现在都包含值 0.5, 0.5, 0.5, 0.75
    5. 然后再次将屏幕绑定为渲染目标,其每个像素仍包含值1, 0, 0, 1
    6. 最后将 FBO 纹理渲染为屏幕上的完整矩形,使这些纹理元素与屏幕像素混合。对于较小的矩形,这意味着混合 0.5, 0.5, 0.5, 0.75 乘以 0.75 和 1, 0, 0, 1 乘以 1-0.75=0.25,这将得到 0.375, 0.375, 0.375, 0.56250.25, 0, 0, 0.25。所以最终的颜色是0.625, 0.375, 0.375, 0,8125

    请务必了解此过程,否则可能会导致一些令人沮丧的奇怪问题。如果您觉得难以理解,您可以拿起笔和纸,手动计算每一步的值。

    【讨论】:

    • 很好的答案,谢谢!现在我想我已经完全熟悉所有 FBO 混合的东西了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-27
    • 2023-03-06
    • 1970-01-01
    • 1970-01-01
    • 2014-01-06
    相关资源
    最近更新 更多