Constructing a Light Transform Matrix

bcloward

Newcomer
I'm trying to write an HLSL FX shader that will run in 3ds Max that will allow a model to cast projected shadows on itself. I've got the whole thing written, but I'm having some issues with the light transform matrix. Max doesn't allow me to pull in a pre-computed transform matrix for the light, so I have to construct it in the vertex shader. I know that it's a big waste to do this per-vertex instead of per-frame, but it's the only way possible to get it working in Max.

I'm looking at chapter 18 of "Shaders for Game Programmers and Artists" and it says that I can construct the light transform Matrix like this:

float3 dirZ = -normalize(lightPosition);
float3 up = float3(0,0,1);
float3 dirX = normalize(cross(up, dirZ));
float3 dirY = normalize(cross(dirZ, dirX));

then I build the matrix like this:

float4x4 lightXf;
lightXf[0] = float4(dirX,1);
lightXf[1] = float4(dirY,1);
lightXf[2] = float4(dirZ,1);
lightXf[3] = float4(1,1,1,1);

Then I put the world space vertex position in light space like this:

float4 LightSpaceVertex = mul(WorldSpaceVertex, mul(lightXf, Projection));

This all looks correct to me, but I get garbage for results. I've got a pixel shader that writes a depth value to an off-screen render target, and then my next pass just maps that render target to the screen for testing purposes. If I use the standard View matrix in place of the lightXf matrix I'm trying to construct, I get good results in the render target - except they're from the point of view of the camera instead of the light. For that reason I'm pretty sure that the problem is in the lightXf matrix I'm trying to construct. Or it could be in the Projection matrix. I'm just assuming it's ok to use the screen's projection matrix for the light as well. I could be wrong.

Can anyone see something obvious that I'm doing wrong here? I'd love to get this working - but I'm an artist, not a graphics programmer, and matrices are one of my weaknesses when it comes to shader writing. If anyone can help me out I'd really appreciate it. I'm planning to share the shader with the community when it's done.

Thanks!

-Ben
 
I'm no matrix guru, far from it, but I can see a couple problems in your stuff.

The first is that any one constant "up" breaks down for certain light positions/directions. It will work in the general case, though. You probably need not worry about this.

The biggest deal is that the 4th row of your light transform is supposed to contain light position data, but it's constant.
 
Thanks a lot for your quick reply. Using world Z as the up direction isn't a good solution because the closer my light vector is to the world Z, the less correct my results will be. However, this should work for now - as long as I keep my light source on the same horizontal plane as my object. Once I get the other stuff working I can come back and figure out a better way to compute the up direction. Like you said, I probably don't need to worry about this just yet - but if anyone has suggestions, I'm listening.

I changed the matrix building bit of the code so that I'm putting the light position in the last row like this:

lightXf[0] = float4(dirX,1);
lightXf[1] = float4(dirY,1);
lightXf[2] = float4(dirZ,1);
lightXf[3] = lightPosition;

That didn't help me much though. I'm still getting bad results - but they're different than they were before. Any one else have ideas?

-Ben
 
hmm
ok,

the first thing that comes to mind, is that you may need to invert your matrix. Don't loose faith, this is easier than it may sound. Because it's a normalised matrix, you can just use a transpose.
A 'camera' matrix (or the view matrix) is simply the inverse of where the camera physically is, hence my reasoning. (world * view = Identity if camera is at the world matrix...)

also the 1's in your matrix shouldn't be there. the only 1 to keep is the very last element - float4(position.xyz,1).

something like:

transpose the 3x3 portion of the matrix, then set the position to:

matrix[3] = floa4(mul(float4(-pos,0),matrix).xyz,1);

(although I may have the mul() round the wrong way.

so.. be something like...

matrix[0] = float4(dirX,0);
matrix[1] = float4(dirY,0);
matrix[2] = float4(dirZ,0);
matrix = transpose(matrix);
matrix[3] = float4(mul(float4(-pos,0),matrix).xyz,1);

remember, the matrix used in the vertex shader normally is WorldViewProjection, so you are still going to have to mult by the world and projection as normal, you have simply replaced the View bit.

Dunno if thats going to help
 
Back
Top