Как добавить бесчисленные источники света в фреймбуфере

После учебного пособия Learnopengl (https://learnopengl.com/Advanced-Lighting/Deferred-Shading)
автор оставляет фиксированное количество света (32 источника света), как показано в GLSL:

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

но мой код отображает только первый свет:
Результат
Может кто-нибудь сказать мне, что я делаю неправильно и как добавить свет в конечном результате?

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 источника света, но может быть легко распространена на многие.

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 вызов на розыгрыш, чтобы применить ambient, а затем n вызовов на n ламп.

Алгоритм на стороне C ++ будет выглядеть так:
Убедитесь, что у вас есть смесь добавок еще.

  1. Чистый экран
  2. Установите шейдер Ambient, Draw Quad.
  3. Установите шейдер Light, сделайте вашу петлю освещения и нарисуйте n Quads для n источников света.

РЕДАКТИРОВАТЬ: Кроме того, похоже, что вы не читаете правильную текстуру Альбедо на основе ваших скриншотов. Похоже, вы читаете буфер положения на основе цветов, которые вы получаете.

0

По вопросам рекламы [email protected]