2014-06-29 64 views
1

我試圖混合從FBO獲得並在四邊形上繪製它們的兩種不同紋理(場景和雲)。LibGDX - 使用着色器在另一個紋理之上疊加紋理

uniform sampler2D u_texture; 
uniform sampler2D u_texture2; 
uniform vec2 u_res; 

void main(void) 
{ 
vec2 texCoord = gl_FragCoord.xy/u_res.xy; 

vec4 sceneColor = texture2D(u_texture, texCoord); 
vec4 addColor = texture2D(u_texture2, texCoord);  

gl_FragColor = sceneColor+addColor; 
} 

glBlendFunc是

Gdx.gl20.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA); 

我試過的所有glBlendFunc組合及以上爲最佳組合。

創建的FBO:

fbClouds = new FrameBuffer(Format.RGBA8888, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true); 
fbScene = new FrameBuffer(Format.RGB565, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true); 
fbMix = new FrameBuffer(Format.RGB565, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true); 

創建雲:

fbClouds.begin(); 
    Gdx.gl.glClearColor(0, 0, 0, 0); // to make it transparent 
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT); 
    modelBatch.begin(cam);  
    for (Integer e : drawOrder) { 
     if(isVisible(cam, clouds[e])){ 
      drawLightning(e, modelBatch);   
      modelBatch.render(clouds[e], cloudShader); 
      modelBatch.flush();    
     } 
    } 
    modelBatch.end(); 
    fbClouds.end(); 

渲染代碼:

Gdx.gl20.glDisable(GL20.GL_BLEND); 
//Gdx.gl20.glEnable(GL20.GL_BLEND); 
//Gdx.gl20.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA); 
fbMix.begin(); 
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT); 

    mixShader.begin(); 
    fbScene.getColorBufferTexture().bind(1); 
    mixShader.setUniformi("u_texture", 1); 

    fbClouds.getColorBufferTexture().bind(0); 
    mixShader.setUniformi("u_texture2", 0); 

    mixShader.setUniformf("u_res", Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); 

    quad.render(mixShader, GL20.GL_TRIANGLES); 

    mixShader.end(); 
fbMix.end(); 

所以,我得到意想不到的結果(雲有絕對的白色,但他們應該灰色):

unexpected result

在情況下,如果我用modelbatch渲染雲的結果是應該是: expected result

什麼是不失色彩混合兩種紋理的正確方法?

+0

因爲您將灰色添加到明亮的藍色,所以它變成了亮白色。但我不確定你的描述到底發生了什麼。混合func是你在繪製FBO的時候使用的什麼,或者你用什麼來繪製所有的FBO到屏幕上?在您使用的片段着色器中,您需要使用雲紋理的alpha來乘以雲紋理和(雲紋理的1- alpha)以乘以天空紋理。但是您需要確保您的雲端FBO支持alpha並寫入該alpha通道。有點複雜。也許有一個更簡單的方法? – Tenfour04

+0

@ Tenfour04,我應該使用哪種配方?你可以根據我的着色器源代碼向我展示它嗎?雲可能是紅色或綠色,沒關係。無論如何,它們都變成了白色! – Nolesh

+0

我還不清楚你在做什麼。如果你能澄清,我想我有一個可行的解決方案。您在頂部顯示片段着色器。我認爲你的意思是你把天空和太陽畫給一個FBO,然後把這些雲畫給另一個FBO。假設這是正確的,你如何用ModelInstance繪製雲?你正在畫雲的FBO的顏色是什麼?你提到混合func你提到了什麼你用雲將他們吸入他們的FBO,或者是你用什麼來繪製你的Quad與兩個FBO到主緩衝區? – Tenfour04

回答

1

你用來繪製兩個FBO到屏幕上的混合函數應該是不相關的,因爲沒有任何東西通過它們顯示,對吧?因此,在繪製FBO之前應該關閉混合,否則就是在浪費GPU循環,將FBO與清晰的顏色混合在一起。

它變成白色的原因是您將灰色添加到藍色而不先使藍色變暗。通常,當您在屏幕上繪製透明對象時,可以使用如下所示的混合函數:GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA。這意味着你將精靈的顏色乘以其透明度(有效地使透明顏色變暗),並將該背景乘以精靈alpha的倒數(從而使將被添加到精靈的不透明像素的像素變暗,以便它們不會太亮)。

就你而言,你希望在你的片段着色器中模擬相同的東西,因爲你要在輸出到屏幕之前混合着色器中的兩個紋理。

所以,如果你的雲FBO有一個alpha通道,你可以在你的片段着色器做到這一點,你會好到哪裏去:

void main() 
{ 
    vec2 texCoord = gl_FragCoord.xy/u_res.xy; 

    vec4 sceneColor = texture2D(u_texture, texCoord); 
    vec4 addColor = texture2D(u_texture2, texCoord);  

    gl_FragColor = addColor*addColor.a + sceneColor*(1-addColor.a); 
} 

然而,你的雲的FBO沒有一個alpha通道,你需要改變一些東西。

你可以做的一件事是讓你的FBO顏色紋理使用RGBA4444,所以它有一個alpha通道,然後仔細畫你的雲,以便他們也寫入alpha通道。這會很複雜,因爲你必須使用分離的混合函數,在這裏你可以分別爲RGB和A通道選擇兩種不同的混合函數。我以前沒有這樣做過。雖然它應該是可能的,我甚至沒有嘗試過這種方法,因爲我認爲4位顏色看起來非常糟糕。

或者,如果您的雲彩全部變爲單色,您可以將Alpha信息編碼到其中一個顏色通道。要做到這一點,你需要自定義你用來繪製雲層到FBO的片段着色器。這將是這個樣子:

vec4 textureColor = texture2D(u_texture, v_texCoord); 
gl_FragColor = vec4(textureColor.r * textureColor.a, textureColor.a, 0, textureColor.a); 

這樣做是把雲的單色在R聲道的與阿爾法乘前,它把阿爾法在G通道。我們想要預先乘以alpha,所以我們可以簡單地將編碼後的雲精靈添加到場景中。這是因爲,當您在已繪製的精靈半透明區域中的已繪製精靈的前面繪製某些東西時,您需要使G編碼的α變亮以使最終FBO圖像中的像素更加不透明。由於我們使用的是預先倍增的alpha,因此使用混合函數GL_ONE GL_ONE_MINUS_SRC_ALPHA繪製雲。 (這是一個輕微的近似值,因爲目標的G編碼alpha變得被混合函數的第二部分變暗了一點,但我看着數學,看起來可以接受,近似結果略微更接近透明的雲。)

所以現在雲FBO看起來像一堆黃色,如果你把它畫到原樣。我們只需要稍作調整,我們的片段着色器上面使用的編碼數據:

void main() 
{ 
    vec2 texCoord = gl_FragCoord.xy/u_res.xy; 

    vec4 sceneColor = texture2D(u_texture, texCoord); 
    vec4 addColor = texture2D(u_texture2, texCoord);  

    gl_FragColor = vec4(addColor.r*addColor.g) + sceneColor*(1-addColor.g); 
} 

如果你想染成你的雲不是純粹的灰色以外的東西,你可以添加一個統一色調:

void main() 
{ 
    vec2 texCoord = gl_FragCoord.xy/u_res.xy; 

    vec4 sceneColor = texture2D(u_texture, texCoord); 
    vec4 addColor = texture2D(u_texture2, texCoord);  

    gl_FragColor = u_cloudTint*vec4(addColor.r*addColor.g) + sceneColor*(1-addColor.g); 
} 
+0

這真的是一個很好的答案。謝謝。如果我理解正確,我應該使用FBO'Format.RGBA8888'作爲具有'Gdx.gl.glClearColor(0,0,0,0);'的雲使它們透明。和任意格式(我使用RGB565)爲其他FBO,包括'混合'FBO。另外,混音時我必須禁用「混合」。但是當我將這兩種紋理混合在一起時,雲彩看起來是透明的(我通過它們看到了地平線),但現在已經有了顏色。我更新了問題,所以你可以看到我在做什麼。 – Nolesh

+0

因此,花費一些時間後,我的結果與上面的截圖幾乎相同,但云彩具有半透明的顏色。但我需要有不透明的雲彩。換句話說,使用這個着色器我會失去雲層的深度。 – Nolesh

+0

不確定您使用的兩種方法中的哪一種,「之前」或之後的部分。如果您使用第一種方法,則需要使用單獨的混合功能。您可能必須使用第一種方法來防止雲略微透明。您需要一個標準的GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA用於RGB,而GL_ONE,GL_ONE用於A.另外請注意,如果以Android爲目標,則必須指定RGBA4444,而不是RGBA8888。並非所有設備都支持RGBA8888。 – Tenfour04