2017-08-09 89 views
0

在過去的幾天裏,我在Java(Libgdx)中玩閃電。我是新來的OpenGL或着色器,我偶然發現一個不錯的教程,如何使用法線貼圖(https://github.com/mattdesl/lwjgl-basics/wiki/ShaderLesson6)實現照明。到目前爲止,我設法用一盞燈做到這一點,現在我試圖用多盞燈做同樣的效果。我試圖用添加劑混合對每個燈進行一次繪製調用。陰影繪製正確,但每次添加燈光時,環境顏色都變得更亮。我嘗試了一些東西,但沒有任何工作,我卡住了。添加燈光時,環境光線會變得更亮(OpenGL,法線貼圖)

我的渲​​染方法:

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

在這裏,我的片段着色器:

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

這取決於你的環境光的定義。如果每個光源「發出」環境光,那麼這種行爲就完全沒有問題。如果環境光是某種特殊的東西,只能添加一次,那麼它應該從光着色器中完全移除,並通過單獨的繪圖調用/着色器添加。就像一個評論一樣:每個光源渲染每個幾何圖形的方式效率非常低。考慮在一個着色器中使用統一數組。作爲奧地利人是沒有什麼不好的:) – BDL

+0

感謝您的快速回復:)我剛剛從着色器中刪除了環境光線。我怎樣才能實現它,而不使用其他着色器? – Quentin

+0

你不能;)。創建一個僅輸出環境光線並使用一次的第二個着色器。我應該在第一條評論中提到,如果屏幕空間中存在對象重疊,那麼您的算法可能會出現問題,因爲它們可能編寫不正確。 – BDL

回答

0

要做到這一點有一個單一的渲染調用,您的片段着色器必須接受光的位置來處理的數組。着色器在編譯時必須知道數組大小,因此您應該將數組設置得足夠大,以滿足您需要的燈數量(並且您需要更少的燈時,可以將其餘燈設置爲黑色)。

我調整了下面的着色器,只是假設它在代碼中正確地工作。我不知道你對光照貼圖做了什麼,所以我用更傳統的東西替換了衰減計算。

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

} 

要通過你的光參數的着色器:

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

非常感謝你:)它的工作正常。 – Quentin