如何在帧缓冲区中添加无数灯光

时间:2018-11-30 00:08:42

标签: c++ opengl glsl deferred-shading

按照learningopengl教程(https://learnopengl.com/Advanced-Lighting/Deferred-Shading) 如GLSL所示,作者保留了固定的光量(32灯):

 #version 330 core
out vec4 FragColor;

in vec2 TexCoords;

uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D gAlbedoSpec;

struct Light {
    vec3 Position;
    color;
};
const int NR_LIGHTS = 32;
uniform Light lights [NR_LIGHTS];
uniform vec3 viewPos;

void main ()
{
    // retrieve data from G-buffer
    vec3 FragPos = texture (gPosition, TexCoords) .rgb;
    vec3 Normal = texture (gNormal, TexCoords) .rgb;
    vec3 Albedo = texture (gAlbedoSpec, TexCoords) .rgb;
    float Specular = texture (gAlbedoSpec, TexCoords) .a;

    // then calculate lighting as usual
    vec3 lighting = Albedo * 0.1; // hard-coded ambient component
    vec3 viewDir = normalize (viewPos - FragPos);
    for (int i = 0; i <NR_LIGHTS; ++ i)
    {
        // diffuse
        vec3 lightDir = normalize (lights [i] .Position - FragPos);
        vec3 diffuse = max (dot (Normal, lightDir), 0.0) * Albedo * lights [i] .Color;
        lighting + = diffuse;
    }

    FragColor = vec4 (lighting, 1.0);
}

在应用灯光时:

glBindFramebuffer (GL_FRAMEBUFFER, 0);

        // 2. lighting pass: calculate lighting by iterating over screen filled quad pixel-by-pixel using the gbuffer's content.

        glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        shaderLightingPass.use ();
        glActiveTexture (GL_TEXTURE0);
        glBindTexture (GL_TEXTURE_2D, gPosition);
        glActiveTexture (GL_TEXTURE1);
        glBindTexture (GL_TEXTURE_2D, gNormal);
        glActiveTexture (GL_TEXTURE2);
        glBindTexture (GL_TEXTURE_2D, gAlbedoSpec);
        // send light relevant uniforms
        for (unsigned int i = 0; i <lightPositions.size (); i ++)
        {
            shaderLightingPass.setVec3 ("lights [" + std :: to_string (i) + "] .Position", lightPositions [i]);
            shaderLightingPass.setVec3 ("lights [" + std :: to_string (i) + "] .Color", lightColors [i]);
            // update attenuation parameters and calculate radius
            const float constant = 1.0; // note that we do not send this to the shader, we assume it is always 1.0 (in our case)
            const float linear = 0.7;
            const float quadratic = 1.8;
            shaderLightingPass.setFloat ("lights [" + std :: to_string (i) + "] .Linear", linear);
            shaderLightingPass.setFloat ("lights [" + std :: to_string (i) + "] .Quadratic", quadratic);
        }
        shaderLightingPass.setVec3 ("viewPos", camera.Position);
        // finally render quad
        renderQuad ();

但是我希望能够添加任意数量的灯光,因为我的项目将有无数的灯光(激光枪,篝火,爆炸),所以我做了一些更改:

GLSL:

uniform Light light;
uniform vec3 viewPos;

void main()
{             
    // retrieve data from gbuffer
    vec3 FragPos = texture(gPosition, TexCoords).rgb;
    vec3 Normal = texture(gNormal, TexCoords).rgb;
    vec3 Diffuse = texture(gAlbedoSpec, TexCoords).rgb;
    float Specular = texture(gAlbedoSpec, TexCoords).a;

    // then calculate lighting as usual
    vec3 lighting  = Diffuse * 0.1; // hard-coded ambient component
    vec3 viewDir  = normalize(viewPos - FragPos);

        // diffuse
        vec3 lightDir = normalize(light.Position - FragPos);
        vec3 diffuse = max(dot(Normal, lightDir), 0.0) * Diffuse * light.Color;
        // specular
        vec3 halfwayDir = normalize(lightDir + viewDir);  
        float spec = pow(max(dot(Normal, halfwayDir), 0.0), 16.0);
        vec3 specular = light.Color * spec * Specular;
        // attenuation
        float distance = length(light.Position - FragPos);
        float attenuation = 1.0 / (1.0 + light.Linear * distance + light.Quadratic * distance * distance);
        diffuse *= attenuation;
        specular *= attenuation;
        lighting += diffuse + specular;        

    FragColor = vec4(lighting, 1.0);
}

然后我一个个地传递值并渲染一个四边形:

for (unsigned int i = 0; i < lightPositions.size(); i++)
        {
            shaderLightingPass.use();
            shaderLightingPass.setInt("gPosition", 0);
            shaderLightingPass.setInt("gNormal", 1);
            shaderLightingPass.setInt("gAlbedoSpec", 2);
            shaderLightingPass.setVec3("light.Position", lightPositions[i]);
            shaderLightingPass.setVec3("light.Color", lightColors[i]);

            const float constant = 1.0; // note that we don't send this to the shader, we assume it is always 1.0 (in our case)
            const float linear = 0.7;
            const float quadratic = 0.08;
            shaderLightingPass.setFloat("light.Linear", linear);
            shaderLightingPass.setFloat("light.Quadratic", quadratic);
            shaderLightingPass.setVec3("viewPos", camera.Position);

            renderQuad();

            glUseProgram(-1);

        }

,并且还添加了一个新的着色器以在屏幕上渲染帧缓冲区:

screenShader.use();
renderQuad();

但是我的代码仅渲染第一盏灯: Result 谁能告诉我我做错了什么以及如何在最终结果中添加灯光?

2 个答案:

答案 0 :(得分:0)

请包含以下代码

void renderDeferredPass(int i)
{
glUseProgram(ps[Passes::Deferred]);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, g_fbo);
glDepthMask(GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
//mat4 model = glm::scale(mat4(1.0f), vec3(3.1f, 3.1f, 3.1f));
   model = glm::translate(mat4(1.0f), vec3(-150.0f, -600.0f, -800.0f+camera));
   model = glm::rotate(model, 30.0f, vec3(0.0f, 1.0f, 0.0f));

    mat4 view = glm::lookAt(glm::vec3(0.0, 0.0, 0.0), glm::vec3(0.0, 0.0, -5.0), glm::vec3(0.0, 1.0, 0.0));

    glUniformMatrix4fv(modelLocation, 1, GL_FALSE, &model[0][0]);
    glUniformMatrix4fv(viewLocation, 1, GL_FALSE, &view[0][0]);
    glUniformMatrix4fv(projLocation, 1, GL_FALSE, &projection[0][0]);
    glUniform1i(textureLocation, 0);

    quad->Render();

    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
    glUseProgram(0);
    glDepthMask(GL_FALSE);
    glDisable(GL_DEPTH_TEST);
   } 

还有

 void renderLightPass()
{
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glClear(GL_COLOR_BUFFER_BIT);
    glEnable(GL_BLEND);
    glBlendEquation(GL_FUNC_ADD);
    glBlendFunc(GL_ONE, GL_ONE);

    glUseProgram(ps[Passes::LightPass]);
    glBindVertexArray(quadVAO);
    bindUbo();

    for (unsigned int i = 0; i < NUM_GBUFFER_TEXTURES; i++) 
     {
           glActiveTexture(GL_TEXTURE1 + i);
           glBindTexture(GL_TEXTURE_2D, 
           g_textures[POSITION_TEXTURE + i]);
      }


    glUniform1i(mapLocations[POSITION_TEXTURE], 1);
    glUniform1i(mapLocations[DIFFUSE_TEXTURE], 2);
    glUniform1i(mapLocations[NORMAL_TEXTURE], 3);
    glUniform1i(mapLocations[TEXCOORD_TEXTURE], 4);

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);

    glUseProgram(0);
    glBindVertexArray(0);

    glEnable(GL_DEPTH_TEST);
    glBindTexture(GL_TEXTURE_2D, 0);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

   } 

您的绘制函数应类似于

void display()
{
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glGenerateMipmap(GL_TEXTURE_2D);
    glEnable(GL_MULTISAMPLE);

    //for (int i = 0; i < quad->m_Entries.size(); i++)
        {
          renderDeferredPass(0);
          renderLightPass();
        }

    glutSwapBuffers();
    glutPostRedisplay();
    } 

有关完整实施的信息,请参见

https://github.com/PixelClear/Deferred-renderer

我上面有代码,我们在SSBO中存储灯光信息,因此此演示有32盏灯,但可以轻松扩展到很多灯。

答案 1 :(得分:0)

问题是由于与场景重叠的#个灯光重复了固定的“环境”一词。

着色器包含:

vec3 lighting  = Diffuse * 0.1; // hard-coded ambient component

这在每次光线重叠时有效地重新添加了反照率。

旧代码的总和如下:

vec3 lighting = Diffuse * 0.1;
foreach (Light l : lights)
    lighting += Diffuse * (l's diffuse lighting)

但是现在有了添加剂混合:

foreach (Light l : lights)
    lighting += Diffuse * 0.1;
    lighting += Diffuse * (l's diffuse lighting)

因此,https://i.ibb.co/gMBtM6c/With-Blend.png中的环境变得过于明亮

要解决此问题,您需要将(Diffuse * 0.1)项分离到单独的着色器中。您将有1个绘制调用以应用环境,然后进行n个绘制调用以生成n个灯光。

C ++端的算法如下所示: 确保您还有添加剂混合物。

  1. 清除屏幕
  2. 设置环境着色器,绘制四边形。
  3. 设置灯光着色器,进行照明循环并为n个灯光绘制n个四边形。

编辑:同样,您似乎也没有根据屏幕截图阅读正确的Albedo纹理。看来您正在根据获得的颜色读取位置缓冲区。