将自透明纹理渲染到帧缓冲区时发生奇怪的融合 [英] Strange blending when rendering self-transparent texture to the framebuffer

查看:118
本文介绍了将自透明纹理渲染到帧缓冲区时发生奇怪的融合的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to render self-transparent textures to the framebuffer, but I'm getting not what I guessed: everything previously rendered on the framebuffer gets ignored, and this texture blends with the colour I cleaned my main canvas.

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

That's what I would like to get, but without using 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的结果(我不明白为什么第二个渲染纹理不与前一个融合,就像没有framebuffer一样):

And that's what I get with using of framebuffer (I can't understand why the second rendered texture doesn't blend with the previous one, as it do without 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

那么如何使帧缓冲版本像第一个版本那样工作? ;)

So how could I make the framebuffer version work the way the first version does? ;)

推荐答案

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

The easy answer is to disable blending when rendering to the screen.

但是,我想很好地理解为什么要使用FBO会发生这种情况.因此,让我们逐步了解一下实际发生的情况.

But I think it is good to understand why this is happening if you want to use FBO. So let's walk through what's actually going on.

首先请确保了解纹理的颜色和批处理的颜色(顶点颜色)的作用:

First make sure to understand what the color of the texture and the color of the batch (the vertex color) does: they are multiplied. So when setting the batch color to 0,0,0,0.5 and the texture pixel (texel) is 1,1,1,1 this will result in a value of 1*0,1*0,1*0,1*0.5 = 0,0,0,0.5.

接下来,请确保了解混合的工作原理.默认情况下启用混合,并且将使用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).

Next make sure to understand how blending works. Blending is enabled by default and will use the SRC_ALPHA and ONE_MINUS_SRC_ALPHA functions. This means that the source value (the texel) is multiplied by the source alpha and that the destination value (the screen pixel) is multiplied by one minus the source alpha. So if your screen pixel has the value 1,1,1,1 and your texel has the value 0,0,0,0.5 then the screen pixel will be set to:(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) which is (0,0,0,0.25) + (0.5, 0.5, 0.5, 0.5) = (0.5, 0.5, 0.5, 0.75).

因此,让我们在您的第一个代码中看看它如何工作:

So let's see how that works for you in your first code:

  1. 1, 0, 0, 1清除屏幕,换句话说:屏幕的每个像素都包含值1, 0, 0, 1.
  2. 然后用每个纹理元素值1,1,1,1渲染一个完整的矩形,屏幕上的每个像素现在都包含值1, 1, 1, 1.
  3. 然后您渲染一个较小的矩形,每个texel值0,0,0,0.5,屏幕那部分的每个像素 现在都包含值0.5, 0.5, 0.5, 0.75.
  1. You clear the screen with 1, 0, 0, 1, in other words: every pixel of the screen contains the value 1, 0, 0, 1.
  2. Then you render a full rectangle with each texel value 1,1,1,1, every pixel of the screen now contains the value 1, 1, 1, 1.
  3. Then you render a smaller rectangle with each texel value 0,0,0,0.5, every pixel on that part of the screen now contains the value 0.5, 0.5, 0.5, 0.75.

已经对这个问题有感觉吗?让我们看看第二个代码中发生了什么:

Got a feeling about the issue already? Let's see what happens in your second code:

  1. 1, 0, 0, 1清除屏幕:屏幕的每个像素都包含值1, 0, 0, 1.
  2. 您绑定FBO并用1, 1, 1, 1清除它:FBO的每个像素都包含值1, 1, 1, 1.
  3. 您向FBO渲染每个texel值1,1,1,1的完整矩形:FBO的每个像素现在都包含值1,1,1,1.
  4. 您使用每个texel值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
  1. You clear the screen with 1, 0, 0, 1: every pixel of the screen contains the value 1, 0, 0, 1.
  2. You bind the FBO and clear it with 1, 1, 1, 1: every pixel of the FBO contains the value 1, 1, 1, 1.
  3. You render a full rectangle with each texel value 1,1,1,1 to the FBO: every pixel of the FBO now contains the value 1,1,1,1.
  4. You render a smaller rectangle with each texel value 0,0,0,0.5, every pixel on that part of the FBO now contains the value 0.5, 0.5, 0.5, 0.75.
  5. Then you bind the screen again as the render target of which each pixel still contains the value 1, 0, 0, 1.
  6. Finally you render the FBO texture as full rectangle to the screen, causing these texels to be blended with the screen pixels. For the smaller rectangle this means blending 0.5, 0.5, 0.5, 0.75 multiplied by 0.75 and 1, 0, 0, 1 multiplied by 1-0.75=0.25, which will result in 0.375, 0.375, 0.375, 0.5625 and 0.25, 0, 0, 0.25. So the final color is 0.625, 0.375, 0.375, 0,8125

请务必了解此过程,否则会引起一些令人沮丧的怪异问题.如果您发现难以遵循,则可以拿笔和纸,手动计算每个步骤的值.

Make sure to understand this process, otherwise it can cause quite some frustrating weird issues. If you find it hard to follow then you could take pen and paper and manually calculate the value for each step.

这篇关于将自透明纹理渲染到帧缓冲区时发生奇怪的融合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆