Artifacts in image that dont appear when using reference device

Started by
17 comments, last by jischneider 11 years, 11 months ago
Here's some things to try:

- Make sure none of the settings are overridden in the driver control panel (especially MSAA).
- Make sure there's no errors reported when you use the debug runtimes (for both hardware and ref).
- Make sure you have the latest drivers for your card.
- Try it on hardware from the other manufacturers if possible.

Note that when PIX debugs a pixel / vertex it uses the reference rasterizer to do so. It doesn't tell you what's happening on the actual hardware. It might be worth looking for any values where a small rounding error could give a different colour as output.

Are you aware of the D3D9 pixel centre issues?
Advertisement

  1. As far as I can tell, the settings aren't overriden in my control panel. I did have it set up that way at one point, but I deleted the profile, and I can clearly see there is no antialiasiing on the final render image.
  2. I've tried to get error output from debug runtimes but I can't figure it out. I followed Shawn Hargreaves' guide but I never got any output from those programs. If someone could tell me how to do that for XNA 4.0 I would love to try.
  3. I was using the latest 301 beta drivers for nvidia, I've now downgraded to the latest official drivers (296.10) with no improvement.
  4. I'll try to get someone with an ATI card to test and see if they have the problem as well


I am aware of the pixel/texel issue, and I do have a + halfPixel in the shader. If that was the issue I think it'd happen on the reference device as well. Also I don't think its an issue of a small rounding error. I think the difference between the values was something like .6 vs 1.
I've figured out a few new things. First, the antialiasing was being turned on because of this:


static readonly RasterizerState WireFramedRaster = new RasterizerState { CullMode = CullMode.None, FillMode = FillMode.WireFrame };
static readonly RasterizerState NormalRaster = new RasterizerState { CullMode = CullMode.CullCounterClockwiseFace, FillMode = FillMode.Solid };

...

Engine.Graphics.GraphicsDevice.RasterizerState = !wireFramed ? NormalRaster : WireFramedRaster;



According to the documentation, RasterizerState has multisampling turned on by default. This was actually coming from the skybox render code, not the directional light code. PIX no longer shows SetRenderState(D3DRS_MULTISAMPLEANTIALIAS, TRUE) anywhere in the event log.

This didn't fix the problem however, and now I think its an issue of pixel/texel alignment. I previously had a project where I implemented a deferred shader with point lights using Catalin Zima's tutorial. The tutorial for that had a bug in it which caused this same type of white halo because it had -halfPixel in the shader instead of +halfPixel.

The directional light and deferred renderer code in this project also comes from that same set of tutorials. In Catalin's directional light tutorial, it again has -halfPixel, but this time its in the vertex shader instead of the pixel shader. I've tried changing it to +halfPixel, and I've tried removing it. Neither had any effect. Could someone take a look at the tutorial code and see what's wrong with it?

I've attached a SUB image comparison made with PIX between the color render target and the light render target. It definately seems like an alignment issue based off that.
You are close. If multisampling was active then it was causing some halos (and performance lost), now you have to found your other source of halos.

In my engine I use this in the vertex shader of my directional light: output.position.xy += halfPixel;

Read this: http://drilian.com/2...-texel-offsets/

[size=1]Project page: [size=1]<

[size=1] XNA FINAL Engine[size=1] [size=1]>
I tried output.Position.xy += HalfPixel and output.Position.xy -= HalfPixel in the vertex shader. The problem is still there
So any other ideas? Heres the full directional light shader code, just so its easier to take a look at.



//Used to compute the world-position
float4x4 InvertViewProjection;

//Position of the camera, for specular light
float3 CameraPosition;

//Light Properties
float3 LightDirection;

//Color Properties
float4 DiffuseLightColor;
float4 AmbientLightColor;
float AmbientIntensity;
float DiffuseIntensity;

//Specular Properties
float SpecularPower;
float SpecularIntensity;

//Textures
float2 HalfPixel;


// diffuse color, and specularIntensity in the alpha channel
texture ColorMap;
// normals, and specularPower in the alpha channel
texture NormalMap;
//depth
texture DepthMap;

sampler colorSampler = sampler_state
{
Texture = (ColorMap);
MagFilter = Linear;
MinFilter = Linear;
Mipfilter = Linear;
AddressU = Clamp;
AddressV = Clamp;
};
sampler depthSampler = sampler_state
{
Texture = (DepthMap);
MagFilter = Point;
MinFilter = Point;
Mipfilter = Point;
AddressU = Clamp;
AddressV = Clamp;
};
sampler normalSampler = sampler_state
{
Texture = (NormalMap);
MagFilter = Point;
MinFilter = Point;
Mipfilter = Point;
AddressU = Clamp;
AddressV = Clamp;
};

struct VertexShaderInput
{
float3 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
};

struct VertexShaderOutput
{
float4 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
};

VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
VertexShaderOutput output;
output.Position = float4(input.Position, 1);

//align texture coordinates
output.TexCoord = input.TexCoord - HalfPixel;

return output;
}
float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
//get normal data from the normalMap
float4 normalData = tex2D(normalSampler,input.TexCoord);

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

//get specular power, and get it into [0,255] range]
float specularPower = saturate(normalData.a * SpecularPower) * 255;

//get specular intensity from the colorMap
float specularIntensity = saturate(tex2D(colorSampler, input.TexCoord).a * SpecularIntensity);

//read depth
float depthVal = tex2D(depthSampler,input.TexCoord).r;

//Only the skybox has a depth this high, so stop processing and return white
if(depthVal > 0.999f)
return float4(1, 1, 1, 0);

//compute screen-space position
float4 position;
position.x = input.TexCoord.x * 2.0f - 1.0f;
position.y = -(input.TexCoord.x * 2.0f - 1.0f);
position.z = depthVal;
position.w = 1.0f;

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

//surface-to-light vector
float3 lightVector = -normalize(LightDirection);

//compute diffuse light
float NdL = saturate(dot(normal, lightVector));
float4 diffuseLight = (NdL * DiffuseLightColor * DiffuseIntensity) + (AmbientLightColor * AmbientIntensity);

//reflexion vector
float3 reflectionVector = normalize(reflect(-lightVector, normal));

//camera-to-surface vector
float3 directionToCamera = normalize(CameraPosition - position);

float specularLight = specularIntensity * pow(saturate(dot(reflectionVector, directionToCamera)), specularPower);

return float4(diffuseLight.rgb, specularLight);
}

technique Technique0
{
pass Pass0
{
VertexShader = compile vs_2_0 VertexShaderFunction();
PixelShader = compile ps_2_0 PixelShaderFunction();
}
}
Like I said before I calculated halfpixel using the following code: output.position.xy += halfPixel; But halfpixel is equal to Vector2(-1f / destination.Width, 1f / destination.Height)); Be aware that the first value is negative. I forgot to mention before.

That said: position.y = -(input.TexCoord.x * 2.0f - 1.0f); The x should be y.

The rest seems fine. I you want to improve performance you can avoid the matrix multiplication in the world position calculation. Ask me if you want.

[size=1]Project page: [size=1]<

[size=1] XNA FINAL Engine[size=1] [size=1]>
Oh, good catch on that one. After a little more playing around, it seems its FINALLY working. Along with fixing that, I also tried something new this time. The tutorial computes halfpixel as 0.5/width, 0.5/height, which doesnt match what your article says, so I had tried globally changing that to -1/width, 1/height, but that didn't help. This time I decided to change it only for the directional light shader and it worked (along with the previous changes). Thanks a lot for your help jischneider.

Heres the final, working shader code, in case anyone else runs into this problem


//Used to compute the world-position
float4x4 InvertViewProjection;

//Position of the camera, for specular light
float3 CameraPosition;

//Light Properties
float3 LightDirection;

//Color Properties
float4 DiffuseLightColor;
float4 AmbientLightColor;
float AmbientIntensity;
float DiffuseIntensity;

//Specular Properties
float SpecularPower;
float SpecularIntensity;

//Textures
float2 HalfPixel;


// diffuse color, and specularIntensity in the alpha channel
texture ColorMap;
// normals, and specularPower in the alpha channel
texture NormalMap;
//depth
texture DepthMap;

sampler colorSampler = sampler_state
{
Texture = (ColorMap);
MagFilter = Linear;
MinFilter = Linear;
Mipfilter = Linear;
AddressU = Clamp;
AddressV = Clamp;
};
sampler depthSampler = sampler_state
{
Texture = (DepthMap);
MagFilter = Point;
MinFilter = Point;
Mipfilter = Point;
AddressU = Clamp;
AddressV = Clamp;
};
sampler normalSampler = sampler_state
{
Texture = (NormalMap);
MagFilter = Point;
MinFilter = Point;
Mipfilter = Point;
AddressU = Clamp;
AddressV = Clamp;
};

struct VertexShaderInput
{
float3 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
};

struct VertexShaderOutput
{
float4 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
};

VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
VertexShaderOutput output;
output.Position = float4(input.Position, 1);
output.Position.xy += HalfPixel;

output.TexCoord = input.TexCoord;

return output;
}
float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
//get normal data from the normalMap
float4 normalData = tex2D(normalSampler,input.TexCoord);

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

//get specular power, and get it into [0,255] range]
float specularPower = saturate(normalData.a * SpecularPower) * 255;

//get specular intensity from the colorMap
float specularIntensity = saturate(tex2D(colorSampler, input.TexCoord).a * SpecularIntensity);

//read depth
float depthVal = tex2D(depthSampler,input.TexCoord).r;

//Only the skybox has a depth this high, so stop processing and return white
if(depthVal > 0.999f)
return float4(1, 1, 1, 0);

//compute screen-space position
float4 position;
position.x = input.TexCoord.x * 2.0f - 1.0f;
position.y = -(input.TexCoord.y * 2.0f - 1.0f);
position.z = depthVal;
position.w = 1.0f;

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

//surface-to-light vector
float3 lightVector = -normalize(LightDirection);

//compute diffuse light
float NdL = saturate(dot(normal, lightVector));
float4 diffuseLight = (NdL * DiffuseLightColor * DiffuseIntensity) + (AmbientLightColor * AmbientIntensity);

//reflexion vector
float3 reflectionVector = normalize(reflect(-lightVector, normal));

//camera-to-surface vector
float3 directionToCamera = normalize(CameraPosition - position);

float specularLight = specularIntensity * pow(saturate(dot(reflectionVector, directionToCamera)), specularPower);

return float4(diffuseLight.rgb, specularLight);
}

technique Technique0
{
pass Pass0
{
VertexShader = compile vs_2_0 VertexShaderFunction();
PixelShader = compile ps_2_0 PixelShaderFunction();
}
}
You are welcome.

I don't remember how I reach my formula, but I do remember that I was sure that it was the correct and of course it works very well for me. But if your engine is working why changed, right?

Bye!!!

[size=1]Project page: [size=1]<

[size=1] XNA FINAL Engine[size=1] [size=1]>

This topic is closed to new replies.

Advertisement