2015-06-30 72 views
0

我试图渲染帧缓冲区的自我透明纹理,但我得到的不是我猜到的:先前在帧缓冲区上呈现的所有东西都被忽略,并且此纹理与我清洗我的主要画布的颜色混合。将自我透明纹理渲染到帧缓冲区时奇怪的混合

这就是我想得,但不使用缓存:

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):

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

所以,我怎么可以使帧缓存版本中运行的第一个版本的方式做? ;)

回答

4

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

但我认为这很好理解,如果你想使用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函数。这意味着源值(texel)与源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呈现每个texel值为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

请务必理解这个过程,否则会导致一些令人沮丧的奇怪问题。如果你觉得很难遵循,那么你可以拿笔和纸,并手动计算每一步的值。

+0

很好的答案,谢谢!现在我认为我完全熟悉所有FBO混合的东西。 –

相关问题