HLSL Diffuse Shader w/ Tokamak Physics Engine

Started by
5 comments, last by mike_ix 17 years, 7 months ago
Hi there! Can anyone help me understand why my shader is not lightining my dynamic objects correctly? when I use the fixed function pipline my spheres are lit correctly. when I use my shader the shading seems to rotate with my spheres as they bounce around. It's like as if the positions are being translated after the lighting, even though I know this is not true. You can see some video I took of both cases (using fraps). Video of spheres being animated with Tokamak Physics Engine without my HLSL diffuse shader Video of spheres with diffuse shader Here is my shader:
float4x4 worldViewProj;
float4x4 world;




struct VS_INPUT
{
	float3 position  : POSITION0;
	float3 normal   : NORMAL0;
	//float4 color    : COLOR0;
	float2 texcoord : TEXCOORD0;

};

struct VS_OUTPUT
{
	float4 position : POSITION0;
	//float3 normal   : TEXCOORD1;
	float4 color    : COLOR0;
	float2 texcoord : TEXCOORD0;
};

VS_OUTPUT main( VS_INPUT IN )
{
	VS_OUTPUT vOut;

	float4 v = float4( IN.position.x,
		               IN.position.y,
					   IN.position.z,
					   1.0f );

    vOut.position = mul( v, worldViewProj );

float3 lightPos = float3(1.0, 150.0, 1.0);
float3 posWorld = mul(IN.position, world).xyz; //get the vertex to world space
float3 normal = mul(IN.normal, world).xyz; //get the normal to world space
float3 light = normalize(lightPos - posWorld); //calculating the light direction
float lightIntensity = clamp(dot(normal, light), 0, 1); //calculate the light intensity and clamp it to a range
float3 lightColor = float3(1.0f, 1.0f, 1.0f); //set the light color
float4 color = float4(lightColor * (lightIntensity), 1.0f); //calculate the color of the vertex
color.rgb += 0.3f; //add an ambient color to the shader
vOut.color = clamp(color, 0, 1); //set the color to the output color

	//vOut.color = IN.color;


    vOut.texcoord = IN.texcoord;

    return vOut;
}



Here is how I get the transform from Tokamak:
	neT3 t;

    t = gSpheres[bodyid]->GetTransform(); 
            // Transfer the values to a D3DMATRIX that we can pass to DirectX
    dxTrans = D3DXMATRIX(    t.rot[0][0], t.rot[0][1], t.rot[0][2], 0.0f, 
                        t.rot[1][0], t.rot[1][1], t.rot[1][2], 0.0f, 
                        t.rot[2][0], t.rot[2][1], t.rot[2][2], 0.0f, 
                        t.pos[0],t.pos[1], t.pos[2], 1.0f );
            // Set the world transformation so that we can draw the cube at the correct position
	//theWorld->g_pd3dDevice->SetTransform(D3DTS_WORLD, &dxTrans );
	theWorld->matWorld = dxTrans;



I use the SetTransform method when not using the shader, otherwise I pass the physics matrix to matWorld, then use matWorld (along with the view and projection matrices) to do the calculations in the shader. I have also tried seperating dxTrans from matWorld and passing them seperately to the shader but I got exactly the same results. As a matter of fact, I have now played and toyed and experimented with every possible adjustment I can make to the lighting calculation and suprisingly most of it had no effect at all. It seems to me that transforming the vertices position to world space is happening somewhere else, or in the wrong order, so that the lighting is done in object space, then the object is moved to world space. If this is true then it would explain why the lighting seems to rotate with the sphere, because the sphere is rotating in world space, not object space. But I cannot see how this can be when I look at the code. Anyone? [Edited by - mike_ix on August 19, 2006 1:44:07 PM]
Advertisement

Well, the code you posted seems quite ok. There are several possibilities here.

- your worldviewproj matrix should be fine since the balls are actually on the screen.
- world matrix might not be alright, though there is no evidence that it isn't correct.
- position of the light, is it in world space or do you accidentally transform it by the inverse world matrix ?

- you'll need to post some more code about actually setting the matrices so that we know more.

- one thing which I do notice though:

vOut.position = mul( v, worldViewProj );
float3 posWorld = mul(IN.position, world).xyz;
float3 normal = mul(IN.normal, world).xyz;

The order matters in the mul instruction. mul(Vector,Matrix) isn't same as
mul(Matrix,Vector) and that might produce problems as described. The other operation actually results a vector as if the matrix was transposed.

Check the SDK for details
Hello Demus79, thanks for replying.

Quote:- your worldviewproj matrix should be fine since the balls are actually on the screen.


That's what I think.

Quote:- world matrix might not be alright, though there is no evidence that it isn't correct.


...or, it's not being used at all? I've tried commenting out the lines of code that use the HLSL global variable "world" and I get the same results, as if it doesn't matter that it's there!

Quote:- position of the light, is it in world space or do you accidentally transform it by the inverse world matrix ?


That is a fine queation! the light position is hardcoded into the shader:

float3 lightPos = float3(1.0, 150.0, 1.0);

And I'm sending the matrix to the shader untransposed, and without inversion. AS far as I've tried, if I transpose, inverse, inverse-transpose, or whatever other method you can think of, I always get the same results. As a matter of fact, if I comment out the SetVertexShaderConstantF statement that sets the world matrix I still get the same results, as if the shader is not getting the matrix at all! Or perhaps the matrix is being set to sero or null? I've tried changing the constant registers, and using SetMatrix instead, but still the same. If I change the constant registers to something I know is wrong (say c10 instead of c4) then there is no lighting, so I know that something is being passed. Here is how I'm currently setting the shader:

		D3DXMatrixTranspose( &theWorld->matWorldViewProj , &(theWorld->matWorld * theWorld->matView * theWorld->matProj) );		theWorld->g_pd3dDevice->SetVertexShaderConstantF(0, theWorld->matWorldViewProj, 4);				//D3DXMatrixInverse(&theWorld->matWorldI, NULL, &thePhysics->dxTrans);		//D3DXMatrixTranspose(&theWorld->matWorldIT, &theWorld->matWorldI);		theWorld->g_pd3dDevice->SetVertexShaderConstantF(4, theWorld->matWorld, 4);		theWorld->g_pd3dDevice->SetVertexShader( retrieve(2) );//		D3DXHANDLE handle;//		if(handle = pConstTable->GetConstantByName(NULL,"WORLDVIEWPROJECTION")) {//					MessageBox(NULL, "got handle for WVP!", "Error!",//        MB_ICONEXCLAMATION | MB_OK);			//tempConstTable->SetMatrix(theWorld->g_pd3dDevice,"WORLDVIEWPROJECTION",(D3DXMATRIX*)&theWorld->matWorldViewProj);//		}//		if(handle = pConstTable->GetConstantByName(NULL,"WORLD")) {//								MessageBox(NULL, "got handle for W!", "Error!", //       MB_ICONEXCLAMATION | MB_OK);			//tempConstTable->SetMatrix(theWorld->g_pd3dDevice,"WORLD",(D3DXMATRIX*)&thePhysics->dxTrans);//		}


AS you can see from all of the commented code, I've tried many different methods.


Quote:The order matters in the mul instruction. mul(Vector,Matrix) isn't same as
mul(Matrix,Vector) and that might produce problems as described. The other operation actually results a vector as if the matrix was transposed.


Thanks, but I already tried that. It did have an effect on how the shading appeared (the shade rotated the opposite direction of the rotation of the sphere instead of rotating with it), but it didn't fix the problem.

OK, It's working better now. It actually was the mul order. The only reason it didn't work before is because I had a line of code in the shader:

light = mul(light, world);

which is just silly, so I took it out and added this:

float3 posWorld = mul(world, IN.position).xyz;
float3 normal = mul(world, IN.normal).xyz;

So now the light does not rotate with the sphere (thanks for the help Demus79!), but there is still one more problem. I set the light position to (1.0, 150.0, 1.0), which is towards the far left of the screen in world coordinates, but is directly above the sphere in object coordinates. However, the lighting appears to come from directly above the spheres instead of the far left, which means the lighting is done is object space? how can this be when i clearly transform the vertex to world space before getting the light direction:

float3 lightPos = float3(1.0, 150.0, 1.0);float3 posWorld = mul(IN.position, world).xyz; //get the vertex to world spacefloat3 normal = mul(IN.normal, world).xyz; //get the normal to world spacefloat3 light = normalize(lightPos - posWorld); //calculating the light direction
Eureka!!!

The problem: HLSL nullifies registers that are reassigned to different registers. I'm not actually sure if this is true, but I fixed my problem with this line of code:

float3 posWorld = mul(world, v).xyz;

Apprently, when I assign IN.position to the variable "v", IN.position is made to be NULL (?), so I can no longer use it.

	float4 v = float4( IN.position.x,		               IN.position.y,					   IN.position.z,					   1.0f );


If I use the variable v instead, everything works great! Does anybody know if I'm right, or if this is just a coincidence and is actually due to another reason?
Quote:Original post by mike_ix
The problem: HLSL nullifies registers that are reassigned to different registers. I'm not actually sure if this is true, but I fixed my problem with this line of code:

float3 posWorld = mul(world, v).xyz;

Apprently, when I assign IN.position to the variable "v", IN.position is made to be NULL (?), so I can no longer use it.

*** Source Snippet Removed ***

If I use the variable v instead, everything works great! Does anybody know if I'm right, or if this is just a coincidence and is actually due to another reason?

HLSL will not "nullify" your IN.position if you create a new float4 using it [smile]

With regards to what is really happening, I admit I have lost track of what changes you have made and the current state of your shader. From the looks of things though you were doing...
float3 posWorld = mul(IN.position, world).xyz;
...and are now doing...
float3 posWorld = mul(world, v).xyz;
Notice that your world matrix is not in the same place in both, but of course you have that ordering changed numerous times within this thread so I'm not sure if that is/was the problem [wink]

Regards,
ViLiO
Richard 'ViLiO' Thomasv.net | Twitter | YouTube
Sorry for taking so long to reply.

Quote:Notice that your world matrix is not in the same place in both, but of course you have that ordering changed numerous times within this thread so I'm not sure if that is/was the problem


Yes, that's true, but if you look at my third post (the one that starts "OK, It's working better now") I had already switched the multiplication order, and it merely stopped the the lighting from rotating with the sphere. It did not however transform the lighting to world space. I can only assume that since I used IN.position and got object space, then used "v" and got world space, that the two are not equal. As a matter of fact, IN.position is NULL! v on the other hand is not! if I use:

	float4 v = float4( IN.position.x,		               IN.position.y,					   IN.position.z,					   1.0f );float3 posWorld = mul(world, v).xyz; //this worksINSTEAD OFfloat3 posWorld = mul(world, IN.position).xyz; //this doesn't


I get the correct results. Why, then, is IN.position NULL, or at least not working, and v is? If assigning a variable to a new variable does not nullify the first variable, then why are IN.position and v not equal?

This topic is closed to new replies.

Advertisement