mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-07-17 23:39:18 +02:00
Sanitize NaN/Inf in env cubemap and tonemap to stop one-frame black flashes
This commit is contained in:
@@ -21,6 +21,11 @@ void gl::cubemap::alloc(GLint format, int width, int height, GLenum components,
|
||||
for (GLuint tgt = GL_TEXTURE_CUBE_MAP_POSITIVE_X; tgt <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; tgt++)
|
||||
glTexImage2D(tgt, 0, format, width, height, 0, components, type, nullptr);
|
||||
|
||||
// make the cubemap sample-complete with no mipmaps required.
|
||||
// generate_mipmaps() will later override MIN_FILTER once mips actually exist.
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,14 @@ vec3 envmap_color( vec3 normal )
|
||||
vec3 refvec = reflect(f_pos.xyz, normal); // view space
|
||||
refvec = vec3(inv_view * vec4(refvec, 0.0)); // world space
|
||||
vec3 envcolor = texture(envmap, refvec).rgb;
|
||||
// Sanitize. The env cubemap can briefly contain NaN/Inf after a
|
||||
// reflection-pass regeneration (m_empty_cubemap was bound during the
|
||||
// face render and on NVIDIA can sample as undefined). Without this
|
||||
// guard, a single bad texel propagates through env_reflection in
|
||||
// apply_lights() and produces one-frame black flashes on glossy
|
||||
// (esp. specgloss) surfaces.
|
||||
if (any(isnan(envcolor)) || any(isinf(envcolor))) envcolor = vec3(0.0);
|
||||
envcolor = max(envcolor, vec3(0.0));
|
||||
#else
|
||||
vec3 envcolor = vec3(0.5);
|
||||
#endif
|
||||
@@ -21,7 +29,12 @@ vec3 envmap_color_lod(vec3 fragnormal, float lod)
|
||||
#if ENVMAP_ENABLED
|
||||
vec3 refvec = reflect(f_pos.xyz, fragnormal); // view space — matches envmap_color exactly
|
||||
refvec = vec3(inv_view * vec4(refvec, 0.0)); // world space — was missing
|
||||
return textureLod(envmap, refvec, lod).rgb;
|
||||
vec3 envcolor = textureLod(envmap, refvec, lod).rgb;
|
||||
// See envmap_color() above — same NaN/Inf sanitize, also needed here
|
||||
// because mipmap generation propagates a single NaN texel across the
|
||||
// whole mip chain.
|
||||
if (any(isnan(envcolor)) || any(isinf(envcolor))) envcolor = vec3(0.0);
|
||||
return max(envcolor, vec3(0.0));
|
||||
#else
|
||||
return vec3(0.5); // was vec3(0.0), match the non-LOD fallback
|
||||
#endif
|
||||
|
||||
@@ -46,6 +46,17 @@ vec4 tonemap(vec4 x)
|
||||
// with pureWhite=1.0 it collapses to identity (L*(1+L)/(1+L) = L) and
|
||||
// just clips HDR>1.0 at the framebuffer -> washed-out / burnt look.
|
||||
// ACES gives a smooth highlight shoulder + slight toe contrast.
|
||||
return FBOUT(vec4(ACESFilm(x.rgb), x.a));
|
||||
|
||||
// Last-line-of-defense sanitize. ACESFilm has the form
|
||||
// (x*(a*x+b)) / (x*(c*x+d)+e)
|
||||
// which maps NaN -> NaN and +Inf -> NaN (Inf/Inf). Either turns the
|
||||
// pixel black after framebuffer clamp. A negative HDR input feeds a
|
||||
// negative numerator/denominator and can produce non-physical output
|
||||
// that also looks like a black flash. Clamp to a sensible range
|
||||
// before the curve so a single bad upstream pixel can't escape.
|
||||
vec3 hdr = x.rgb;
|
||||
hdr = mix(hdr, vec3(0.0), vec3(any(isnan(hdr)) || any(isinf(hdr))));
|
||||
hdr = max(hdr, vec3(0.0));
|
||||
return FBOUT(vec4(ACESFilm(hdr), x.a));
|
||||
//return FBOUT(vec4(reinhard(x.rgb), x.a));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user