I'm working on a game in XNA 4 and I recently switched to a deferred shading implementation following this guide. A strange white outline is showing up on my models now and I'm not sure whats causing this. I thought maybe it was a lack of precision on the normal render target or the depth render target but increasing them to 64 bits (Rgba64) instead of 32 (Color) didn't help. I also thought it might be some problem with the specular calculation so I tried setting the specular to 0 but that didn't help either. Debugging the pixel in PIX shows the diffuse value is being calculated as nearly white for that pixel. Any ideas? I included a picture of the problem below, it's easier to see it at full size.

Pixel Shader:

float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
    input.ScreenPosition.xy /= input.ScreenPosition.w;

    float2 texCoord = 0.5f * (float2(input.ScreenPosition.x, -input.ScreenPosition.y) + 1) - halfPixel;

    float4 normalData = tex2D(normalSampler, texCoord);

    //Transform normal back into [-1, 1] range
    float3 normal = normalize(2.0f * normalData.xyz - 1.0f);

    float specularPower = normalData.a;
    float specularHardness = tex2D(colorSampler, texCoord).a * 255;
    float depthVal = tex2D(depthSampler, texCoord).r;

    //Compute screen-space position
    float4 position = float4(input.ScreenPosition.xy, depthVal, 1.0f);

    //Transform to world space
    position = mul(position, InvertViewProjection);
    position /= position.w;

    //Surface-to-light vector
    float3 lightVector = lightPosition - position;

    float3 projectedTexCoords = position - lightPosition;
    projectedTexCoords = mul(projectedTexCoords, float3x3(
                            float3(-1, 0, 0),
                            float3(0, 1, 0),
                            float3(0, 0, 1)));

    float distanceToLight = length(lightVector) / lightRadius;
    float shadow = ShadowContribution(projectedTexCoords, distanceToLight);

    float attenuation = saturate(1.0f - distanceToLight);

    lightVector = normalize(lightVector);

    //Compute diffuse light
    float normalDotLight = max(0, dot(normal, lightVector));
    float3 diffuseLight = normalDotLight * Color.rgb;

    float3 reflectionVector = normalize(reflect(-lightVector, normal));
    float3 directionToCamera = normalize(cameraPosition - position);
    float specularLight = specularPower * pow(saturate(dot(reflectionVector, directionToCamera)), specularHardness);

    return shadow * attenuation * lightIntensity * float4(diffuseLight.rgb, specularLight);

Edit: Sorry about the JPG, stackexchange's uploader did that on its own. Roy T: I did actually reference your XNA4 conversion of the tutorial for the parts that changed from XNA3. Since I didn't make any big changes to the code I thought maybe this bug existed in the original but was just impossible to see with so many lights all moving around, so I removed all but one light and the bug showed up again (look near the lizard's elbow)

Here are the GBuffer contents for my scene:

Color Buffer: Depth Buffer: Normal Buffer: Final Render:


I'm starting to suspect the problem is CombineFinal.fx when it samples the color map. This is the computation for a properly colored pixel right next to a white pixel:

diffuseColor        : (0.435, 0.447, 0.412)
diffuseLight        : (1.000, 1.000, 0.902)
light               : (1.000, 1.000, 0.902, 0.000)
PixelShaderFunction : (0.435, 0.447, 0.371, 1.000)
specularLight       : 0.000

And this is the output from an improperly colored white pixel directly next to it:

diffuseColor        : (0.824, 0.792, 0.741)
diffuseLight        : (1.000, 1.000, 0.902)
light               : (1.000, 1.000, 0.902, 0.000)
PixelShaderFunction : (0.824, 0.792, 0.669, 1.000)
specularLight       : 0.000

The diffuseColor is the only difference, and this color is taken directly from the color map. Perhaps there is a slight error in the computation of the texture coordinate to sample from?

Lighting Pass:

  • 4
    \$\begingroup\$ Please no not jpeg compress a screenshot showing a subtle graphical glitch. People will want to see the problem in perfect reproduction. \$\endgroup\$ Commented Sep 11, 2011 at 9:02
  • \$\begingroup\$ Well, the jpeg compression actually lets the glitch through quite nicely I think, one pixel only and only when certain things are reunited (big diff in Z-depth and the lighting is (quite) high on all pixels). Good luck, deferred rendering is quite technical. \$\endgroup\$
    – Valmond
    Commented Sep 11, 2011 at 20:00
  • 2
    \$\begingroup\$ Not sure if it helps, but you're using a very old version of the code/tutorial, Catalin Zima's actual code/tutorial is now at: catalinzima.com/tutorials/deferred-rendering-in-xna and a sanctioned XNA4.0 version is located here: roy-t.nl/index.php/2010/12/28/… \$\endgroup\$
    – Roy T.
    Commented Sep 11, 2011 at 20:32
  • \$\begingroup\$ Also as always with debugging deferred rendering, could you post screenshots from all buffers? It's probably in the lighting buffer, but it could be something else. \$\endgroup\$
    – Roy T.
    Commented Sep 11, 2011 at 20:34
  • \$\begingroup\$ My instinct is that it will be an issue with the way part of the algorithm handles an exactly 90 degree angle, but I'll have to play with the ode after work to look into it further. \$\endgroup\$ Commented Sep 11, 2011 at 22:13

2 Answers 2


I had this white outline or "halo" around my models too, when I started on my own deferred renderer. The problem was that the texture offset value for overlaying the render targets was not setup correctly. Some textures needed to be set to +0.5 pixels for the offset instead of -0.5 pixels.

It was only after tweaking values of some of the texture offsets that the outlines were gone. Most likely it's in one of the lighting shaders.

Edit: by the way I also learned from Catalin Zima's tutorial which is a branch off of your example, so it should work.

  • \$\begingroup\$ Yup, that was it. I changed it to +halfPixel for all the coordinate sampling in the light shader and the halo is gone now. \$\endgroup\$
    – Telanor
    Commented Sep 23, 2011 at 19:58

I'm not sure exactly what's going on, but a couple of things to double-check:

  1. Make sure you've got texture filtering turned off when sampling the G-buffers - no bilinear or anisotropic or anything.

  2. I see you're adding a half-pixel offset to the texture coordinates; double-check that that's right. I'm not familiar enough with XNA to know what the right offset should be, but you should be able to check it by writing a shader that samples one of the G-buffers and just outputs the sample. Then flip back and forth between this and displaying the G-buffer directly on-screen. If you've got the correct offset, you should see no differences, not even a single pixel.

  • \$\begingroup\$ I went and changed everything related to the GBuffer to POINT (I assume that disables filtering?) and its still the same. About your second point, wouldn't outputting the GBuffer directly be the exact same: make a shader that just outputs the data? Can you elaborate on that a bit more? \$\endgroup\$
    – Telanor
    Commented Sep 19, 2011 at 21:52
  • \$\begingroup\$ Well, how did you generate the screenshots of the G-buffer above? I presume you call some sort of built-in API function, to copy the pixels one-to-one from the G-buffer to the screen (back buffer)? (I don't know XNA so I don't know what it would be called.) I'm saying, compare the output of the built-in one-to-one copy function to the output of a pixel shader written to do a one-to-one copy...that way you can be sure the texture coordinates are exactly correct and you really are getting one-to-one sampling. \$\endgroup\$ Commented Sep 19, 2011 at 22:06

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .