zoukankan      html  css  js  c++  java
  • 四元数学习之四元数和矩阵的转换

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

             四元数是一种可以替代矩阵和欧拉角的数学工具。他最初是由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
    "
                "%8.2f%8.2f%8.2f%8.2f
    "
                "%8.2f%8.2f%8.2f%8.2f
    "
                "%8.2f%8.2f%8.2f%8.2f
    ",
                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:
    " );
        PrintMatrix( matrix2 );
        printf( "Now perform OpenGL glRotate function.
    " );
        glRotatef( angle, x, y, z );
        glGetFloatv( GL_MODELVIEW_MATRIX, matrix1 );
        PrintMatrix( matrix1 );
    
        printf( "Now using quaternion to perform rotation.
    " );
        Quaternion::FromRotation( x, y, z, angle ).ToMatrix( matrix2 );
        PrintMatrix( matrix2 );
    }
    
    void GLWidget::paintGL( void )
    {
    
    }

        以下是运行结果。

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

  • 相关阅读:
    vue分页组件(二)
    ES6里let、const、var区别总结
    electron-vue项目打包踩坑记录
    node环境变量配置
    第一次把本地项目与git相连
    java项目环境搭建
    npm上发布vue插件
    HTML5日期时间输入类型注意事项(time,date)
    vue 分页组件
    vue里ref ($refs)用法
  • 原文地址:https://www.cnblogs.com/bbsno1/p/3266744.html
Copyright © 2011-2022 走看看