2009-09-13

Oops, I got it.

前天出了一个问题(http://leepyzh.blogspot.com/2009/09/cg-lighting.html),是在看Cg例子“09_vertex_lighting”的时候,为了计算光照,他有一个对modelview矩阵求逆的过程,这是由于GPU顶点程序的顶点位于model坐标系,将灯光和眼睛的位置逆变换到物体的位置空间,这样在同一空间里面可以求解。

看了这个例子后,总觉得求逆是一件费时的事,干吗不把顶点坐标和向量变换到世界坐标系?这样也可以在同一空间求解呀。但是修改程序后,感觉灯光的位置有些偏差,百思不得其解。Cg程序如下:

#if 1
//here is my code to change the vertex coord & normal to world space

float4 worldcoordinate = mul(modelToWorld, position);
float3 P = worldcoordinate.xyz;
float4 Normal4;
Normal4[0] = normal[0];
Normal4[1] = normal[1];
Normal4[2] = normal[2];
Normal4[3] = 1;
float4 N4 = mul(modelToWorld, Normal4);
float3 N = N4.xyz;
N = normalize(N);

#else
 //here is the formal code

float3 P = position.xyz;
float3 N = normal;
#endif

今天,看到“18_cube_map_reflection”的时候发现他用到这个想法。呵呵,至少心里放心了。看看例子的代码,比我的简洁多了:
// Compute position and normal in world space

float3 P = mul(modelToWorld, position).xyz;
float3 N = mul((float3x3)modelToWorld, normal);
N = normalize(N);

经过盘查,我终于找到了问题所在,原来是向量相乘的时候出了问题:

Normal4[3] = 1;

其实对于向量来说,第四维是没有意义的。如果赋值为1,那么需要将原点看成起点,在两个变换完成后,然后相减得到新的向量。最简单的方法,将第四维赋值为0。从几何上可以这么理解:平移不改变向量。

到此,似乎问题已经解决。当接着往下看Cg tutorial时,又让我吃了一大惊,他说:

If the modeling transform scales positions nonuniformly, you must multiply normal by the inverse transpose of the modeling matrix ( modelToWorldInvTrans ), rather than simply by modelToWorld.

是呀。旋转和平移时,点的相对位置不会改变,所以直接变换向量没有问题;然后,不均匀的缩放时,平面发生变形,此时,法向不能简单地乘以变换矩阵而得到。

网上一位老兄给出了总结,我想应该是对的:

1.如果变换模型位置的仅仅是rotation 或 translation, 可以用该Matrix直接作用于法线,获得最终的法线.

2.如果传输模型的Matrix 是同比例scale, 用该matrix作用法线后,获得的新法线需要重新normalize

3.如果非同比例scale的matrix,必须用该matrix的inverse后,再transpose来作用于该法线。

4. 具体内容可参考: Real-Time Shader Programming 82-83页。

Finally, 看来“09_vertex_lighting”的求逆阵还是个最好的、不会出错的解决方法。

没有评论: