2017-08-09 5 views
0

Ces derniers jours, j'ai joué avec la foudre en Java (Libgdx). Je suis nouveau à OpenGL ou Shaders et j'ai trébuché à travers un joli tutoriel comment mettre en œuvre l'éclairage avec le mappage normal (https://github.com/mattdesl/lwjgl-basics/wiki/ShaderLesson6). Jusqu'à présent, j'ai réussi à le faire avec une lumière et maintenant j'essaye de faire le même effet avec plusieurs lumières. J'ai essayé d'effectuer un appel de tirage pour chaque lumière avec un mélange additif. Les ombres dessinent correctement, mais chaque fois que j'ajoute une lumière, la couleur ambiante devient plus claire. J'ai essayé quelques petites choses mais rien n'a fonctionné et je suis coincé.La lumière ambiante devient plus claire quand une lumière est ajoutée (OpenGL, cartographie normale)

Mon rendre les méthodes:

@Override 
public void render() { 
    renderToFbo(Gdx.input.getX(), Gdx.graphics.getHeight() - Gdx.input.getY()); 
    renderToScreen(Gdx.input.getX(), Gdx.graphics.getHeight() - Gdx.input.getY()); 

    renderToFbo(200, 200); 
    batch.setBlendFunction(GL_ONE,GL_ONE_MINUS_SRC_COLOR); 
    renderToScreen(200,200); 

    renderToFbo(500, 500); 
    batch.setBlendFunction(GL_ONE, GL_ONE_MINUS_SRC_COLOR); 
    renderToScreen(500,500); 
} 


private void renderToFbo(float posX, float posY){ 
    fbo.begin(); 
    batch.setBlendFunction(GL_ONE, GL_ZERO); 
    batch.setShader(defaultShader); 
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); 
    batch.begin(); 
    batch.draw(lightMap, posX - lightSize/2, posY - lightSize/2, lightSize,lightSize); 
    batch.end(); 
    fbo.end(); 
} 

private void renderToScreen(float posX, float posY){ 
    batch.setShader(lightningShader); 
    batch.begin(); 

    float x = posX/(float) Gdx.graphics.getWidth(); 
    float y = posY/(float) Gdx.graphics.getHeight(); 
    LIGHT_POS.x = x; 
    LIGHT_POS.y = y; 

    lightningShader.setUniformf("lightPos", LIGHT_POS.x, LIGHT_POS.y, LIGHT_POS.z); 

    fbo.getColorBufferTexture().bind(2); 
    normalMap.bind(1); 
    texture.bind(0); 

    batch.draw(texture, 0,0); 
    batch.end(); 
} 

Et voici mon shader fragment:

varying vec4 vColor; 
varying vec2 vTexCoord; 
uniform sampler2D u_texture; //diffuse map 
uniform sampler2D u_normals; //normal map 
uniform sampler2D u_light;  //light map 

uniform vec2 resolution;  //resolution of screen 
uniform vec3 lightPos;  //light position, normalized 
uniform vec4 lightColor;  //light RGBA -- alpha is intensity 
uniform vec4 ambientColor; //ambient RGBA -- alpha is intensity 


void main() { 
//RGBA of our diffuse color 
vec4 diffuseColor = texture2D(u_texture, vTexCoord); 

//RGB of our normal map 
vec3 normalMap = texture2D(u_normals, vTexCoord).rgb; 
//NormalMap.g = 1.0 - NormalMap.g; 

//The delta position of light 
vec3 lightDir = vec3(lightPos.xy - (gl_FragCoord.xy/resolution.xy), lightPos.z); 

lightDir.x *= resolution.x/resolution.y; 


//normalize our vectors 
vec3 N = normalize(normalMap * 2.0 - 1.0); 
vec3 L = normalize(lightDir); 

//Pre-multiply light color with intensity 
//Then perform "N dot L" to determine our diffuse term 
vec3 diffuse = (lightColor.rgb * lightColor.a) * max(dot(N, L), 0.0); 

//pre-multiply ambient color with intensity 
vec3 ambient = ambientColor.rgb * ambientColor.a; 

//calculate attenuation from lightmap 
vec2 lighCoord = (gl_FragCoord.xy/resolution.xy); 
vec3 attenuation = texture2D(u_light, lighCoord).rgb; 

//the calculation which brings it all together 
vec3 intensity = ambient + diffuse * attenuation; 
vec3 finalColor = diffuseColor.rgb * intensity; 
gl_FragColor = vColor * vec4(finalColor, diffuseColor.a); 
} 
+0

Cela dépend de votre définition de la lumière ambiante. Si chacune de vos sources de lumière "émet" de la lumière ambiante, alors ce comportement est complètement correct. Si la lumière ambiante est une sorte de chose spéciale qui ne doit être ajoutée qu'une seule fois, elle doit être complètement supprimée du light shader et ajoutée par un draw-call/shader séparé. Juste comme un commentaire: Votre façon de rendre chaque géométrie une fois par source lumineuse est très inefficace. Envisagez plutôt d'utiliser des tableaux uniformes dans un shader. Et être autrichien n'est rien de mal :) – BDL

+0

Merci pour votre réponse rapide :) Je viens de supprimer la lumière ambiante du shader. Comment puis-je l'implémenter maintenant, sans utiliser un autre shader? – Quentin

+0

Vous ne pouvez pas;). Créez un second shader qui sort juste la lumière ambiante et l'utilise une fois. J'aurais dû aussi mentionner dans le premier commentaire que vous pourriez avoir un problème avec votre algorithme si vous avez des objets qui se chevauchent dans l'espace d'écran car ils pourraient ne pas être composés correctement. – BDL

Répondre

0

Pour ce faire, avec un seul rendu appel, votre fragment shader doit accepter un tableau de positions claires à traiter. Les shaders doivent connaître la taille des tableaux au moment de la compilation, vous devez donc définir le tableau assez grand pour le nombre de lumières dont vous aurez besoin (et lorsque vous en avez besoin, vous pouvez définir les lumières restantes en noir).

J'ai adapté votre shader ci-dessous, en supposant qu'il fonctionne correctement dans votre code. Je ne sais pas ce que vous faites avec les trucs lightmap, donc j'ai remplacé votre calcul d'atténuation par quelque chose de plus traditionnel.

varying vec4 vColor; 
varying vec2 vTexCoord; 
uniform sampler2D u_texture; //diffuse map 
uniform sampler2D u_normals; //normal map 

const int LIGHT_COUNT = 4; 

uniform vec2 resolution;  //resolution of screen 
uniform vec3[LIGHT_COUNT] lightPos;  //light position, normalized 
uniform vec4[LIGHT_COUNT] lightColor;  //light RGBA -- alpha is intensity 
uniform vec4 ambientColor; //ambient RGBA -- alpha is intensity 

void main() { 

    vec4 diffuseColor = texture2D(u_texture, vTexCoord); 
    vec3 normalMap = texture2D(u_normals, vTexCoord).rgb; 
    vec3 N = normalize(normalMap * 2.0 - 1.0); 

    float resolutionFactor = resolution.x/resolution.y; 

    vec3 diffuse = new vec3(0.0); 
    for (int i=0; i<LIGHT_COUNT; i++){ 
     vec3 lightDir = vec3(lightPos[i].xy - (gl_FragCoord.xy/resolution.xy), lightPos[i].z); 
     lightDir.x *= resolutionFactor; 
     vec3 L = normalize(lightDir); 
     float distance = length(lightDir); 
     vec3 attenuation = 1.0/(0.4 + 3.0*distance + (20.0*distance*distance)); 
     diffuse += attenuation * (lightColor[i].rgb * lightColor[i].a) * max(dot(N, L), 0.0); 
    } 

    //pre-multiply ambient color with intensity 
    vec3 ambient = ambientColor.rgb * ambientColor.a; 


    //the calculation which brings it all together 
    vec3 intensity = min(vec3(1.0), ambient + diffuse); // don't remember if min is critical, but I think it might be to avoid shifting the hue when multiple lights add up to something very bright. 
    vec3 finalColor = diffuseColor.rgb * intensity; 
    gl_FragColor = vColor * vec4(finalColor, diffuseColor.a); 

} 

Pour passer vos paramètres de lumière à votre shaders:

static final int LIGHT_COUNT = 4; 
final float[] tmpLightPositions = new float[3 * LIGHT_COUNT]; 
final float[] tmpLightColors = new float[4 * LIGHT_COUNT]; 

//... 

int i = 0; 
for (Vector3 pos : myLightPositions) {// should be LIGHT_COUNT of them 
    tmpLightPositions[i++] = pos.x; 
    tmpLightPositions[i++] = pos.y; 
    tmpLightPositions[i++] = pos.z; 
} 
i = 0; 
for (Color col : myLightColors) { 
    tmpLightColors[i++] = color.r; 
    tmpLightColors[i++] = color.g; 
    tmpLightColors[i++] = color.b; 
    tmpLightColors[i++] = color.a; 
} 
shader.setUniform3fv("lightPos", tmpLightPositions, 0, tmpLightPositions.length); 
shader.setUniform4fv("lightColor", tmpLightColors, 0, tmpLightColors.length); 
+0

Merci beaucoup :) Ça fonctionne très bien. – Quentin