游戏开发论坛

 找回密码
 立即注册
搜索
查看: 5397|回复: 12

求助:数学高手请进,关于四元数的问题。

[复制链接]

7

主题

87

帖子

109

积分

注册会员

Rank: 2

积分
109
QQ
发表于 2006-11-30 13:06:00 | 显示全部楼层 |阅读模式
  四元数进行旋转变换要比矩阵快。转换后可通过复杂的变化生成一个相应的旋转矩阵。但是另一个关键的问题是,怎样从旋转矩阵生成一个四元数?
  我查了很多资料和网,都没有这方面的解释。Direct3D有一个方法是Quaternion.RotationMatrix(),速度也很快,有谁知道这个详细算法吗?给个数学推导思路也可。欢迎高手指点迷津。我的邮箱是:Lv_Ximing@yahoo.com.cn。



  下面是我自己实现的四元数生成旋转矩阵的方法:
Public Function QuaternionToMatrix(Quaternion As DirectX.Quaternion) As DirectX.Matrix
  '
  '旋转变换矩阵(3 X 3)为:
  '1-2*(Qy^2+Qz^2),        2*(Qx*Qy-Qw*Qz),        2*(Qx*Qz+Qw*Qy)
  '2*(Qx*Qy+Qw*Qz),        1-2*(Qx^2+Qz^2),        2*(Qy*Qz-Qw*Qx)
  '2*(Qx*Qz-Qw*Qy),        2*(Qy*Qz+Qw*Qx),        1-2*(Qx^2+Qy^2)
  '
  '返回值。
  Dim r As DirectX.Matrix
  '计算中间变量减少重复运算。
  Dim Qx2 As Single = Quaternion.X ^ 2
  Dim Qy2 As Single = Quaternion.Y ^ 2
  Dim Qz2 As Double = Quaternion.Z ^ 2
  Dim QxQy As Single = Quaternion.X * Quaternion.Y
  Dim QyQz As Single = Quaternion.Y * Quaternion.Z
  Dim QzQx As Single = Quaternion.Z * Quaternion.X
  Dim QwQx As Single = Quaternion.W * Quaternion.X
  Dim QwQy As Single = Quaternion.W * Quaternion.Y
  Dim QwQz As Single = Quaternion.W * Quaternion.Z
  '生成旋转矩阵。
  r.m11=1-2*(Qy2+Qz2) : r.m12=2*(QxQy-QwQz) : r.m13=2*(QzQx+QwQy) : r.m14=0
  r.m21=2*(QxQy+QwQz) : r.m22=1-2*(Qx2+Qz2) : r.m23=2*(QyQz-QwQx) : r.m24=0
  r.m31=2*(QzQx-QwQy) : r.m32=2*(QyQz+QwQx) : r.m33=1-2*(Qx2+Qy2) : r.m34=0
  r.m41=0 : r.m42=0 : r.m43=0 : r.m44=1
  '返回。
  Return r
End Function
  

7

主题

87

帖子

109

积分

注册会员

Rank: 2

积分
109
QQ
 楼主| 发表于 2006-11-30 13:09:00 | 显示全部楼层

Re:求助:数学高手请进,关于四元数的问题。

Dim Qz2 As Double = Quaternion.Z ^ 2 一句笔误了,应该是Dim Qz2 As Single ...

89

主题

4036

帖子

4132

积分

论坛元老

Rank: 8Rank: 8

积分
4132
发表于 2006-11-30 13:17:00 | 显示全部楼层

Re:求助:数学高手请进,关于四元数的问题。

四元数 做旋转不是快,而是直观。
matrix是linear algebra的东西。目前的图形运算都是建立在Linear Algebra上的。用matrix来表示旋转,现在效率会高一些。
quaternion是Geometry Algebra的东西。是multi-vector的一种,用它来表示朝向是很直观的(复数也可以认为是multi-vector。普通欧氏向量也是)。它用一种统一的代数语言来描述几何。

所以,用quaternion仅仅是为了方便。不是效率。这个方便还包括支持slerp等matrix完成不了的功能。

quaternion 和 matrix互相转化在网上有很多这样的代码。随便找找就应该一堆。

7

主题

87

帖子

109

积分

注册会员

Rank: 2

积分
109
QQ
 楼主| 发表于 2006-11-30 14:21:00 | 显示全部楼层

Re:求助:数学高手请进,关于四元数的问题。

我刚才也看过,那些都是四元数转矩阵,没有矩阵转四元数的。

89

主题

4036

帖子

4132

积分

论坛元老

Rank: 8Rank: 8

积分
4132
发表于 2006-11-30 14:24:00 | 显示全部楼层

Re:求助:数学高手请进,关于四元数的问题。

我记得好几本讲渲染的书上都有。

24

主题

256

帖子

267

积分

中级会员

Rank: 3Rank: 3

积分
267
发表于 2006-12-1 08:47:00 | 显示全部楼层

Re:求助:数学高手请进,关于四元数的问题。

/*
   Class Name:

      CQuaternion.

   Created by:

      Allen Sherrod (Programming Ace of www.UltimateGameProgramming.com).

   Description:

      This class is used to create a quaternion to be used with rotations.
*/


#ifndef QUATERNION_H
#define QUATERNION_H

#define PI 3.141592654f                                       // Define for PI.
#define GET_RADIANS(degree) (float)((degree * PI) / 180.0f)   // Will convert degrees to radians.

#include<math.h>                                            // Math header file.
#include "Matrix4.h"

template <class qType>

class Quaternion
{
   public:
      Quaternion()
      {
         // Initialize each member variables.
         x = y = z = 0.0f;
         w = 1.0f;
      }
      
      Quaternion(qType xAxis, qType yAxis, qType zAxis, qType wAxis)
      {
          // Initialize each member variables.
          x = xAxis; y = yAxis; z = zAxis;
          w = wAxis;
      }

      Quaternion &operator=(const Matrix4<qType> &matrix)
      {

              w = 0.5f * sqrtf(matrix[0] + matrix[5] +
                         matrix[10] + matrix[15] );
              w  = w  < 0.0001f ? 0.0004f : 4.0f*w;

              x  = (matrix[6] - matrix[9])/w;
              y  = (matrix[8] - matrix[2])/w;
              z  = (matrix[1] - matrix[4])/w;
        w /= 4.0f;

        return *this;
      }

      Quaternion &operator=(const Quaternion<qType> &q)
      {
         // This will make this quaternion equal to q.
         w = q.w; x = q.x; y = q.y; z = q.z;
         return *this;
      }

  Quaternion operator*(const Quaternion  &q)
  {
    // To multiply a quaternion you must first do the dot and cross product
    // of the 2 quaternions then add/subract them to a result.
    Quaternion<qType>  result;

    result.x = w * q.x + x * q.w + y * q.z - z * q.y;
    result.y = w * q.y - x * q.z + y * q.w + z * q.x;
    result.z = w * q.z + x * q.y - y * q.x + z * q.w;
    result.w = w * q.w - x * q.x - y * q.y - z * q.z;

    return result;
  }

  void slerp(Quaternion &q1, Quaternion &q2, float t)
  {
    float o, co, so, scale0, scale1;
    float qi[4];


    // Do a linear interpolation between two quaternions (0 <= t <= 1).
    co = q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w;  // dot product

    if (co < 0)
    {
      co = -co;
      qi[0] = -q2.x;
      qi[1] = -q2.y;
      qi[2] = -q2.z;
      qi[3] = -q2.w;
    }
    else
    {
      qi[0] = q2.x;
      qi[1] = q2.y;
      qi[2] = q2.z;
      qi[3] = q2.w;
    }

    // If the quaternions are really close, do a simple linear interpolation.
    if ((1 - co) <= 0.0001f)
    {
      scale0 = 1 - t;
      scale1 = t;
    }
    else
    {
      // Otherwise SLERP.
      o      = (float) acos(co);
      so     = sinf(o);
      scale0 = sinf((1 - t) * o) / so;
      scale1 = sinf(t * o) / so;
    }

    // Calculate interpolated quaternion:
  
    x = scale0 * q1.x + scale1 * qi[0];
    y = scale0 * q1.y + scale1 * qi[1];
    z = scale0 * q1.z + scale1 * qi[2];
    w = scale0 * q1.w + scale1 * qi[3];
  }

  Quaternion conjugate()
  {
     // The Conjugate is basically all axis negated but the w.
     return Quaternion<qType> (-x, -y, -z, w);
   }

     void rotatef(float amount, float xAxis, float yAxis, float zAxis)
     {
            // Pretty much what is going on here is that if there is any axis that is not
            // a 0 or a 1 (meaning its not normalized) then we want to normalize it.
            // I created a if statement because I thought this would be better than normalizing
            // it every time this function is called, which would result in a lot of expensive
            // sqrt() call.  This is just in case the user forgot to use only normalized values.
            if((xAxis != 0 && xAxis != 1) ||
               (yAxis != 0 && yAxis != 1) ||
               (zAxis != 0 && zAxis != 1))
            {
               float length = (float)sqrt(xAxis * xAxis + yAxis * yAxis + zAxis * zAxis);
               xAxis /= length; yAxis /= length; zAxis /= length;
            }

            // Convert the angle degrees into radians.
            float angle = GET_RADIANS(amount);

            // Call this once for optimization, just like using the if statement to determine if
            // we should normalize.
           float sine = (float)sin(angle / 2.0f);

            // Create the quaternion.
           x = xAxis * sine;
           y = yAxis * sine;
           z = zAxis * sine;
            w = (float)cos(angle / 2.0f);

            // Normalize the quaternion.
            float length = 1 / (float)sqrt(x * x + y * y + z * z + w * w);
            x *= length;
            y *= length;
            z *= length;
         }

     Matrix4<qType> convertToMatrix()
     {
       Matrix4<qType> pMatrix;

           // Calculate the first row.
           pMatrix[0]  = 1.0f - 2.0f * (y * y + z * z);
           pMatrix[1]  = 2.0f * (x * y + z * w);
           pMatrix[2]  = 2.0f * (x * z - y * w);
           pMatrix[3]  = 0.0f;  

           // Calculate the second row.
           pMatrix[4]  = 2.0f * (x * y - z * w);  
           pMatrix[5]  = 1.0f - 2.0f * (x * x + z * z);
           pMatrix[6]  = 2.0f * (z * y + x * w);  
           pMatrix[7]  = 0.0f;  

           // Calculate the third row.
           pMatrix[8]  = 2.0f * (x * z + y * w);
           pMatrix[9]  = 2.0f * (y * z - x * w);
           pMatrix[10] = 1.0f - 2.0f * (x * x + y * y);  
           pMatrix[11] = 0.0f;  

           // Calculate the fourth row.
           pMatrix[12] = 0;  
           pMatrix[13] = 0;  
           pMatrix[14] = 0;  
           pMatrix[15] = 1.0f;
       return pMatrix;
     }

     qType x, y, z, w;                      // x, y , z, and w axis.
};
typedef Quaternion<float> Quaternionf;
typedef Quaternion<double> Quaterniond;
#endif


// Copyright September 2003
// All Rights Reserved!
// Allen Sherrod
// ProgrammingAce@UltimateGameProgramming.com
// www.UltimateGameProgramming.com

18

主题

971

帖子

982

积分

高级会员

Rank: 4

积分
982
发表于 2006-12-1 08:49:00 | 显示全部楼层

Re:求助:数学高手请进,关于四元数的问题。

1.更正一下野猪的说法--四元数不比矩阵快,四元数做旋转要比矩阵少了数个乘法,所以从数学上来说,肯定要快。但现在有硬件支持的matrix并行计算,所以不见得两个在真正的程序执行速度上能分个伯仲来。
2.四元数转换成矩阵和矩阵转换成四元数的算法都很简单,可以自己推导,如果不知怎么推导的话,在网上也能找到相关的算法和资料,《realtime rendering》书上有此转换算法,但没给出推导过程。事实上表现旋转目前来说有四种方式都比较常用:
四元数、MATRIX、欧拉角、旋转轴带角 。这几种表现之间都可以相互转换。如果要深入了解的话,建议楼主看看ID公司给出的部分DOOM3的游戏代码idLib库,里面的数学库就有这些代码.

24

主题

256

帖子

267

积分

中级会员

Rank: 3Rank: 3

积分
267
发表于 2006-12-1 08:53:00 | 显示全部楼层

Re:求助:数学高手请进,关于四元数的问题。

see :
Matrix4<qType> convertToMatrix();

     Quaternion &operator=(const Matrix4<qType> &matrix)
      {

      w = 0.5f * sqrtf(matrix[0] + matrix[5] +
                         matrix[10] + matrix[15] );
      w  = w  < 0.0001f ? 0.0004f : 4.0f*w;

      x  = (matrix[6] - matrix[9])/w;
      y  = (matrix[8] - matrix[2])/w;
      z  = (matrix[1] - matrix[4])/w;
        w /= 4.0f;

        return *this;
      }

89

主题

4036

帖子

4132

积分

论坛元老

Rank: 8Rank: 8

积分
4132
发表于 2006-12-1 11:33:00 | 显示全部楼层

Re: Re:求助:数学高手请进,关于四元数的问题。

whb9633: Re:求助:数学高手请进,关于四元数的问题。

1.更正一下野猪的说法--四元数不比矩阵快,四元数做旋转要比矩阵少了数个乘法,所以从数学上来说,肯定要快...



如果从数学上讲,它是会快一些。你自己也说了哈,硬件支持Matrix. 其实关键还是3D API只支持matrix。你还是要把它转成Matrix。 就那个转化就不知道多出多少个乘法了 ~_~。
这段时间在看GA的东西。 有兴趣讨论讨论

36

主题

1047

帖子

1147

积分

金牌会员

Rank: 6Rank: 6

积分
1147
发表于 2006-12-1 13:08:00 | 显示全部楼层

Re: Re: Re:求助:数学高手请进,关于四元数的问题。

xpertsoft: Re: Re:求助:数学高手请进,关于四元数的问题。




如果从数学上讲,它是会快一些。你自己也说了哈,硬件支持Matrix. 其实关键还是3D API只支持matrix。...

单就旋转运算来说,也许不相上下,但如果加上构建旋转矩阵和四元数,还是四元数快一些。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

GMT+8, 2026-1-26 03:13

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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