查看: 3526|回复: 17

请教一个矩阵变换的问题

[复制链接]

该用户从未签到

发表于 2009-6-1 17:57:52 | 显示全部楼层 |阅读模式
已知:一个局部坐标系到世界坐标系的变换矩阵M,局部坐标系的一个顶点P,局部坐标系的一个方向向量V

则:1 P点在世界坐标系下的坐标P1 = M.preMult(P);     // 测试发现结果正确,没有问题
       2 V向量在世界坐标系中的表示,根据OpenGL红宝书上的说法,我理解就是使用M的逆转置矩阵对V做变换,即V1 = osg::Matrixd::inverse(M).posMult(V); 不知道这样理解对不对,不过结果看起来是错误的,得到的向量与正确结果反向,并且模长也不对。
测试代码如下:
osg::Matrixd mat = osg::Matrixd::translate(2, 2, 2);
osg::MatrixTransform* transform = new osg::MatrixTransform;
transform->setMatrix(mat);
transform->addChild(osgDB::readNodeFile("axes.osg"));
root->addChild(transform);
osg::Vec3 pos(0, 0, 0), vec(1, 1, 1);
osg::Vec3 pos0 = mat.preMult(pos);
osg::Matrixd matInv = osg::Matrixd::inverse(mat);
osg::Vec3 vec1 = matInv.postMult(vec);
osg::Vec3 temp = mat.preMult(pos+vec);
osg::Vec3 vec2 = temp-pos0;
root->addChild(createLine(pos0, pos0+vec1, osg::Vec4(1, 0, 0, 1)));       // 红色线是用上面提到的对向量多矩阵变换的方法得到的
root->addChild(createLine(pos0, pos0+vec2, osg::Vec4(0, 0, 1, 1)));      // 蓝色的线是用两点分别做矩阵变换后求差得到的(这个肯定是正确的)

请问上面2使用的对向量做矩阵变换的方法哪里有问题,正确的对向量做矩阵变换的方法是怎样的?谢谢

该用户从未签到

发表于 2009-6-1 19:43:09 | 显示全部楼层
局部坐标系下的P 转换到 世界坐标系下的P1:
P1 = P * Mat

反之,世界坐标系下的V 转换到 局部坐标系下的V2,则通过:
V = V2 * Mat,
得到
V * InverseMat = V2 * (Mat * InverseMat) = V2

即V2 = inverse(M).preMult(V),您的数学推导有误

该用户从未签到

 楼主| 发表于 2009-6-1 20:30:29 | 显示全部楼层
局部坐标系下的P 转换到 世界坐标系下的P1:
1)P1 = P * Mat        这个没有问题
反之,世界坐标系下的V 转换到 局部坐标系下的V2,则通过:
2)V = V2 * Mat        这个是依据什么呢?这里的Mat和1)中的Mat都是局部坐标到世界坐标的变换矩阵呀!

我上面提到的方向向量的矩阵变换公式,来自OpenGL编程指南第六版附录F其次坐标和变换矩阵F.1.2法线变换:V = V2 * (Mat-1)T  (原文:法线向量是由需要变换的点的变换矩阵的逆转置矩阵进行变换的) 进而得到:V = (Mat-1) * V2
由法线向量推广到一般方向向量,即:V = osg::Matrix::inverse(Mat).postMult(V2);
可是得到的结果却就是不对,而且恰恰是正确方向的反向

bwt:根据您上面给的方法修改了一下程序,就是把osg::Vec3 vec1 = matInv.postMult(vec);
改成osg::Vec3 vec1 = matInv.preMult(vec);
发现结果也是错误的


[ 本帖最后由 indif 于 2009-6-1 20:31 编辑 ]

该用户从未签到

 楼主| 发表于 2009-6-1 20:37:44 | 显示全部楼层
对不起,刚才对这个“反之,世界坐标系下的V 转换到 局部坐标系下的V2,则通过:
V = V2 * Mat,”有所误解

局部坐标点P可以通过P*Mat变换到世界坐标系下,
但是,局部坐标的方向向量V2是不能通过相同的变换V2*Mat变换到世界坐标系下的,
这个通过简单的空间想象应该可以理解

该用户从未签到

发表于 2009-6-1 20:40:31 | 显示全部楼层
请仔细阅读我给出的条件,1)P1 = P * Mat 和2)V = V2 * Mat本来就是同一个式子,不需要什么依据之说。

我只是要通过2)式推导出正确的变换运算结果,根据矩阵运算的法则:Mat * InvMat = 1(单位矩阵),因此有
V * InverseMat = V2 * (Mat * InverseMat) = V2,推导得出的结论不会有问题。

至于您的v2 = matInv.preMult(v)为什么有误,恐怕是其它地方出了问题,请再检查一下您的实现过程。osg中的世界坐标转为局部坐标就是这样实现的。

此外osg中不存在M * v (即M.postMult(v))这样的运算,而只能使用v * M来变换空间顶点。前者虽然的确可以得出结果,但是这样的运算其实是没有意义的,只是为了在有的时候方便某种运算而设置的。按照osg创始人Don Burns的话说,M.postMult(v)是一个“令人混淆”的运算符,请不要使用它。

[ 本帖最后由 array 于 2009-6-1 20:42 编辑 ]

该用户从未签到

 楼主| 发表于 2009-6-1 20:51:56 | 显示全部楼层
如上面提到的,对于方向向量,V = V2 * Mat是错误的,简单想象一下,对于osg::Matrix::translate(1,0,0)这样一个平移矩阵,假设V2=(1,1,1),按上式得到的V=(2,1,1),这显然是错误的

而且,我这里已知的是V2,要求的是V,因此我不能明白您推导出V2 = inverse(M).preMult(V)式的目的为何

该用户从未签到

发表于 2009-6-1 20:54:59 | 显示全部楼层
V是世界坐标下的坐标,V2是局部坐标,因此V = V2 * mat和P = P1 * mat是一样的,麻烦您再看一下我所写的
V2 = V * matInv,这难道不是您所求的“世界坐标转换到局部坐标”吗?或者您还是再叙述一下您要求解的问题,抱歉我看着也有些糊涂

该用户从未签到

发表于 2009-6-1 20:58:08 | 显示全部楼层
我又看一一下您的主题,好像的确不是求解“世界坐标转换到局部坐标”,抱歉我惯性思维地阅读了。

不过“局部坐标系的一个方向向量V”和“局部坐标系的一个顶点P”,这两者有什么区别吗?仍然用原来的公式不就可以了?

该用户从未签到

 楼主| 发表于 2009-6-1 21:00:21 | 显示全部楼层
关于M.postMult(v),我想它是有意义的
因为 M * V = V * MT(M转置),
所以postMult的意义就在于在不需要求M转置的情况下,可以使用M.postMult(v)来等价实现 V × MT
这个在Don Burns的回答中似乎也有提及(不排除我理解错误的可能

而M * V这样的运算,说实话我只是从红宝书上看来的,方向向量是这样做变换的

[ 本帖最后由 indif 于 2009-6-1 21:05 编辑 ]

该用户从未签到

 楼主| 发表于 2009-6-1 21:03:47 | 显示全部楼层
原帖由 array 于 2009-6-1 20:58 发表
不过“局部坐标系的一个方向向量V”和“局部坐标系的一个顶点P”,这两者有什么区别吗?仍然用原来的公式不就可以 ...


方向向量的变换和顶点的变换应该是有区别的,如上提到的:“简单想象一下,对于osg::Matrix::translate(1,0,0)这样一个平移矩阵,假设V2=(1,1,1),按上式得到的V=(2,1,1),这显然是错误的”

而我上面提到的“Mat逆转置 * V”,则是我理解的对方向向量应用矩阵变换的正确方法(出处上面提到了)

该用户从未签到

发表于 2009-6-1 21:06:22 | 显示全部楼层
OpenGL和OSG的矩阵定义是有区别的,这一点请特别注意。OpenGL使用列主序矩阵,而OSG是行主序的。因此向量和矩阵运算也是相反的,OpenGL是 列主序矩阵 * 列向量(M * V),而OSG是 行向量 * 行主序矩阵(V * M)

该用户从未签到

 楼主| 发表于 2009-6-1 21:11:12 | 显示全部楼层
这一点我已经考虑进去了,因此,我已经把书中公式的M * V与V * M做了对换
从而得到现在的关于方向向量的矩阵变换公式

该用户从未签到

 楼主| 发表于 2009-6-1 21:12:36 | 显示全部楼层
也许您还记得“http://bbs.osgchina.org/viewthread.php?tid=1359” 这个帖子,我们在里面讨论过这个问题

该用户从未签到

 楼主| 发表于 2009-6-1 21:21:35 | 显示全部楼层
留意一下这段叙述:
“V' = V*M and M* V are defined....

the first, as Don points out is the standard one you should use, and the later is just for special occasions...

And the special occasion is when you'd want to do V' = V* Mtranspose where you only have M to hand and would have to do a transpose of M.

This can be rewritten V' = M * V;

The particular time when you'll do this would be when transforming normals and planes by the inverse transpose. This is done all the time in the cull traversal, where the inverse just happens to be the accumulated modelview matrix.

This is all a bit crafty, but certainly helps remove all those extra ops in transposing.

出自:http://www.openscenegraph.org/pr ... trixTransformations
关于M * V的解释和我上面提到的是一样的

该用户从未签到

 楼主| 发表于 2009-6-2 07:54:43 | 显示全部楼层
昨晚查了一下专业资料,array上面提到的对方向向量矩阵变换使用公式:V = V2 * Mat 是正确的
但需要注意的是,此时V2需要是齐次向量,第四个分量必须为0,即对Vec3的向量需要类似这样的代码:
osg::Vec4 temp(V2.x(), V2.y(), V2.z(), 0);
temp = Mat.preMult(temp);
V = osg::Vec3(temp.x(), temp.y(), temp.z());

对顶点做矩阵变换则可以直接使用:P = Mat.preMult(P1);
因为osg::Matrix::preMult( const Vec3& v )的实现中默认把其次坐标的第四个分量当作1来处理了。

不过,我最初提到的方法(来源于红宝书),还是不知道什么地方出了问题,难道它只用于法向量的变换而不能推广到一般方向向量???

该用户从未签到

发表于 2009-6-2 12:23:02 | 显示全部楼层
重要的是遵循线性代数的基础运算法则,然后您完全可以推导出正确的公式了~~至于红宝书里怎么说的,倒没必要拘泥,它的表达不清或者有些地方有错也是再正常不过的事情

大家在大学都有线性代数课程吧,我个人就经常翻看大学的教科书,查漏补缺,很有助益~~

该用户从未签到

 楼主| 发表于 2009-6-2 14:43:04 | 显示全部楼层
同感同感,几何学和矩阵基础理论对3D编程是非常重要的
谢谢array的热情帮助

该用户从未签到

发表于 2011-2-19 09:43:29 | 显示全部楼层
是这样理解的
您需要登录后才可以回帖 登录 | 注册

本版积分规则

OSG中国官方论坛-有您OSG在中国才更好

网站简介:osgChina是国内首个三维相关技术开源社区,旨在为国内更多的技术开发人员提供最前沿的技术资讯,为更多的三维从业者提供一个学习、交流的技术平台。

联系我们

  • 工作时间:09:00--18:00
  • 反馈邮箱:1315785073@qq.com
快速回复 返回顶部 返回列表