Jump to content
  • Advertisement
Sign in to follow this  
kovacsp

Vertex shaders and various coordinate systems, different ways of normal traf

This topic is 4767 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi all, now I have seen many shaders and I can hardly find two which do normal transformation in the same way. As far as I know, the normal should be rotated with the object (that is, multiplied by the upper 3x3 part of the World matrix of the object), and normalized. This is exatly what is done in here: http://www.toymaker.info/Games/html/vertex_shaders.html
float3 N = normalize(mul(Norm, (float3x3)WorldView)); // normal (view space)

Others do it by multiplying with the inverted, transposed world matrix, eg. here: http://www.gamedev.net/columns/hardcore/dxshader2/page3.asp
; transform normal
dp3 r1.x, v3, c[INVERSE_WORLD_MATRIX]
dp3 r1.y, v3, c[INVERSE_WORLD_MATRIX_1]
dp3 r1.z, v3, c[INVERSE_WORLD_MATRIX_2]

; renormalize it
dp3 r1.w, r1, r1
rsq r1.w, r1.w
mul r1, r1, r1.w

(I beleive this is the same but I don't have the book Real-time Rendering for an explanation) Thenk, here comes another from the same author: http://www.gamasutra.com/features/20030418/engel_pfv.htm
float4x4 matWorldViewProj;
float4x4 matWorld;
float4 vecLightDir;

struct VS_OUTPUT
{
    float4 Pos : POSITION;
    float3 Light : TEXCOORD0;
    float3 Norm : TEXCOORD1;
};

VS_OUTPUT VS(float4 Pos : POSITION, float3 Normal : NORMAL)
{
    VS_OUTPUT Out = (VS_OUTPUT)0;
    Out.Pos = mul(Pos, matWorldViewProj); // transform Position
    Out.Light = vecLightDir; // output light vector
    Out.Norm = normalize(mul(Normal, matWorld)); // transform       Normal and normalize it
    return Out;
}

He simply multiplies the Normal with the World matrix! Why? The other thing that causes a confusion in my mind is this: how should I decide the coordinate system into which I transform everything and do my computations there. Some use the global coord system (that is, they multiply position with World), others use View space (they multiply with WorldView), others use tangent space... To make more confusion, some RenderMonkey samples assume that my mesh is in the origo so they don't use the Wolrd matrix at all! I would be very happy if someone could clear up these things. Thanks, kp

Share this post


Link to post
Share on other sites
Advertisement
Rule #1: "for an operation involving two vectors, both vectors should be in the same space".

Rule #2: "for the best performance, use whichever space requires the least overall computation effort, it doesn't matter which".


Those are the two main rules to follow; quite often example/tutorial code doesn't follow the second rule for reasons of keeping the code easy to understand.

The light direction and/or position for a scene is usually stored in world space, and the normal vectors for your untransformed model are usually in model space (aka object space). Simple Lambertian diffuse lighting requires a dot product between a light direction vector and a normal; because of rule #1, either the light needs to be transformed into the same space as the normal OR the normal needs to be transformed into the same space as the light.


Consider a mesh with 1000 vertices and a single directional light being used in a simple L.N:

- to transform the normal into the same space as the light, we must transform 1000 normals to get them from object space into world space (i.e. transform by the world matrix).

- to transform the light into the same space as the object, we must transform 1 light into object space (i.e. transform by the inverse of the world matrix)

1000 computations vs 1 computation for a simple diffuse light means in this case, lighting in object space is more efficient; but doing it world space is more understandable if its in a tutorial.


If the top left 3x3 part of a matrix ONLY contains rotations and no non-uniform scales or skews, the the inverse of that top left 3x3 part is the SAME as the transpose of that top left 3x3 part. Both cancel each other and the inverse transpose ends up as just the top left 3x3 part of the world matrix again!

The "inverse transpose" for transforming normals is only necessary if the top left 3x3 part of the matrix contains scales or skews; I don't have the time to go through the maths for that now unfortunately (end of lunch break, back to work).

But basically, if you *know* ahead of time that your world matrix doesn't contain skews or non-uniform scaling, then you don't need to transform normals with the inverse transpose; if you don't know, then you do need to transform by it. Most of the time you DO know (or can ensure your artists create content that doesn't scale/skew).

Share this post


Link to post
Share on other sites
Hi and thanks for your help!

Now I cas understand some things. But: if I do the transformation in the vertex shader then it doesn't matter whether I transform light or normals, since the vertex shader will run 1000 times anyway unless I do the transformation before passing it to the shader.. maybe this is what you told me? :)

And I still don;t understand the last shader, since it multiplies the normal with thw whole World matrix, thus it applies translation too! It will crash that vector, right?

kp

Share this post


Link to post
Share on other sites
Quote:
Original post by kovacsp
Now I cas understand some things. But: if I do the transformation in the vertex shader then it doesn't matter whether I transform light or normals, since the vertex shader will run 1000 times anyway unless I do the transformation before passing it to the shader.. maybe this is what you told me? :)


Yes, the idea is one vector * matrix transformation using the CPU to transform the light vector into object space saves you having to do 1000 vector * matrix transformations in the vertex shader.


Quote:
And I still don;t understand the last shader, since it multiplies the normal with thw whole World matrix, thus it applies translation too! It will crash that vector, right?


Correct, a normal should only be rotated, it shouldn't have translation applied to it. However in the sample code for the article you linked, the object is only ever rotated using a trackball; no translations are performed on it so the translation part of the matrix is always 0,0,0. Transforming a vector by a 4x4 matrix where it's translation part is 0,0,0 is identical to transforming by the top left 3x3 part of that matrix.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!