2014-06-29 3 views
1

J'essaie de mélanger deux textures différentes (scène et nuages) qui sont obtenues à partir de FBO et les dessiner sur quad.LibGDX - superposer la texture au-dessus d'une autre texture en utilisant le shader

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 est

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

J'ai essayé toutes les combinaisons de glBlendFunc et la combinaison ci-dessus était le meilleur.

Création FBOs:

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

nuages ​​créant:

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(); 

rendre le code:

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(); 

Alors, j'obtenir le résultat inattendu (les nuages ​​ont absolument couleur blanche, mais ils devraient être gris):

unexpected result

En cas si j'utilise modelbatch pour rendre les nuages ​​le résultat est comme cela devrait être: expected result

Quelle est la bonne façon de mélanger deux textures sans perdre la couleur?

+0

Il devient blanc brillant parce que vous ajoutez du gris au bleu vif. Mais je ne suis pas sûr de votre description exactement ce qui se passe. Est-ce que ce mélange est ce que vous utilisez quand vous dessinez à l'un des FBO, ou ce que vous utilisez pour attirer tous vos FBO à l'écran? A l'intérieur de ce fragment de shader que vous avez utilisé, vous devez utiliser l'alpha de la texture du nuage pour multiplier par la texture du nuage et (1-alpha de la texture du nuage) pour multiplier par la texture du ciel. Mais vous devez vous assurer que le FBO de votre cloud prend en charge l'alpha et qu'il écrit sur ce canal alpha. Un peu compliqué. Peut-être qu'il y a un moyen plus facile? – Tenfour04

+0

@ Tenfour04, quelle formule dois-je utiliser? Pourriez-vous me montrer en fonction de mon code source de shader? Les nuages ​​peuvent être rouges ou verts, cela n'a pas d'importance. Ils sont devenus blancs dans tous les cas! – Nolesh

+0

Je ne sais toujours pas exactement ce que vous faites. Si vous pouvez clarifier, je pense que j'ai une solution qui fonctionnerait. Vous montrez un fragment de shader en haut. Je pense que vous voulez dire que vous dessinez le ciel et le soleil à un FBO, et les nuages ​​à un autre FBO. En supposant que c'est juste, comment dessinez-vous les nuages, avec une ModelInstance? Et quelle est la couleur claire du FBO sur lequel vous dessinez des nuages? Et est-ce que le mélange est ce que vous mentionnez ce que vous utilisez pour que les nuages ​​les dessinent dans leur FBO, ou est-ce ce que vous utilisez pour dessiner votre Quad avec les deux FBO dans le buffer principal? – Tenfour04

Répondre

1

La fonction de fusion que vous utilisez pour dessiner les deux FBO à l'écran devrait être sans importance, parce que rien ne les traverse, n'est-ce pas? Le mélange doit donc être désactivé avant de dessiner les FBO, ou vous perdez des cycles de GPU en mélangeant les FBO avec votre couleur claire.

La raison pour laquelle il devient blanc est que vous ajoutez du gris au bleu sans assombrir le bleu en premier. Normalement, lorsque vous dessinez un objet transparent sur l'écran, vous utilisez une fonction de fusion comme celle-ci: GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA. Cela signifie que vous multipliez la couleur de l'image-objet par sa transparence (obscurcissant efficacement les couleurs transparentes) et multipliez l'arrière-plan par l'inverse de l'alpha de l'image-objet (assombrissant ainsi les pixels qui seront ajoutés aux pixels opaques de l'image-objet. brillant).

Dans votre cas, vous voulez émuler la même chose dans votre fragment shader, puisque vous essayez de fusionner deux textures dans votre shader avant de les afficher à l'écran.

Donc, si votre nuage OIR avait un canal alpha, vous pouvez le faire dans votre fragment shader et vous seriez bien aller:

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); 
} 

Cependant, OIR de votre nuage ne dispose pas d'un canal alpha donc vous devez changer quelque chose. Une chose que vous pourriez faire est de faire en sorte que votre texture couleur FBO utilise RGBA4444 afin qu'elle ait un canal alpha, puis dessinez soigneusement vos nuages ​​afin qu'ils écrivent aussi sur le canal alpha. Ce serait un peu compliqué, car vous devez utiliser une fonction de mélange séparée, dans laquelle vous sélectionnez deux fonctions de mélange différentes pour les canaux RVB et A séparément. Je n'ai pas fait ça avant. Bien que cela devrait être possible, je n'ai même pas encore essayé cette méthode parce que je pense que les couleurs 4 bits sembleraient plutôt moche.Alternativement, si vos nuages ​​vont tous être monochrome, vous pouvez encoder vos informations alpha dans l'un des canaux de couleur. Pour ce faire, vous devrez personnaliser le fragment shader que vous utilisez pour dessiner les nuages ​​au FBO. Il ressemblerait à quelque chose comme ceci:

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

Qu'est-ce que cela n'est mis dans le canal R avec alpha pré-multiplié couleur monochrome du nuage, et il met l'alpha dans le canal G. Nous voulons pré-multiplier l'alpha afin que nous puissions simplement ajouter le sprite de nuage encodé sur la scène. En effet, lorsque vous dessinez quelque chose devant une image-objet déjà dessinée dans une zone translucide dans l'image-objet déjà dessinée, vous voulez éclaircir l'alpha codé en G pour rendre le pixel plus opaque dans l'image FBO finale. Comme nous utilisons un alpha pré-multiplié, dessinez les nuages ​​en utilisant la fonction de fusion GL_ONE, GL_ONE_MINUS_SRC_ALPHA.

(Ceci est une légère approximation parce que l'alpha codé en G de la destination est assombri un peu par la deuxième partie de la fonction de fusion, mais j'ai regardé les maths et cela semble acceptable. Maintenant, le nuage FBO ressemblerait à un tas de jaune si vous l'avez dessiné à l'écran tel quel. Nous avons juste besoin de faire un léger ajustement à notre shader fragment ci-dessus pour utiliser les données codées:

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); 
} 

Si vous voulez teinter vos nuages ​​autre chose que du gris pur, vous pouvez ajouter une teinte de couleur uniforme:

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

C'est vraiment une bonne réponse. Je vous remercie. Si je vous comprends bien, je devrais utiliser FBO 'Format.RGBA8888' pour les nuages ​​avec' Gdx.gl.glClearColor (0, 0, 0, 0); 'pour les rendre transparents. Et formats arbitraires (j'utilise RGB565) pour les autres FBO, y compris 'mix' FBO. De plus, je dois désactiver le "blending" lors du mixage.Mais quand je mélange les deux textures, les nuages ​​semblent transparents (je vois l'horizon à travers eux) mais ont maintenant de la couleur. J'ai mis à jour la question, afin que vous puissiez voir ce que je fais. – Nolesh

+0

Donc, après avoir passé quelque temps j'ai presque le même résultat que sur la capture d'écran ci-dessus, mais les nuages ​​ont une couleur semi-transparente. Mais j'ai besoin de nuages ​​d'opacité. En d'autres termes, en utilisant ce shader, je perds la profondeur des nuages. – Nolesh

+0

Vous ne savez pas laquelle de mes deux méthodes vous utilisez, la partie avant "Alternativement" ou après. Si vous utilisez la première méthode, vous devrez utiliser des fonctions de fusion séparées. Vous devrez peut-être utiliser la première méthode pour empêcher les nuages ​​d'être légèrement transparents. Vous voulez un standard GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA pour RVB, et GL_ONE, GL_ONE pour le A. Notez également que si vous ciblez Android, vous devez spécifier RGBA4444, pas RGBA8888. Tous les appareils ne prennent pas en charge RGBA8888. – Tenfour04

Questions connexes