Jump to content
  • Advertisement
Sign in to follow this  
lordikon

Shader divide by zero?

This topic is 2572 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I am trying to implement a normal-mapped, textures, specular and ambient shader. Within my vertex shader I'm getting a divide by zero error on the line below that is in bold:

VertexToPixel GenericTransform_VS(VertexInput input)
{
VertexToPixel output;

// Transform vertex by world-view-projection matrix
output.position = mul(input.position, WorldViewProjection);
output.WorldPos = mul(input.position, World);

float4 lightDir = (0.0f, 0.7f, 0.0f);

float3x3 worldToTangentSpace;
worldToTangentSpace[0] = mul(normalize(input.tangent), World);
worldToTangentSpace[1] = mul(normalize(input.binormal), World);
worldToTangentSpace[2] = mul(normalize(input.normal), World);

output.lightDir = normalize( mul( worldToTangentSpace, lightDir ) );
output.viewDir = normalize( mul( worldToTangentSpace, CameraPos - output.WorldPos ) );

output.texCoords = input.texCoords;

output.ClipDistances = dot(input.position, ClippingPlane);

return output;
}

I get two warnings and an error on the line in bold, but only if the lightDir's Z component is zero. It doesn't give this error if the X or Y components are zero. The warning and error are this:

(126,18): warning X4008: floating point division by zero
(126,18): warning X4008: floating point division by zero
(126,18): error X4579: NaN and infinity literals not allowed by shader model

The only division I see on that line would be within the normalize method, but it shouldn't run into that problem unless the lightDir vector was all zeros. It's almost as if it only cares about the Z component and the X and Y are treated as zero no matter what I do. This explains why I'm getting another issue, which is that the Z variable is the only one that seems be affecting lighting of my model, my changes to X and Y are completely ignored.

Here's the vertex shader input and output structures, in case it helps:


struct VertexInput
{
float4 position : POSITION;
float2 texCoords : TEXCOORD0;
float3 normal : NORMAL0;
float3 binormal : BINORMAL0;
float3 tangent : TANGENT0;
};

struct VertexToPixel
{
float4 position : POSITION;
float2 texCoords : TEXCOORD0;
float3 lightDir : TEXCOORD1;
float3 viewDir : TEXCOORD2;
float4 ClipDistances : TEXCOORD3;
float4 WorldPos : TEXCOORD4;
};



Thanks in advance on any help.

Share this post


Link to post
Share on other sites
Advertisement
f[color=#1C2837][size=2]loat4 lightDir = (0.0f, 0.7f, 0.0f);
[color=#1C2837][size=2]

[color=#1C2837][size=2]should it be
[color=#1C2837][size=2]

f[color=#1C2837][size=2]loat4 lightDir = (0.0f, 0.7f, 0.0f, 0.0f);
[color=#1C2837][size=2]

[color="#1c2837"]just a wild guess, never written shaders in HLSL

Share this post


Link to post
Share on other sites
Hi,
As suggested by bwhiting, [font="Courier New"]lightDir.w[/font] must be explicitly set to zero, since [font="Courier New"]lightDir [/font]is a vector.


I also think it's a problem of normalizing a null vector :
[font="Courier New"]lightDir [/font]is obviously not null, so the problem can come from your [color="#1C2837"][font="Courier New"]worldToTangentSpace [/font]matrix : it probably transforms [font="Courier New"]lightDir [/font]into a null vector

[color="#1C2837"]Maybe you can check this [font="Courier New"][color="#1C2837"]worldToTangentSpace [/font][color="#1C2837"]matrix ?

[color="#1C2837"]Nico wink.gif

EDIT : Personally, I transform tangent, binormal and normal vectors (into world space) in the vertex shader;
in the pixel shader, I just gather the 3 vectors into a 'TBN' matrix, and compute the normal accordingly

EDIT : Could you post your vertex shader code so that we can see how tangent, normal, binormal are computed ?

Share this post


Link to post
Share on other sites

Hi,
As suggested by bwhiting, [font="Courier New"]lightDir.w[/font] must be explicitly set to zero, since [font="Courier New"]lightDir [/font]is a vector.


I also think it's a problem of normalizing a null vector :
[font="Courier New"]lightDir [/font]is obviously not null, so the problem can come from your [color="#1C2837"][font="Courier New"]worldToTangentSpace [/font]matrix : it probably transforms [font="Courier New"]lightDir [/font]into a null vector

[color="#1C2837"]Maybe you can check this [font="Courier New"][color="#1C2837"]worldToTangentSpace [/font][color="#1C2837"]matrix ?

[color="#1C2837"]Nico wink.gif

EDIT : Personally, I transform tangent, binormal and normal vectors (into world space) in the vertex shader;
in the pixel shader, I just gather the 3 vectors into a 'TBN' matrix, and compute the normal accordingly

EDIT : Could you post your vertex shader code so that we can see how tangent, normal, binormal are computed ?


The tangent, binormal, and normal are computed in a content processor and stored within the vertex buffer. That part is done by XNA, but the content processor was written custom so there could theoretically be a problem there. However I have taken a look at the data in each vertex and all three vectors appear to be correct and orthogonal.

The problem with the divide-by-zero was indeed that I wasn't defining a .w component. That's what I get for programming at 2am. I have thought about computing the normal in the pixel shader, but as pixel shaders tend to usually be a game's bottleneck I first try and put everything I can into the vertex shader.

EDIT: There are still other things wrong with my shader. I guess the main problem is that I'm not a graphics programmer, but since I have no graphics programmer to donate time to this open-source project I'm doing what I can. I managed to get shadow mapping and reflective/refractive water working so far, which is a big feat for me. :P

EDIT: I'm working off of this tutorial though: http://digitalerr0r.wordpress.com/2009/03/23/xna-shader-programming-tutorial-4-normal-mapping/

Share this post


Link to post
Share on other sites
Your actual problem here isn't that you didn't include the w component (although that is a problem, since it is illegal to assign a smaller vector type to a larger vector type). The problem is that you're not using a float4 constructor or an initializer list:

float4 ldir = float4(0.0f, 0.7f, 0.0f, 0.0f); // okay
float4 ldir = { 0.0f, 0.7f, 0.0f, 0.0f }; // okay
float4 ldir = (0.0f, 0.7f, 0.0f, 0.0f) // not okay!

If I try to compile a shader with that last version, I get this warning (it's an error for me since I have warnings treated as errors):

error X3081: comma expression used where a vector constructor may have been intended

It's possible that you're not getting this warning due to some bug in the compiler, or because XNA is still using an older version of the shader compiler (I'm not sure if this is true or not).

As for the other problem, you should get this error if you try to assign a float3 to a float4:

error X3017: cannot implicitly convert from 'const float3' to 'float4'

Share this post


Link to post
Share on other sites
MJP, not sure why this wouldn't work, it worked fine for me, no errors:
[color="#000000"]float4 ldir [color="#666600"]= [color="#666600"]([color="#006666"]0.0f[color="#666600"], [color="#006666"]0.7f[color="#666600"], [color="#006666"]0.0f[color="#666600"],[color="#000000"] 1[color="#006666"].0f[color="#666600"])

[color="#666600"]I did have to change the W component to 1.0f however to get rid of my divide by zero error.

[color="#666600"]In the end that shader just didn't seem to work right at all, I ended up moving the matrix calculations to the pixel shader. Anyway, here's the final result:

[color="#666600"]float4x4 LightViewProjection : LIGHTVIEWPROJECTION;
float4x4 WorldViewProjection : WORLDVIEWPROJECTION;
float4x4 World : WORLD;
float4x4 view : VIEW;

float4 LightDir : LIGHT_DIRECTION;
float4 CameraForward : VIEW_FORWARD;
float4 CameraPos : VIEW_POS;

float SpecularPower : SPECULAR_POWER;
float4 SpecularColor : SPECULAR_COLOR;

float4 AmbientColor : AMBIENT_COLOR;

texture diffuseMap : DIFFUSE_MAP;
texture normalMap : NORMAL_MAP;

bool UsingClippingPlane : USING_CLIPPING_PLANE;
float4 ClippingPlane : CLIPPING_PLANE;

float DepthBias = 0.0015f;

sampler diffuseSampler = sampler_state
{
Texture = (diffuseMap);
ADDRESSU = CLAMP;
ADDRESSV = CLAMP;
MAGFILTER = LINEAR;
MINFILTER = LINEAR;
MIPFILTER = LINEAR;
};

sampler normalSampler = sampler_state
{
Texture = (normalMap);
ADDRESSU = CLAMP;
ADDRESSV = CLAMP;
MAGFILTER = LINEAR;
MINFILTER = LINEAR;
MIPFILTER = LINEAR;
};

Texture ShadowMap : SHADOW_MAP;
sampler ShadowMapSampler = sampler_state { texture = ; MinFilter = POINT; MagFilter = POINT;
MipFilter = NONE; AddressU = Clamp;
AddressV = Clamp; AddressW = Wrap; };

float4 ComputeShadowColor(float4 worldPos, float4 Color)
{
// Find the position of this pixel in light space
float4 lightingPosition = mul(worldPos, LightViewProjection);

// Find the position in the shadow map for this pixel
float2 ShadowTexCoord = 0.5 * lightingPosition.xy /
lightingPosition.w + float2( 0.5, 0.5 );
ShadowTexCoord.y = 1.0f - ShadowTexCoord.y;

// Get the current depth stored in the shadow map
float4 shadowInfo = tex2D(ShadowMapSampler, ShadowTexCoord);
float shadowdepth = shadowInfo.r;
float shadowOpacity = 0.5f + 0.5f * (1 - shadowInfo.g);

// Calculate the current pixel depth
// The bias is used to prevent folating point errors that occur when
// the pixel of the occluder is being drawn
float ourdepth = (lightingPosition.z / lightingPosition.w) - DepthBias;

// Check to see if this pixel is in front or behind the value in the shadow map
if ( shadowdepth < ourdepth)
{
// Shadow the pixel by lowering the intensity
Color *= float4(shadowOpacity, shadowOpacity, shadowOpacity, 1);
};

return Color;
}

struct VertexInput
{
float4 position : POSITION;
float2 texCoords : TEXCOORD0;
float3 normal : NORMAL0;
float3 binormal : BINORMAL0;
float3 tangent : TANGENT0;
};

struct VertexToPixel
{
float4 position : POSITION;
float2 texCoords : TEXCOORD0;
float3 lightDir : TEXCOORD1;
float3 viewDir : TEXCOORD2;
float4 ClipDistances : TEXCOORD3;
float4 WorldPos : TEXCOORD4;
};

VertexToPixel VertexShaderFunction(VertexInput input)
{
VertexToPixel output;

// Transform vertex by world-view-projection matrix
output.position = mul(input.position, WorldViewProjection);
output.WorldPos = mul(input.position, World);

float3 viewPos = mul(-view._m30_m31_m32, transpose(view));

float3 viewDir = viewPos - output.WorldPos.xyz;

float3 normNormal = normalize(input.normal);
float3 normBinormal = normalize(input.binormal);
float3 normTangent = normalize(input.tangent);

output.viewDir.x = dot(normTangent, viewDir);
output.viewDir.y = dot(normBinormal, viewDir);
output.viewDir.z = dot(normNormal, viewDir);

output.lightDir.x = dot(normTangent, -LightDir);
output.lightDir.y = dot(normBinormal, -LightDir);
output.lightDir.z = dot(normNormal, -LightDir);

output.texCoords = input.texCoords;

output.ClipDistances = dot(input.position, ClippingPlane);

return output;
}

float4 PixelShaderFunction(VertexToPixel input) : COLOR0
{
float4 diffuseColor = tex2D(diffuseSampler, input.texCoords);
float3 normal = normalize((tex2D(normalSampler, input.texCoords).xyz * 2.0f) - 1.0f);

float3 normLightDir = normalize(input.lightDir);
float3 normViewDir = normalize(input.viewDir);

float nDotL = dot(normal, normLightDir);

float3 refl = normalize(((2.0f * normal) * nDotL) - normLightDir);
float rDotV = max(dot(refl, normViewDir), 0);

float4 retColor = (AmbientColor * diffuseColor + (diffuseColor * rDotV) + (SpecularColor * pow(rDotV, SpecularPower)));
float4 finalColor = ComputeShadowColor(input.WorldPos, retColor);

if ( UsingClippingPlane )
{
clip(input.ClipDistances);
}

return float4(finalColor.xyz, 1.0f);
}

technique NormalMapSpecular
{
pass Single_Pass
{
VertexShader = compile vs_1_1 VertexShaderFunction();
PixelShader = compile ps_2_0 PixelShaderFunction();
}
}



[color="#666600"]The result is a normal mapped, textured object with specularity that can receive shadows cast onto it. There is more to the shader, which is the part that allows this object to also cast shadows.

Share this post


Link to post
Share on other sites

MJP, not sure why this wouldn't work, it worked fine for me, no errors:
[color="#000000"]float4 ldir [color="#666600"]= [color="#666600"]([color="#006666"]0.0f[color="#666600"], [color="#006666"]0.7f[color="#666600"], [color="#006666"]0.0f[color="#666600"],[color="#000000"] 1[color="#006666"].0f[color="#666600"])


It doesn't work, because it doesn't do what you want it to do. That bit of code there makes use of the comma operator, which in HLSL works like the C++ comma operator. You end up assigning 1.0f to every component in the vector. It's still valid HLSL (which is why you get a warning and not an error from the compiler), but it's most definitely not what you want to do here.The reason you got a divide by 0 error is because in your previous code a 0 would get assigned to all 4 components.

Share this post


Link to post
Share on other sites

MJP, not sure why this wouldn't work, it worked fine for me, no errors:
[color="#000000"]float4 ldir [color="#666600"]= [color="#666600"]([color="#006666"]0.0f[color="#666600"], [color="#006666"]0.7f[color="#666600"], [color="#006666"]0.0f[color="#666600"],[color="#000000"] 1[color="#006666"].0f[color="#666600"])

[color="#666600"]I did have to change the W component to 1.0f however to get rid of my divide by zero error.

Which compile flags do you use when you compile your shader?

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!