将自透明纹理渲染到帧缓冲区时的奇怪混合

时间:2015-06-30 17:04:09

标签: opengl opengl-es libgdx

我试图将自我透明的纹理渲染到帧缓冲区,但我没有得到我猜到的东西:之前渲染在帧缓冲区上的所有东西都被忽略了,这个纹理与我清理过的颜色混合在一起主帆布。

这就是我想要的,但不使用framebuffers:

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

这就是我使用framebuffer得到的东西(我无法理解为什么第二个渲染的纹理不会与之前的纹理混合,因为它没有帧缓冲):

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

那么我怎么能让framebuffer版本像第一个版本那样工作呢? ;)

1 个答案:

答案 0 :(得分:5)

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

但我认为如果你想使用FBO,理解为什么会发生这种情况是很好的。因此,让我们逐步了解实际情况。

首先确保了解纹理的颜色和批次的颜色(顶点颜色)的作用:they are multiplied。因此,当将批处理颜色设置为0,0,0,0.5且纹理像素(纹素)为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) = {{1} }。

因此,让我们在您的第一个代码中了解它的工作原理:

  1. 您使用(0.5, 0.5, 0.5, 0.75)清除屏幕,换句话说:屏幕的每个像素都包含值1, 0, 0, 1
  2. 然后使用每个纹素值1, 0, 0, 1渲染一个完整的矩形,屏幕的每个像素现在都包含值1,1,1,1
  3. 然后使用每个纹素值1, 1, 1, 1渲染一个较小的矩形,屏幕该部分的每个像素 现在包含值0,0,0,0.5
  4. 已经对这个问题有所了解?让我们看看你的第二个代码会发生什么:

    1. 您使用0.5, 0.5, 0.5, 0.75清除屏幕:屏幕的每个像素都包含值1, 0, 0, 1
    2. 绑定FBO并使用1, 0, 0, 1清除它:FBO的每个像素都包含值1, 1, 1, 1
    3. 使用每个纹素值1, 1, 1, 1向FBO渲染一个完整的矩形:FBO的每个像素现在都包含值1,1,1,1
    4. 您使用每个纹素值1,1,1,1渲染一个较小的矩形,FBO 部分的每个像素现在都包含值0,0,0,0.5
    5. 然后再次将屏幕绑定为每个像素仍包含值0.5, 0.5, 0.5, 0.75的渲染目标。
    6. 最后,将FBO纹理渲染为完整的矩形到屏幕,使这些纹素与屏幕像素混合。对于较小的矩形,这意味着混合1, 0, 0, 1乘以0.75和0.5, 0.5, 0.5, 0.75乘以1-0.75 = 0.25,这将导致1, 0, 0, 10.375, 0.375, 0.375, 0.5625。所以最终的颜色是0.25, 0, 0, 0.25
    7. 确保理解这个过程,否则会引起一些令人沮丧的奇怪问题。如果您发现很难遵循,那么您可以拿笔和纸并手动计算每一步的值。