Normal transformation in vertex shaders

Started by
5 comments, last by TheAdmiral 17 years, 7 months ago
Hi, I see in many shaders that vertex normals are being transformed with the world transformation( including scale ) and then they are normalized to regain the unit length. The code is usually like this: matrix4x4 m_worldViewProj; matrix4x4 m_world; VS_OUTPUT vs_main(VS_INPUT In) { VS_OUTPUT Out = (VS_OUTPUT)0; Out.Pos = mul(In.Pos,m_worldViewProj); float3 world_normal = normalize(mul(In.Norm, (float3x3)m_world)); // etc... } Wouldn't it be better if we pass a 3x3 rotation matrix ( without the scale, only world rotation ) and transform the normal without normalizing it? Or the speed we gain because of the lesser instructions( no normalize() call ) is lost due to the extra parameter pass( the extra 3x3 matrix for the rotation )? matrix4x4 m_worldViewProj; matrix4x4 m_world; matrix3x3 m_worldRotation; VS_OUTPUT vs_main(VS_INPUT In) { VS_OUTPUT Out = (VS_OUTPUT)0; Out.Pos = mul(In.Pos,m_worldViewProj); float3 world_normal = mul(In.Norm, m_worldRotation); // etc... }
Advertisement
It would be possible, in simple cases.

But if the world/view matrix contains non-uniform scaling component, you could not reduce it to 3x3 rotation matrix. Such matrix would have to be non-orthonormal - thus requiring to normalize the vector after the multiplication anyway!

In reality, non-uniform scaling of models is rarely used - models are better to be prepared in some 3D editor, with normals recalculated whenever scale of the model changes.

HTH.
~def
Thanks for the reply deffer.

Actually, I keep scale, translation and rotation to separate variables. Rotation is kept in a separate 3x3 Matrix and scale in a separate Vector3. So I have no problem such as extracting the rotation from a world matrix.

What troubles me is that the time needed for passing the extra matrix to the shader may be greater than the time gained from the fewer instructions( no normalize() call ).
I have looked to some proffesional game shaders released for modding and they always prefer to transform the normal with the world matrix and then normalize it.
It should exists some reason for doing that.
Quote:Original post by George109
Actually, I keep scale, translation and rotation to separate variables. Rotation is kept in a separate 3x3 Matrix and scale in a separate Vector3. So I have no problem such as extracting the rotation from a world matrix.
.


Maybe I didn't make myself clear.
Even if you did separate the R/S/T components from the initial WorldView matrix(*), the normals should be _scaled_ as well (in your vertex shader) - which implies renormalizing afterwards.
You cannot be relying on just multiplying by 3x3 orthonormal matrix.
Unless you're sure that there was no non-uniform scaling.


The 3x3 matrix that you extract from WorldView - is it nothing else than top-left part of the original 4x4 WorldView matrix? If it is (and it should be (when there's no scaling)), then it doesn't matter if you multiply by 3x3 or 4x4 matrix. The assembly generated would be the same.
If there is scaling, but uniform, you could be passing the additional non-scaled matrix. But it would be even better to compute it in the vertex shader itself. The shader compiler would optimize that calculation to run outside the vertex-wise loop, in the preprocessing stage.


As for your oroginal question:
No, I don't think that passing one additional matrix would mater at all.


(*) Why would you do that in the first place? How does the vertex shader look with that tactic used?
Ok, I understood, a non-uniform scale affects the direction of the normal. That's right. The normal should be scaled when the scale is not uniform.

Quote:Original post by deffer
(*) Why would you do that in the first place? How does the vertex shader look with that tactic used?


Every object in the scene keeps its world transformation as a translation vector3, a scale vector3 and a rotation Matrix3x3( this is done for faster updating of the objects ). Before rendering an object, I combine them( translation, rotation and scale ) to a 4x4 matrix and I pass it to the shader. So there is nothing special in the shader code, it's similar to code i wrote at the first post.
Also, there are times when you don't want to renormalize a normal. Specifically, some folks are using normal shortening and bending to encode the results of ambient occlusion mapping at the per-vertex level. Renormalizing the vertex normals would totally hose that.

I should also note that, just because something's done in a commercial game, doesn't mean you should assume it's a good idea, or that there's a great reason for it. There's plenty of sub-optimal stuff in shipping titles.
I see what you're trying to do, George, but it sounds like a logistical nightmare in the making. Sure, you could save yourself an instruction per vertex shader execution, but you're putting a fair bit more stress on the application. Although you may get a fps or two (if you're lucky) out of it in a typical high-poly scene, you're asking for bugs.

The extra constant (3x3 matrix) will resolve to a SetValue call, which is reasonably expensive (much moreso if it's the only shader constant being set) and it's altogether possible that the extra operation (pointwise float4 multiply) you save is being parallel optimised with another operation, in which case you'll see no performance difference at all.

In conclusion, unless your project is very simple, very demanding, or your vertex shader will double in size from the extra operation, you're probably best off making the strategical sacrifice.

Regards
Admiral
Ring3 Circus - Diary of a programmer, journal of a hacker.

This topic is closed to new replies.

Advertisement