"Spotty" Bloom and HDR Artifacts (Deferred)

Started by
4 comments, last by trock3155 11 years, 8 months ago
I'll start out by saying I worked from MJPs oldish example of HDR: http://www.xnainfo.com/content.php?content=28

Note that without the HDR postprocess everyone looks just fine. In my code, I am using HdrBlendable surface formats for my Color and Lightmaps, which are then combined into a final RenderTarget, also with HdrBlendable as its surface type, before being passed off to the HDR postprocessor

My results can be seen in the below video (where a single directional light is being used at an intensity of 5). You can see the bloom effect is very "spotty", changing drastically when the camera angle changes. You can also see near the roof of each house a super-dark area, that almost looks like noise. Note that the super-dark areas appear even if I pull bloom out of the equation altogether.

http://img341.imageshack.us/img341/5366/rxtfsmkqvrfuypnechfltu.mp4

I think my problem is most likely due to changing a majority of the samplers from MJPs project to use Point sampling, since XNA doesnt allow linear filtering of HdrBlendable RenderTargets, however I tried implementing some manual linear filtering in my shaders, and not only did they slow things down terribly, they didn't seem to solve the problem.

Anyone have an idea of what is causing these visual glitches? I can provide any code necessary, but here is the ToneMapping PS:
\
float3 ToneMap(float3 vColor)
{
// Get the calculated average luminance
float fLumAvg = tex2D(PointSampler1, float2(0.5f, 0.5f)).r;

//return float3(fLumAvg, fLumAvg, fLumAvg);
// Calculate the luminance of the current pixel
float fLumPixel = dot(vColor, LUM_CONVERT);
// Apply the modified operator (Eq. 4)
float fLumScaled = (fLumPixel * g_fMiddleGrey) / fLumAvg;
float fLumCompressed = (fLumScaled * (1 + (fLumScaled / (g_fMaxLuminance * g_fMaxLuminance)))) / (1 + fLumScaled);
return fLumCompressed * vColor;
}
float4 ToneMapPS ( in float2 in_vTexCoord : TEXCOORD0,
uniform bool bEncodeLogLuv ) : COLOR0
{
// Sample the original HDR image
float4 vSample = tex2D(PointSampler0, in_vTexCoord);
float3 vHDRColor;
if (bEncodeLogLuv)
vHDRColor = LogLuvDecode(vSample);
else
vHDRColor = vSample.rgb;

// Do the tone-mapping
float3 vToneMapped = ToneMap(vHDRColor);

// Add in the bloom component
float3 vColor = vToneMapped + tex2D(LinearSampler2, in_vTexCoord).rgb * g_fBloomMultiplier;
return float4(vColor, 1.0f);
}
Advertisement
The values being blurred are too intense.
Either your lighting shader is producing specular values of abnormally strong intensities (which you would never have seen until now because they would have been capped at 1.0) or your bright-pass shader is doubling intensities.

#2 can happen when your bright-pass shader simply stores the values above the cut-off point raw. This creates blooms that range from 0.8 (for example) and up instead of from 0.0 and up.
Basically-
Wrong:
void Main(
in vec4 _vInPos : POSITION0,
in vec2 _vIn2dTex0 : TEXCOORD0,
out vec4 _vOutColor : COLOR ) {
// Compute the vAverage of the 4 necessary samples.
vec4 vAverage = vec4( 0.0, 0.0, 0.0, 0.0 );
for( int I = 0; I < 4; I++ ) {
vAverage += tex2d( g_sSampler2dTex0, _vIn2dTex0 + vec2( g_vDowns.x, g_vDowns.y ) );
}
vAverage *= 0.25;
// Output the adjusted color to the render target.
if ( vAverage < g_fThresh ) { vAverage = vec4( 0.0, 0.0, 0.0, 0.0 ); } // *** WRONG.
_vOutColor.w = 1.0;
}





Right:
void Main(
in vec4 _vInPos : POSITION0,
in vec2 _vIn2dTex0 : TEXCOORD0,
out vec4 _vOutColor : COLOR ) {
// Compute the vAverage of the 4 necessary samples.
vec4 vAverage = vec4( 0.0, 0.0, 0.0, 0.0 );
for( int I = 0; I < 4; I++ ) {
vAverage += tex2d( g_sSampler2dTex0, _vIn2dTex0 + vec2( g_vDowns.x, g_vDowns.y ) );
}
vAverage *= 0.25;
vAverage.rgb -= g_fThresh; // ************ IMPORTANT.
// Output the adjusted color to the render target.
_vOutColor.rgb = max( vAverage.rgb, vec3( 0.0, 0.0, 0.0 ) );
_vOutColor.w = 1.0;
}



#2 is easy to check, so once you have verified this is not the cause, examine your specular lighting calculations.


The dark areas are caused by NaN.
Since they are not part of the blur, and are “on top” of the blur, they are created afterwards.
If they were there in the original image, they would propagate in your luminance pass and give you fully NaN luminance, which would then propagate over the entire screen causing it to turn fully black, so it is not part of your original render.
So look around in that area for any possible divisions by 0 etc.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

You are definitely right about the strong intensities causing the bloom artifacts; if I turn the bloom threshold down things work just fine at lower intensities.

I think the problem that I'm trying to overcome is that i must make my light intensities 5-10 times higher than I did prior to implementing HDR to get the same visibility levels. Literally, in order for me to get the same scene brightness from a directional light that I got with LDR set at ".7" intensity, I must set the same lights with my HDR potprocess to 4 or 5.

Is this normal? How is this best countered given my tonemapping algorithms above? This will be especially problematic if I have profiles that allow you to turn off HDR rendering, where things will be unbelievably bright.

My luminance test also seems to be outputting a full white color no matter how dark the screen is (when I map the have my renderer output the average luminance sampled in the tonemap function). Is that normal (perhaps due to the larger color range of the actual HDR sample than I can output from the renderer? Even if that was the case, I would think and unlit, black scene would produce something more gentle than what looks like full white.
The first problem is caused by the second problem.
Normally you do not need to adjust your lighting values—you only need to adjust them if you changed your lighting model to BRDF’s.

Thanks to luminance adjustments, you can use the same values for your lights and whether the overall result is darker or brighter, it will be adjusted back into the normal range.

However your luminance has a bug causing it to be mostly or entirely white, causing the scene to be adjusted into the darker range, causing you to use higher-intensity lights.
The bug is in your luminance shaders. You should switch back to normal lighting ranges.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

Alright, so I found the error in my luminance code that was causing it to bug and out and make everything super dark (it always thought the previous frame was max intensity white, as suspected).

I can now use my normal light intensities to get similar results with and without HDR, which is awesome.

However, the ultra-black (or burnt out) areas of the frame are even more pronounced and more common, and the entire scene is actually worse off than it was before. I have messed with my specularity, but that alone does not seem to be causing it. I am thinking maybe it is my normals, but I'm not sure? In any case, take a look at what I'm seeing now, and let me know if you have any thoughts:

http://img15.imagesh...fslgfpwmwbb.mp4

Thanks!
Alright, so it looks like my shaders are not properly downscaling the render target target that contains the luminance data. Everything seems correct until I get to the final single pixel target. At that point, I am (seemingly) only getting a 1 pixel sample from my previous/larger RenderTarget (not totally sure which pixel it is pulling though).

It's odd, because they all scale down just fine, and I can see in my luminance chain render targets that they are showing light where my screen is lit (albeit in mega-low resolution).

I have to figure out why my final downscale shader is not properly sampling each pixel it needs to before it outputs the current frame's luminance. Below is my scaling shader code. Note that this function should be taking in 5 samples total as a sort of manual linear filter (see below)


float4 vColor = 0;
for (int x = 0; x < 4; x++)
{
for (int y = 0; y < 4; y++)
{
float2 vOffset;
vOffset = float2(g_vOffsets[x], g_vOffsets[y]) / g_vSourceDimensions;
float4 vSample = tex2D(PointSampler0, in_vTexCoord + vOffset);
if (bEncodeLogLuv)
vSample = float4(LogLuvDecode(vSample), 1.0f);
vColor += vSample;
}
}
vColor /= 16.0f;

if (bEncodeLogLuv)
vColor = LogLuvEncode(vColor.rgb);

if (bDecodeLuminance)
vColor = float4(exp(vColor.r), 1.0f, 1.0f, 1.0f);

return vColor;

This topic is closed to new replies.

Advertisement