matrix, mul, Normal multiplication in Shader

  Damn it! I've been stuck in the matrix-problem of Shader all the day!

After searching and thinking, I've figured out the answer, and here is my memo about matrix of D3D and Shader

 

 Row-major or Column-major are all about how matrix is STORED IN THE REGISTERS, they won't change matrix's access method. For example, no matter the matrix m in the registers is row or column majored, m._43 is always reprents the 4th row and 3rd column elements.

 

   Therefor, Though Matrix in Shader is Column-major order, and it's looked like a right-handed matrix, it's still left-handed, because the multiplcation still perform the pre-multiple like: pos=mul( pos, mat );

 

 

   The reason that the shader use column-major order is that multiply can be implemented as four dot products, which is more efficietn than row-major order.

 

   Therefore, if you are going to set the matrix to the Shader using ASM, you must transpose your matrix before IDirect3DDevice9::SetVertexShaderConstantF() or IDirect3DDevice9::SetPixelShaderConstantF() to pass the matrix to the shader.

 

   And while you are writing the HLSL, you don't have to transpose the matrix by yourself before setting the matrix to the Shaer, all you have to do is just calling ID3DXConstantTable::SetMatrix() and it will help to transpose the matrix before assigning.

 

  After assigning the matrix, you always use it with mul intinsic function, hence, you must notice that:

    

Multiplies x and y using matrix math. The inner dimension x-columns and y-rows must be equal.

ret mul(x, y)

Parameters

x

[in] The x input value. If x is a vector, it treated as a row vector.

y

[in] The y input value. If y is a vector, it treated as a column vector.

Return Value

The result of x times y. The result has the dimension x-rows x y-columns.

 

And following is about normal multiplication, it's originally from:

http://www.unknownroad.com/rtfm/graphics/rt_normals.html

 

In one word, while performing normal transforming,

vecNormalTransformed=mul( vecNormal, matWorldViewInverseTransposed );

 

When it comes to ray tracing, it's nice to be able to move primitives around in the scene. Otherwise everything is at the origin, and scenes aren't very interesting. So we use affine transformations like scaling, rotation and translation. No problem. However, intersecting with a scaled, rotated, translated primitive can be kind of difficult. The easiest way to deal with it is to keep the primitive at the origin, then apply the inverse of the transformation matrix to the ray instead. If you get a hit, you apply the transformation to move the point of intersection back into world space.

This is all easy. So now you have your point of intersection, you grab the normal from the unit primitive, and use the world transformation to put it into world space too. It's so easy. But it doesn't work. Your scaled primitive is a hole in space. The normals are all wrong.

This problem is common and well-documented. The first place I read about it was in Ray Tracing News volume 0, the article is Abnormal Normals by Eric Haines. See, normals are VECTORS, not points. Your transformation matrices are for points. If you are lucky, most of your transformation matrices will work for vectors, but if you are doing non-uniform scaling you have a problem. And if you have a bad vector class, you're going to have a tough time translating as well.

First we'll deal with a little issue about vectors. If you are doing any real graphics, you are using homogenoeus vectors and matrices. That means 4 dimensions, vectors are [x y z w]. That fourth coordinate is the homogenoeous coordinate, it should be 1 for points and 0 for vectors. The reason will be obvious in a minute. First, realize the difference between points and vectors. Points 'exist' in space (at least theoretically). You can draw a line between two points. Vectors do not. A vector is way of specifying a direction. The vector [1 1 1] means 'go one unit in the positive direction along each axis'. Usually we _represent_ vectors with lines, which is why you might be confused. What we are really drawing is a direction and a magnitude. Hopefully you understand this.

Ok, so why do we have a 'w', or homogeneous coordinate. Couldn't we just leave it off? Well, we could, but then we couldn't have translation matrices that would work for points and vectors. Think about it, a translation matrix looks like this:

100tx
010ty
001tz
0001

Now when you right-multiply by the point [x y z 1], you get [x + tx, y + ty, z + tz, 1]. When you multiply a vector [x y z 0], you get [x y z 0]. Remember, a vector is just a direction. You can't translate a direction, it won't change. The direction [0 0 1 0] is 'one unit up the z axis' no matter where you are in space. We use homogeneous coordinates so we can do translation of points and vectors with the same matrix.

If you are representing points and vectors properly, and you have tried translating, rotating, and uniformly-scaling your objects, you will notice that the 'multiply normal by transformation matrix' scheme actually works. There aren't any problems. But try non-uniform scaling (that means scaling by different amounts in the different axis). Oops. Everything comes crashing down. Now, you could just outlaw non-uniform scaling, but then you can't use spheres to make ellipsoids. And your cones are always going to be at 45 degree angles. This is no good, so a better method is needed.

First a bit of explanation as to why this doesn't work. Look at the graphs below, and pretend that the red line is the line of intersection of the plane x=y with the XY plane. The green line is the normal to this plane at the point [0.5 0.5 0]. The direction of this line is [0.5 0.5 0].

Now we will apply a non-uniform scaling matrix that scales by 2 in the X-axis. The new graph is drawn on the right. The green line is the 'unit' plane normal with the non-uniform scaling matrix applied, it's direction has become [1.0 0.5 0]. This is clearly wrong, the normal should be perpendicular to the plane. In fact, the correct normal is [0.25 0.5 0], which is drawn in blue.


So how do we get this normal? The answer is to multiply by the transpose of the inverse of the transformation matrix instead. This is intuitive if you think about it. The transpose of the inverse of a rotation matrix is the same rotation matrix, so rotations are preserved. Translations don't matter because the w coordinate is 0. The only case left is scaling. The inverse of scaling in an axis is 1 divided by the scaling factor. The transpose operation has no effect here, as scaling is all done on the diagonal. Think about stretching a sphere out to be twice as long on the X axis as it is on the Y and Z axis (making an ellipsoid). The surface of the ellipsoid is twice as large, hence the curvature is half what it used to be. So the normal changes half as quickly. Hence when we scale something by 2 in the x axis, we multiply the x component of the normal by one half.

I hope that made sense..


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值