四元数学习之四元数和矩阵的转换

四元数学习之四元数和矩阵的转换

         四元数是一种可以替代矩阵和欧拉角的数学工具。他最初是由William Rowan Hamilton发现的(参考维基百科),它的最大的特点是不满足交换率。也谈一下自己对这一点的体会。在离散数学中有讲到半群、群、环和域的概念,其中环的定义是具有交换率和分配率(详情参考环的数学定义),而域的概念则是在环的基础上加上了交换率。所以说四元数无法满足域的定义,它是除法环的一种。何为除法环?其实很简单,被除数和除数都满足结合律和分配律,但是如果要满足交换律,即被除数和除数交换位置,那么它的结果是不同的(准确地说,如果它们不为0的话,那么结果呈倒数关系)。又由于要在四维解空间上解得i3=-1,所以只能在不满足交换率的条件下得出i、j、k。

    四元数在计算机图形学的优势在于运算量小和利于插值,且旋转没有缺陷。可是普通的一个四元数定义又比较难懂,而且OpenGL的API中又没有带四元数的参数的函数,所以需要我们根据公式做一做小小的转换。

    原创文章,反对未声明的引用。原博客地址http://blog.csdn.net/gamesdev/article/details/10036105

    设四元数Q(x, y, z, w)表示向量a(xa,ya, za)经过α角旋转后的结果,则x、y、z和w分别为:

x= sin(α/2)·xa

y= sin(α/2)·ya

z= sin(α/2)·za

w= cos(α/2)

    在上一篇博客中讲到了如何用一个矩阵表示坐标系沿向量的旋转,这里我直接给出公式:


将这两个公式结合起来。再结合高中所学的半角公式:

sinα = 2sin(α/2)·cos(α/2)

cosα = cos2(α/2) - sin2(α/2)

cos2(α/2) = (1 +cosα)/2

sin2(α/2) = (1 -cosα)/2

可以解出用四元数表示旋转矩阵为:

    该来验证一下公式的正确性,同样地,采用OpenGL托管的矩阵来测试,看看使用自带的glRotatef()函数和我们写的公式相比,究竟有没有差距。

首先写了一个简单的Quaternion类,它的定义如下:

#ifndef QUATERNION_H
#define QUATERNION_H

#include <QtGlobal>
#include <math.h>

class Quaternion
{
public:
    Quaternion( float _x,
                float _y,
                float _z,
                float _w )
    {
        x = _x;
        y = _y;
        z = _z;
        w = _w;
    }
    void ToMatrix( float matrix[] )
    {
        Q_ASSERT( matrix != Q_NULLPTR );
#ifndef MATRIX
#define MATRIX( row, col ) matrix[row * 4 + col]
#endif
        MATRIX( 0, 0 ) = 2 * ( x * x + w * w ) - 1;
        MATRIX( 0, 1 ) = 2 * ( x * y + z * w );
        MATRIX( 0, 2 ) = 2 * ( x * z - y * w );
        MATRIX( 0, 3 ) = 0.0f;
        MATRIX( 1, 0 ) = 2 * ( x * y - z * w );
        MATRIX( 1, 1 ) = 2 * ( y * y + w * w ) - 1;
        MATRIX( 1, 2 ) = 2 * ( y * z + x * w );
        MATRIX( 1, 3 ) = 0.0f;
        MATRIX( 2, 0 ) = 2 * ( x * z + y * w );
        MATRIX( 2, 1 ) = 2 * ( y * z - x * w );
        MATRIX( 2, 2 ) = 2 * ( z * z + w * w ) - 1;
        MATRIX( 2, 3 ) = 0.0f;
        MATRIX( 3, 0 ) = 0.0f;
        MATRIX( 3, 1 ) = 0.0f;
        MATRIX( 3, 2 ) = 0.0f;
        MATRIX( 3, 3 ) = 1.0f;
#undef MATRIX
    }
    static Quaternion FromRotation( float _x,
                         float _y,
                         float _z,
                         float angleInDegree )
    {
        // 向量的单位化
        float length = sqrt( _x * _x + _y * _y + _z * _z );
        Q_ASSERT( !qFuzzyCompare( length, 0.0f ) );// 希望length不为0

        _x /= length;
        _y /= length;
        _z /= length;

        float alpha = angleInDegree / 180 * 3.1415926;// 已转换弧度制

        return Quaternion(
                    sin( alpha / 2 ) * _x,
                    sin( alpha / 2 ) * _y,
                    sin( alpha / 2 ) * _z,
                    cos( alpha / 2 ) );
    }
private:
    float x, y, z, w;
};

#endif // QUATERNION_H


下面展示了如何调用这个类。

#include <assert.h>
#include <stdio.h>
#include "GLWidget.h"
#include "Quaternion.h"

void PrintMatrix( float matrix[16] )
{
    Q_ASSERT( matrix != 0 );
    printf( "%8.2f%8.2f%8.2f%8.2f\n"
            "%8.2f%8.2f%8.2f%8.2f\n"
            "%8.2f%8.2f%8.2f%8.2f\n"
            "%8.2f%8.2f%8.2f%8.2f\n",
            matrix[0], matrix[1], matrix[2], matrix[3],
            matrix[4], matrix[5], matrix[6], matrix[7],
            matrix[8], matrix[9], matrix[10], matrix[11],
            matrix[12], matrix[13], matrix[14], matrix[15] );
}

GLWidget::GLWidget( QWidget* pParent ):
    QGLWidget( pParent )
{
    setWindowTitle( "Test OpenGL Quaternion and matrix" );
}

void GLWidget::initializeGL( void )
{
    float angle = 30.0f;
    float x = 12.0f;
    float y = 8.0f;
    float z = 3.0f;


    float matrix1[16], matrix2[16];
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity( );
    glGetFloatv( GL_MODELVIEW_MATRIX, matrix1 );
    glGetFloatv( GL_MODELVIEW_MATRIX, matrix2 );

    printf( "The initial identity matrix is:\n" );
    PrintMatrix( matrix2 );
    printf( "Now perform OpenGL glRotate function.\n" );
    glRotatef( angle, x, y, z );
    glGetFloatv( GL_MODELVIEW_MATRIX, matrix1 );
    PrintMatrix( matrix1 );

    printf( "Now using quaternion to perform rotation.\n" );
    Quaternion::FromRotation( x, y, z, angle ).ToMatrix( matrix2 );
    PrintMatrix( matrix2 );
}

void GLWidget::paintGL( void )
{

}

    以下是运行结果。

    这说明上述运算是正确的。

展开阅读全文

没有更多推荐了,返回首页