Direct3D 10: Shader constants problem

Started by
3 comments, last by DieterVW 13 years, 4 months ago
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 shadermMesh.reset(new Mesh(mDevice, L"models/teapot.dae", L"shaders/light.fx", 0.1f));// Setup material and lightmMtrl.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 :)
------------------------
Advertisement
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
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
------------------------
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
------------------------
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.

This topic is closed to new replies.

Advertisement