为什么前向渲染灯时会出现 Z-fighting?
Why is there Z-fighting when forward rendering lights?
在我的 OpenGL 程序中,我注意到在前向渲染算法中光线混合在一起的地方出现了黑色像素。起初我忽略了它,直到我切换 Window API(从 SDL 到 GLFW)时它变得太有视觉问题了。
当我禁用 GL_DEPTH_TEST 时,黑色伪像消失了,但它们后面的灯光变得透明,所以这不是解决方案。 (这就是我发现潜在问题的方式)
我不确定问题出在哪里,可能是深度缓冲区问题,但切换 window API 可以增强伪像,有什么帮助吗?
当我移动相机时图案会闪烁
似乎黑色 lines/dots 在每个三角形中都是孤立的
这是我混合灯光的地方
// Note: I wrapped OpenGL calls into wrapper functions but the naming convenction is still the same
gl::Clear(gl::e_ColorBufferBit | gl::e_DepthBufferBit);
mesh->Render(m_forward_ambient);
gl::Enable(gl::e_Blend);
gl::SetBlendFunc(gl::e_One, gl::e_One); // Additive blending
gl::SetDepthMask(false); // No need to write to depth buffer
gl::SetDepthFunc(gl::e_Equal); // Only draw fragments of equal depths (ignore fragments behind basically)
{
for (word i = 0; i < m_lights.Length(); ++i)
{
m_active_light = m_lights[i];
mesh->Render(m_active_light->shader); // Shaders use active light
}
}
gl::SetDepthFunc(gl::e_Lequal); // Restore default state
gl::SetDepthMask(true);
gl::Disable(gl::e_Blend);
Shader代码(shader有全部代码,其他实现较少)
// Vertex
void main()
{
// pv_matrix is projection and camera, ml_matrix is model transform
gl_Position = pv_matrix * ml_matrix * vec4(pos, 1);
f_pos = (ml_matrix * vec4(pos, 1)).xyz;
f_nrm = (ml_matrix * vec4(normalize(nrm), 0)).xyz;
f_txc = txc;
}
// Fragment
struct Light
{
vec3 color;
float intensity;
};
struct Attenuation
{
float constant;
float linear;
float exponent;
};
struct PointLight
{
Light light;
Attenuation atten;
vec3 position;
float range;
};
struct SpotLight
{
PointLight plight;
vec3 direction;
float cutoff;
};
vec4 CalculateLight(Light light, vec3 direction, vec3 normal)
{
float diffuse_factor = dot(normal, -direction);
vec4 diffuse_color = vec4(0, 0, 0, 0);
vec4 specular_color = vec4(0, 0, 0, 0);
if (diffuse_factor > 0)
diffuse_color = vec4(light.color, 1) * light.intensity * diffuse_factor;
vec3 direction_to_eye = normalize(eye_pos - f_pos); // eye_pos is uniform for camera pos, f_pos is (position) attribute sent from vertex shader
vec3 reflect_direction = normalize(reflect(direction, normal));
float specular_factor = dot(reflect_direction, direction_to_eye); // specular calculations
if (specular_factor > 0)
{
specular_factor = pow(specular_factor, specular_power);
specular_color = vec4(light.color, 1) * specular_intensity * specular_factor;
}
return diffuse_color + specular_color;
}
vec4 CalculatePointLight(PointLight plight, vec3 normal)
{
vec3 light_direction = f_pos - plight.position;
float distance = length(light_direction);
if (distance > plight.range)
return vec4(0, 0, 0, 0);
light_direction = normalize(light_direction);
vec4 color = CalculateLight(plight.light, light_direction, normal);
float a = plight.atten.constant + (plight.atten.linear * distance) + (plight.atten.exponent * (distance * distance)) + 0.0001;
return color / a;
}
vec4 CalculateSpotLight(SpotLight slight, vec3 normal)
{
vec3 light_direction = normalize(f_pos - slight.plight.position);
float spot_factor = dot(light_direction, slight.direction);
vec4 color = vec4(0, 0, 0, 0);
if (spot_factor > slight.cutoff)
color = CalculatePointLight(slight.plight, normal) * (1.0 - ((1.0 - spot_factor) / (1.0 - slight.cutoff)));
return color;
}
uniform SpotLight spot_light;
void main()
{
FragColor = CalculateSpotLight(spot_light, f_nrm); // f_nrm is a (normal) attribute sent from vertex shader
}
您没有将 gl_Position
顶点着色器输出声明为 invariant
,因此即使您使用完全相同的公式,并且在两个过程中按位输入完全相同,您的着色器也不会保证得到完全相同的结果。您应该向所有通道中的所有着色器添加适当的 invariance qualifiers。
在我的 OpenGL 程序中,我注意到在前向渲染算法中光线混合在一起的地方出现了黑色像素。起初我忽略了它,直到我切换 Window API(从 SDL 到 GLFW)时它变得太有视觉问题了。
当我禁用 GL_DEPTH_TEST 时,黑色伪像消失了,但它们后面的灯光变得透明,所以这不是解决方案。 (这就是我发现潜在问题的方式)
我不确定问题出在哪里,可能是深度缓冲区问题,但切换 window API 可以增强伪像,有什么帮助吗?
当我移动相机时图案会闪烁
似乎黑色 lines/dots 在每个三角形中都是孤立的
这是我混合灯光的地方
// Note: I wrapped OpenGL calls into wrapper functions but the naming convenction is still the same
gl::Clear(gl::e_ColorBufferBit | gl::e_DepthBufferBit);
mesh->Render(m_forward_ambient);
gl::Enable(gl::e_Blend);
gl::SetBlendFunc(gl::e_One, gl::e_One); // Additive blending
gl::SetDepthMask(false); // No need to write to depth buffer
gl::SetDepthFunc(gl::e_Equal); // Only draw fragments of equal depths (ignore fragments behind basically)
{
for (word i = 0; i < m_lights.Length(); ++i)
{
m_active_light = m_lights[i];
mesh->Render(m_active_light->shader); // Shaders use active light
}
}
gl::SetDepthFunc(gl::e_Lequal); // Restore default state
gl::SetDepthMask(true);
gl::Disable(gl::e_Blend);
Shader代码(shader有全部代码,其他实现较少)
// Vertex
void main()
{
// pv_matrix is projection and camera, ml_matrix is model transform
gl_Position = pv_matrix * ml_matrix * vec4(pos, 1);
f_pos = (ml_matrix * vec4(pos, 1)).xyz;
f_nrm = (ml_matrix * vec4(normalize(nrm), 0)).xyz;
f_txc = txc;
}
// Fragment
struct Light
{
vec3 color;
float intensity;
};
struct Attenuation
{
float constant;
float linear;
float exponent;
};
struct PointLight
{
Light light;
Attenuation atten;
vec3 position;
float range;
};
struct SpotLight
{
PointLight plight;
vec3 direction;
float cutoff;
};
vec4 CalculateLight(Light light, vec3 direction, vec3 normal)
{
float diffuse_factor = dot(normal, -direction);
vec4 diffuse_color = vec4(0, 0, 0, 0);
vec4 specular_color = vec4(0, 0, 0, 0);
if (diffuse_factor > 0)
diffuse_color = vec4(light.color, 1) * light.intensity * diffuse_factor;
vec3 direction_to_eye = normalize(eye_pos - f_pos); // eye_pos is uniform for camera pos, f_pos is (position) attribute sent from vertex shader
vec3 reflect_direction = normalize(reflect(direction, normal));
float specular_factor = dot(reflect_direction, direction_to_eye); // specular calculations
if (specular_factor > 0)
{
specular_factor = pow(specular_factor, specular_power);
specular_color = vec4(light.color, 1) * specular_intensity * specular_factor;
}
return diffuse_color + specular_color;
}
vec4 CalculatePointLight(PointLight plight, vec3 normal)
{
vec3 light_direction = f_pos - plight.position;
float distance = length(light_direction);
if (distance > plight.range)
return vec4(0, 0, 0, 0);
light_direction = normalize(light_direction);
vec4 color = CalculateLight(plight.light, light_direction, normal);
float a = plight.atten.constant + (plight.atten.linear * distance) + (plight.atten.exponent * (distance * distance)) + 0.0001;
return color / a;
}
vec4 CalculateSpotLight(SpotLight slight, vec3 normal)
{
vec3 light_direction = normalize(f_pos - slight.plight.position);
float spot_factor = dot(light_direction, slight.direction);
vec4 color = vec4(0, 0, 0, 0);
if (spot_factor > slight.cutoff)
color = CalculatePointLight(slight.plight, normal) * (1.0 - ((1.0 - spot_factor) / (1.0 - slight.cutoff)));
return color;
}
uniform SpotLight spot_light;
void main()
{
FragColor = CalculateSpotLight(spot_light, f_nrm); // f_nrm is a (normal) attribute sent from vertex shader
}
您没有将 gl_Position
顶点着色器输出声明为 invariant
,因此即使您使用完全相同的公式,并且在两个过程中按位输入完全相同,您的着色器也不会保证得到完全相同的结果。您应该向所有通道中的所有着色器添加适当的 invariance qualifiers。