游戏开发论坛

 找回密码
 立即注册
搜索
查看: 12318|回复: 6

求根据变换矩阵求三分量(平移 旋转(四元数) 缩放)方法

[复制链接]

20

主题

65

帖子

65

积分

注册会员

Rank: 2

积分
65
发表于 2009-8-5 09:04:00 | 显示全部楼层 |阅读模式
我从x文件中读到了动画每一针的变换矩阵了。我想把它
分解成平移(x,y,z) 旋转4元数 (x,y,z,w) 和缩
放 (x,y,z)不知道大家有没有什么方法和公式,好像说direct 提供了个函数,但是我不知道函
数名字,有没有谁知道的可以告诉我?谢谢
另外我还想尽量不通过direct提供的函数来实现这个功能,因为我的这个程序只是对文本文件.x进行分析并生成一个二进制文件,如果加上direct的函数的话编译环境变大了。如果可以自己算的话就简单多了。不知道兄弟们有没有人知道计算的方法,一个矩阵如何才能求得 平移旋转缩放三分量呢?

59

主题

984

帖子

1200

积分

金牌会员

Rank: 6Rank: 6

积分
1200
发表于 2009-8-5 10:35:00 | 显示全部楼层

Re:求根据变换矩阵求三分量(平移 旋转(四元数) 缩放)方

microsoft matrix.decompose() source code(c#) in xna framework:

public unsafe bool Decompose(out Vector3 scale, out Quaternion rotation, out Vector3 translation)
{
    bool flag = true;
    fixed (float* numRef = &scale.X)
    {
        uint num;
        uint num3;
        uint num4;
        VectorBasis basis2;
        Vector3** vectorPtr = (Vector3**) &basis2;
        Matrix identity = Identity;
        CanonicalBasis basis = new CanonicalBasis();
        Vector3* vectorPtr2 = &basis.Row0;
        basis.Row0 = new Vector3(1f, 0f, 0f);
        basis.Row1 = new Vector3(0f, 1f, 0f);
        basis.Row2 = new Vector3(0f, 0f, 1f);
        translation.X = this.M41;
        translation.Y = this.M42;
        translation.Z = this.M43;
        *((IntPtr*) (vectorPtr + 1)) = (IntPtr) &identity.M11;
        *((IntPtr*) (vectorPtr + 1)) = (IntPtr) &identity.M21;
        *((IntPtr*) (vectorPtr + 2)) = (IntPtr) &identity.M31;
        *(*((IntPtr*) (vectorPtr + 1))) = new Vector3(this.M11, this.M12, this.M13);
        *(*((IntPtr*) (vectorPtr + 1))) = new Vector3(this.M21, this.M22, this.M23);
        *(*((IntPtr*) (vectorPtr + 2))) = new Vector3(this.M31, this.M32, this.M33);
        scale.X = *(((IntPtr*) (vectorPtr + 1))).Length();
        scale.Y = *(((IntPtr*) (vectorPtr + 1))).Length();
        scale.Z = *(((IntPtr*) (vectorPtr + 2))).Length();
        float num11 = numRef[0];
        float num10 = numRef[4];
        float num7 = numRef[8];
        if (num11 < num10)
        {
            if (num10 < num7)
            {
                num = 2;
                num3 = 1;
                num4 = 0;
            }
            else
            {
                num = 1;
                if (num11 < num7)
                {
                    num3 = 2;
                    num4 = 0;
                }
                else
                {
                    num3 = 0;
                    num4 = 2;
                }
            }
        }
        else if (num11 < num7)
        {
            num = 2;
            num3 = 0;
            num4 = 1;
        }
        else
        {
            num = 0;
            if (num10 < num7)
            {
                num3 = 2;
                num4 = 1;
            }
            else
            {
                num3 = 1;
                num4 = 2;
            }
        }
        if (numRef[num * 4] < 0.0001f)
        {
            *(*((IntPtr*) (vectorPtr + (num * sizeof(Vector3*))))) = vectorPtr2[(int) (num * sizeof(Vector3))];
        }
        *(((IntPtr*) (vectorPtr + (num * sizeof(Vector3*))))).Normalize();
        if (numRef[num3 * 4] < 0.0001f)
        {
            uint num5;
            float num9 = Math.Abs(*(((IntPtr*) (vectorPtr + (num * sizeof(Vector3*))))).X);
            float num8 = Math.Abs(*(((IntPtr*) (vectorPtr + (num * sizeof(Vector3*))))).Y);
            float num6 = Math.Abs(*(((IntPtr*) (vectorPtr + (num * sizeof(Vector3*))))).Z);
            if (num9 < num8)
            {
                if (num8 < num6)
                {
                    num5 = 0;
                }
                else if (num9 < num6)
                {
                    num5 = 0;
                }
                else
                {
                    num5 = 2;
                }
            }
            else if (num9 < num6)
            {
                num5 = 1;
            }
            else if (num8 < num6)
            {
                num5 = 1;
            }
            else
            {
                num5 = 2;
            }
            Vector3.Cross(ref (Vector3) ref *(((IntPtr*) (vectorPtr + (num3 * sizeof(Vector3*))))), ref (Vector3) ref *(((IntPtr*) (vectorPtr + (num * sizeof(Vector3*))))), out (Vector3) ref (vectorPtr2 + (num5 * sizeof(Vector3))));
        }
        *(((IntPtr*) (vectorPtr + (num3 * sizeof(Vector3*))))).Normalize();
        if (numRef[num4 * 4] < 0.0001f)
        {
            Vector3.Cross(ref (Vector3) ref *(((IntPtr*) (vectorPtr + (num4 * sizeof(Vector3*))))), ref (Vector3) ref *(((IntPtr*) (vectorPtr + (num * sizeof(Vector3*))))), out (Vector3) ref *(((IntPtr*) (vectorPtr + (num3 * sizeof(Vector3*))))));
        }
        *(((IntPtr*) (vectorPtr + (num4 * sizeof(Vector3*))))).Normalize();
        float num2 = identity.Determinant();
        if (num2 < 0f)
        {
            numRef[num * 4] = -numRef[num * 4];
            *(*((IntPtr*) (vectorPtr + (num * sizeof(Vector3*))))) = Vector3.op_UnaryNegation(*((Vector3*) *(((IntPtr*) (vectorPtr + (num * sizeof(Vector3*)))))));
            num2 = -num2;
        }
        num2--;
        num2 *= num2;
        if (0.0001f < num2)
        {
            rotation = Quaternion.Identity;
            flag = false;
        }
        else
        {
            Quaternion.CreateFromRotationMatrix(ref identity, out rotation);
        }
    }
    return flag;
}


2

主题

67

帖子

67

积分

注册会员

Rank: 2

积分
67
发表于 2009-8-5 12:37:00 | 显示全部楼层

Re:求根据变换矩阵求三分量(平移 旋转(四元数) 缩放)方

我说一下原理吧,以下矩阵都是opengl形式的。
平移矩阵
T =
1 0 0 tx
0 1 0 ty
0 0 1 tz

旋转矩阵
R =
ux uy uz 0
vx vy vz 0
nx ny nz 0
0  0  0  1

缩放矩阵
S =
sx 0  0 0
0 sy  0 0
0  0 sz 0
0  0 0  1

组合在一起就是
C = T * R * S =
ux * sx uy * sy uz * sz tx
vx * sx vy * sy vz * sz ty
nx * sx ny * sy nz * sz tz
0       0       0       1

给矩阵元素标号,下面叙述方便
m00 m10 m20 m30
m01 m11 m21 m31
m02 m12 m22 m32
m03 m13 m23 m33

得到平移向量很简单,就是m30,m31,m32
下面算缩放向量sx,sy,sz,
m00^2 + m01^2 + m02^2 =
(ux * sx)^2 + (vx * sx)^2 + (nx * sx)^2
= (ux^2 + vx^2 + nx^2) * sx^2
这里有个旋转矩阵的性质,
旋转矩阵是单位正交矩阵,
其转置矩阵就是其逆矩阵,
也是单位正交矩阵,即也构成一个旋转矩阵,
那么,(ux^2 + vx^2 + nx^2) = 1,
可求出sx,同理可求出sy和sz,
然后
m00/sx m10/sy m20/sz 0
m01/sx m11/sy m21/sz 0
m02/sx m12/sy m22/sz 0
0      0      0      1
就是一个旋转矩阵了,再转换为四元数即可。

20

主题

65

帖子

65

积分

注册会员

Rank: 2

积分
65
 楼主| 发表于 2009-8-5 12:54:00 | 显示全部楼层

Re:求根据变换矩阵求三分量(平移 旋转(四元数) 缩放)方

谢谢大家的讲解 帮了大忙了。^_^

20

主题

65

帖子

65

积分

注册会员

Rank: 2

积分
65
 楼主| 发表于 2009-8-5 18:08:00 | 显示全部楼层

Re:求根据变换矩阵求三分量(平移 旋转(四元数) 缩放)方

再次感谢。我现在已经求得了 平移缩放 和旋转矩阵 不知道能不能讲解以下如何把旋转矩阵转换为四元数呢?
谢谢

2

主题

67

帖子

67

积分

注册会员

Rank: 2

积分
67
发表于 2009-8-6 19:08:00 | 显示全部楼层

Re:求根据变换矩阵求三分量(平移 旋转(四元数) 缩放)方

这个很多书都有介绍吧
比如这本,3D Math Primer for graphics and game development
翻到第185页,10.6.3节

绕任意轴向量nx,ny,nz,旋转角度a

用四元数w,x,y,z
表示为
w = cos(a/2)
x = nx * sin(a/2)
y = ny * sin(a/2)
z = nz * sin(a/2)

用旋转矩阵
m00 m10 m20 0
m01 m11 m21 0
m02 m12 m22 0
0   0   0   1
表示为(这个是opengl形式,跟上面书里的为转置关系)
m00 = nx^2 * (1 - cos(a)) + cos(a)
m10 = ny * nx * (1 - cos(a)) - nz * sin(a)
m20 = nz * nx * (1 - cos(a)) + ny * sin(a)

m01 = nx * ny * (1 - cos(a)) + nz * sin(a)
m11 = ny^2 * (1 - cos(a)) + cos(a)
m21 = nz * ny * (1 - cos(a)) - nx * sin(a)

m02 = nx * nz * (1 - cos(a)) - ny * sin(a)
m12 = ny * nz * (1 - cos(a)) + nx * sin(a)
m22 = nz^2 * (1 - cos(a)) + cos(a)

然后可以得出
m00 + m11 + m22 = 4 * w^2 - 1(这个叫矩阵的迹,trace)
m00 - m11 - m22 = 4 * x^2 - 1
-m00 + m11 - m22 = 4 * y^2 - 1
-m00 - m11 + m22 = 4 * z^2 - 1
可以分别求出w,x,y,z

有个问题就是开方的时候不知道正负号的选取
正负四元数其实就是,逆时针旋转一个正角和顺时针旋转一个负角(就是360减去前面的正角),结果一样
所以取个正值就行了
不过,这四个值只能用一个
具体原因不清楚,可能仅有对角线元素信息不足吧

然后继续推导得到

用w表示x,y,z
x = (m12 - m21) / 4w
y = (m20 - m02) / 4w
z = (m01 - m10) / 4w

用x表示w,y,z
w = (m12 - m21) / 4x
y = (m01 + m10) / 4x
z = (m20 + m02) / 4x

用y表示x,w,z
x = (m01 + m10) / 4y
w = (m20 - m02) / 4y
z = (m12 + m21) / 4y

用z表示x,y,w
x = (m20 + m02) / 4z
y = (m12 + m21) / 4z
w = (m01 - m10) / 4z

四个值只要有一个就可以求出另外三个
书里用的策略是,在前面求出来的w,x,y,z中取一个最大的
然后用后面的式子求出另外三个
章节后面附有源代码,可以看看...

0

主题

2

帖子

0

积分

新手上路

Rank: 1

积分
0
发表于 2009-8-7 13:00:00 | 显示全部楼层

Re:求根据变换矩阵求三分量(平移 旋转(四元数) 缩放)方

解答的太详细了!我也谢谢回答的同学!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-5-18 13:46

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表