Java 添加灯光时,环境光变得更亮(OpenGL,法线贴图)

Java 添加灯光时,环境光变得更亮(OpenGL,法线贴图),java,opengl,libgdx,shader,blending,Java,Opengl,Libgdx,Shader,Blending,在过去的几天里,我在Java中玩了lightning(Libgdx)。我是OpenGL或着色器的新手,我偶然发现了一个很好的教程,介绍了如何使用法线贴图实现照明()。到目前为止,我设法做到这一点与一个灯光,现在我正试图做同样的效果与多个灯光。我尝试使用添加混合为每个灯光执行一次绘制调用。阴影绘制正确,但每次添加灯光时,环境光颜色都会变得更亮。我试了几次,但都没用,我被卡住了 我的渲染方法: @Override public void render () { renderToFbo(Gdx

在过去的几天里,我在Java中玩了lightning(Libgdx)。我是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);
}

要使用单个渲染调用执行此操作,片段着色器必须接受要处理的灯光位置数组。着色器必须在编译时知道数组大小,因此应将数组设置为足够大,以容纳所需数量的灯光(当需要较少的灯光时,可以将其余灯光设置为黑色)

我在下面修改了你的着色器,只是假设它在你的代码中正确工作。我不知道你在用光照贴图做什么,所以我用更传统的东西代替了你的衰减计算

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

}

这取决于您对环境光的定义。如果每个光源“发射”环境光,则此行为完全正常。如果环境光是某种特殊的东西,只应添加一次,则应将其从灯光着色器中完全删除,并通过单独的绘制调用/着色器添加。正如注释所示:每个光源渲染每个几何体一次的方法效率非常低。考虑在一个着色器中使用统一数组。作为一个奥地利人也没什么不好:)谢谢你的快速回复:)我刚刚从着色器中删除了环境光。我现在如何实现它,而不使用其他着色器?您不能;)。创建第二个仅输出环境光的着色器并使用一次。我还应该在第一条评论中提到,如果在屏幕空间中有对象重叠,那么您的算法可能会出现问题,因为它们可能组合不正确。我现在了解了如何显示正确的环境光。我尝试运行20多个灯光,但性能非常差,但我不知道如何重写着色器以一次计算所有灯光。对于具有多个对象的场景,这种方法将很难保持。它的性能也很差。对可以处理多个灯光的着色器使用一个渲染调用。
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);