2015-06-30 104 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混合的東西。 –

相關問題