Sign in to follow this  
SAL1

Direct3D 10: Shader constants problem

Recommended Posts

Good day to you all.

I'm trying to get into Direct3D 10 and build a framebuffer on the way, my current goal is to write a simple per-vertex lighting shader using HLSL/fx4 and cbuffers.
But there is a strange problem I've been trying to figure-out since yesterday. Simply put, some of the constant values I set in C++ are somehow not the actual values that get to the shader, this specifically happens when setting light constants which is a struct in it's own cbuffer.

My shader currently looks like this:

struct PointLight
{
float3 pos;
float4 ambient;
float4 diffuse;
float4 specular;
float3 att;
float range;
};

struct Material
{
float4 diffuse;
float4 specular;
float power;
};

cbuffer cbPerFrame
{
float3 gEyePos;
};

cbuffer cbPerObject
{
float4x4 gWorld;
float4x4 gWVP;
Material gMaterial;
};

cbuffer cbPerLight
{
PointLight gLight;
};

struct VS_IN
{
float3 posL : POSITION;
float3 normal : NORMAL;
float2 tex : TEXCOORD0;
};

struct VS_OUT
{
float4 posH : SV_POSITION;
float4 color : COLOR;
};

float4 lightPoint(in PointLight light, float3 posL, float3 normal)
{
float3 dir = normalize(light.pos - posL);
float d = distance(light.pos, posL);

// Diffuse term
float kd = max(dot(dir, normal), 0.0f);

// Specular term
float3 v = normalize(gEyePos - posL);
float3 r = reflect(-dir, normal);
float ks = pow(saturate(kd) * max(dot(v, r), 0.0f), gMaterial.power);

return ((light.ambient) + ((light.diffuse * gMaterial.diffuse) * kd) + ((light.specular * gMaterial.specular) * ks)) / (dot(light.att, float3(1.0f, d, d * d)));
}

VS_OUT LightVS(VS_IN input)
{
VS_OUT output;
output.posH = mul(float4(input.posL, 1.0f), gWVP);
output.color = lightPoint(gLight, input.posL, input.normal);
// The value in gLight.att is not the same value I set in C++!
//output.color = float4(1.0f, gLight.att);
return output;
}

float4 LightPS( float4 posH : SV_POSITION,
float4 color : COLOR) : SV_TARGET
{
return color;
}

technique10 LightTech
{
pass P0
{
SetVertexShader(CompileShader(vs_4_0, LightVS()));
SetGeometryShader(NULL);
SetPixelShader(CompileShader(ps_4_0, LightPS()));
}
}







I set the values in C++ like this:

// Load collada file and set experimental shader
mMesh.reset(new Mesh(mDevice, L"models/teapot.dae", L"shaders/light.fx", 0.1f));

// Setup material and light
mMtrl.diffuse = D3DXVECTOR4(0.6f, 0.4f, 0.5f, 1.0f);
mMtrl.specular = D3DXVECTOR4(1.0f, 0.5f, 0.5f, 0.5f);
mMtrl.power = 2.0f;

mPointLight.ambient = D3DXVECTOR4(1.0f, 0.1f, 0.1f, 0.1f);
mPointLight.diffuse = D3DXVECTOR4(1.0f, 1.0f, 1.0f, 1.0f);
mPointLight.specular = D3DXVECTOR4(1.0f, 1.0f, 1.0f, 1.0f);
mPointLight.att = D3DXVECTOR3(1.0f, 0.0f, 0.0f);
mPointLight.pos = D3DXVECTOR3(0.0f, 5.0f, -10.0f);
mPointLight.range = 40.0f;

mMesh->getEffect()->setMaterial("gMaterial", mMtrl);
mMesh->getEffect()->setObject<PointLight>("gLight", mPointLight);







And here is the definition of PointLight structure in C++ (identical to the one in shader):

struct PointLight
{
PointLight()
{
memset(this, 0, sizeof(PointLight));
}

D3DXVECTOR3 pos;
D3DXVECTOR4 ambient;
D3DXVECTOR4 diffuse;
D3DXVECTOR4 specular;
D3DXVECTOR3 att;
float range;
};







The strange thing is that setting Material constants works perfectly, and using a very similar shader for directional lights works perfectly too! I'm having this problem only with PointLight for some reason..
At first glance, I thought it was a packing issue, I tried to specify packing manually using register and packoffset, it didn't work and I'm not sure I did it right to begin with.
Did I miss something?

Thanks for any help :)

Share this post


Link to post
Share on other sites
I'm guessing you have to add a constant after the position constant, like this:

In your shader:

struct PointLight
{
float3 pos;
float pad;
float4 ambient;
float4 diffuse;
float4 specular;
float3 att;
float range;
};





And also in C++:



struct PointLight
{
PointLight()
{
memset(this, 0, sizeof(PointLight));
}

D3DXVECTOR3 pos;
D3DXVECTOR4 ambient;
D3DXVECTOR4 diffuse;
D3DXVECTOR4 specular;
D3DXVECTOR3 att;
float range;
};



Because vectors from C++ to HLSL are packed in 4D vectors: http://msdn.microsoft.com/en-us/library/bb509632%28VS.85%29.aspx

Share this post


Link to post
Share on other sites
According to the packing rules for constants. There shouldn't be a problem, it's true that position is float3, but the constant next to it `ambient` is float4 which should be packed in a new constant vector.
The problem seems to take place around attenuation constant `att`, the remaining constants correctly get to the shader, which I find bizarre.

I just looked at a similar shader from the SDK samples, they define light constants like this:

struct Light
{
float4 Position;
float4 Diffuse;
float4 Specular;
float4 Ambient;
float4 Atten;
};


They seem to be hacking it themselves :D
Well, I guess I'll try doing it like the SDK and see what happens.

Thanks for replying Aqua Costa

Share this post


Link to post
Share on other sites
Seems to work fine, using this structure:

struct PointLight
{
float4 pos;
float4 ambient;
float4 diffuse;
float4 specular;
float3 att;
float range;
};



So I only changed pos to float4 like Aqua Costa adviced.
I don't understand the behavior though, according to packing rules it shouldn't matter whether `pos` is float3 or float4, because ambient would be packed in a new vector either way.

Now I know what this guy was thinking about..
thinking man

Share this post


Link to post
Share on other sites
When I look at the D3DX10Math.h file I don't see anything that would make D3DXVECTOR3 or D3DXVECTOR4 have any special alignment. Neither contain the special __m128 intrinsic type and they don't have __align(16) on them either. That means the C++ compiler can pack them tightly and so the C++ compiler is going to build a type that has no 4 byte padding between the vec3 and the vec4 - which is going to shift all of your values and mean that they won't get uploaded correctly.

The HLSL compiler enforces proper alignment so your structure in HLSL can remain a float3. HLSL and c++ have different rules. They don't pack structures teh same way. In order to get a similar alignment between the two you will have to take special steps. You just need to fix your c++ version.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this