Setting up HLSL lighting, rotation & you

Started by
1 comment, last by howie_007 14 years, 1 month ago
My goal is simple, take a 3D object that's in model space, translate it to world space and light it based on where the point light is. This isn't that big a deal until you want to rotate this 3D object on more then 1 axis. It's also odd what you have to do to rotate it on the z axis to have it lit correctly. I'm amazed at all the hoops I need to jump through to get this to work correctly which makes me wonder if I'm doing this right. To just place an object in your world and have it lit correctly, all you need to do is invert it's z. Now to be able to rotate it and still have it lit correctly, you have to swap the order in which you handle it's rotation. So if you rotate the position of your object in the order of z,y,x, then the matrix you provide for the lighting needs to be handled in an opposite order (x,y,z). If you rotate only on the z axis, or a combination there of, you need to invert the z sin entries in your lighting matrix. Oy.[wow] So at this point it all works except if you have a 3D object orbit a point and if the object is rotating on a different axis (or multiple axises) then it's orbit axis. Here's my shader.
//-----------------------------------------------------------------------------
// Effect File Variables
//-----------------------------------------------------------------------------

// Lighting only matrix - rigid body transform
// Rotations in x,y,z order, z pos inverted, z sin inverted
float4x4 worldMatrix;
float4x4 worldMatrixTranspose;

// Pos only matrix
// Rotations in z,y,x order, can include scaling
float4x4 worldViewProjMatrix;
texture material;

//-----------------------------------------------------------------------------
// Vertex Definitions
//-----------------------------------------------------------------------------

// For testing, let's hard code our light position
float3 lightPos = float3(-20.0f, -10.0f, 35.0f);

struct VS_INPUT
{
    float4 position	: POSITION;
	float2 texture0 : TEXCOORD0;
	float3 normal   : NORMAL;
};

struct VS_OUTPUT
{
    float4 position : POSITION;
	float2 texture0 : TEXCOORD0;
    float4 color	: COLOR0;
};

struct PS_OUTPUT
{
	float4 color : COLOR;
};

//-----------------------------------------------------------------------------
// Texture samplers
//-----------------------------------------------------------------------------
sampler textureSampler = 
sampler_state
{
    Texture = <material>;
};

//-----------------------------------------------------------------------------
// Simple Vertex Shader
//-----------------------------------------------------------------------------

VS_OUTPUT v_shader( VS_INPUT IN )
{
    VS_OUTPUT OUT;

	float3 lDir = normalize( lightPos - (float3)mul( IN.position, worldMatrix ) );
	float diffuse = max(0.0, dot(lDir, mul( IN.normal, (float3x3)worldMatrixTranspose )));
	
	// for test sake, add some ambient
	OUT.color = float4( 0.15, 0.15, 0.15, 1.0 );
	
	if( diffuse > 0.0f )
	{
		OUT.color += float4( float3( 0.5, 0.5, 0.5 ) * diffuse, 1.0 );
	}
	 
	OUT.position = mul( IN.position, worldViewProjMatrix );

	OUT.texture0 = IN.texture0;

	return OUT;
}

//-----------------------------------------------------------------------------
// Simple Pixel Shader
//-----------------------------------------------------------------------------

PS_OUTPUT p_shader( VS_OUTPUT IN )
{
    PS_OUTPUT OUT;

	OUT.color = tex2D( textureSampler, IN.texture0 ) * IN.color;

    return OUT;
}

//-----------------------------------------------------------------------------
// Simple Effect (1 technique with 1 pass)
//-----------------------------------------------------------------------------

technique effect0
{
    pass Pass0
    {
		VertexShader = compile vs_2_0 v_shader();
		PixelShader  = compile ps_2_0 p_shader();
    }
}


[Edited by - howie_007 on March 15, 2010 10:36:31 AM]
Advertisement
Quote:My goal is simple, take a 3D object that's in model space, translate it to world space and light it based on where the point light is. This isn't that big a deal until you want to rotate this 3D object on more then 1 axis. It's also odd what you have to do to rotate it on the z axis to have it lit correctly.

I'm amazed at all the hoops I need to jump through to get this to work correctly which makes me wonder if I'm doing this right.

To just place an object in your world and have it lit correctly, all you need to do is invert it's z. Now to be able to rotate it and still have it lit correctly, you have to swap the order in which you handle it's rotation. So if you rotate the position of your object in the order of z,y,x, then the matrix you provide for the lighting needs to be handled in an opposite order (x,y,z).

If you rotate only on the z axis, or a combination there of, you need to invert the z sin entries in your lighting matrix. Oy.[wow]

So at this point it all works except if you have a 3D object orbit a point and if the object is rotating on a different axis (or multiple axises) then it's orbit axis.
Hm, none of that sounds right :-S As you note, you shouldn't have to jump through that many hoops (or any hoops at all, really) to apply simple vertex lighting in world space.

Looking at your shaders, the first thing I noticed was this line:
float diffuse = max(0.0, dot(lDir, mul( IN.normal, (float3x3)worldMatrixTranspose )));
Why are you transforming the normal using the transpose of the world matrix?

If the world transform for the object is a rigid body transform (i.e. rotation and translation only), you should just be able to use the world transform here. For some transform types you would need to use the inverse transpose of the world matrix, but then you'd also need to renormalize the normal in the shader.

What types of transforms are included in the world transform? Is it just rotation and translation? If so, you might try replacing the transpose world matrix with the world matrix in the above line of code and see if that solves the problem.
Quote:Original post by jyk
What types of transforms are included in the world transform? Is it just rotation and translation?
yep.

Quote:Original post by jyk
If so, you might try replacing the transpose world matrix with the world matrix in the above line of code and see if that solves the problem.
I tested it every which way to Sunday. I've also been looking at the ATI fixed function shader as example code. They do a lot of transposes and inversions to their matrixes which is what gave me the idea to try it, but I must admit I don't fully understand why it needs to be done. Through much experimentation, I came up with what is working now.

Below is a code snippet from the ATI fixed function cpp file. I find it hard to follow what they are doing and why. Comments would have been nice.

//render using programmable shaderm_pEffect->SetTechnique(m_pEffect->GetTechniqueByName("basic_with_shader"));D3DXMATRIXA16 compMat, invMat;m_pEffect->SetMatrixTranspose("matView", &m_matView);D3DXMatrixMultiply(&compMat, &m_matWorld, &m_matView);m_pEffect->SetMatrixTranspose("matWorldView", &compMat);D3DXMatrixInverse(&invMat, NULL, &compMat);D3DXMatrixTranspose(&invMat, &invMat);m_pEffect->SetMatrixTranspose("matWorldViewIT", &invMat);D3DXMatrixInverse(&invMat, NULL, &m_matView);D3DXMatrixTranspose(&invMat, &invMat);m_pEffect->SetMatrixTranspose("matViewIT", &invMat);D3DXMatrixMultiply(&compMat, &compMat, &m_matProj);m_pEffect->SetMatrixTranspose("matWorldViewProj", &compMat);m_pEffect->SetMatrixTranspose("matProj", &m_matProj);m_pEffect->SetMatrixTranspose("matWorld", &m_matWorld);

Why would you have to re-normalize a vertex normal? It's being sent to DirectX normalized.

Before switching to shaders, I was using the normal DirectX lighting calls and had the same problem so I had to transform the light position to the object in model space. This was one of the reasons for switching to shaders. In my Java/OpenGL engine, OpenGL handles the lighting of objects correctly without any additional work. It's the one thing I like about OpenGL over DirectX.

This topic is closed to new replies.

Advertisement