四元数矩阵转化
- 格式:docx
- 大小:17.14 KB
- 文档页数:5
eigen 四元数变换矩阵-概述说明以及解释1.引言1.1 概述概述概述部分将介绍本篇文章的主题以及提供对Eigen库、四元数和变换矩阵的基本理解。
本文主要关注于介绍Eigen库中四元数与变换矩阵的相关概念和实现方法,并提供一些应用实例。
在图形学和机器人学等领域,四元数和变换矩阵是非常重要的数学工具。
四元数是一种数学结构,可用于表示和操作三维旋转的姿态。
它们广泛应用于姿态估计、路径规划和机器人控制等领域。
变换矩阵则是用于在三维空间中表示和处理旋转、平移和缩放等变换的数学工具。
它们在计算机图形学中被广泛使用,用于模型变换、相机投影和场景渲染等应用。
Eigen库是一个C++模板库,提供了高性能的线性代数和数值计算功能。
它具有简洁的接口和高度优化的实现,使得它成为处理数学计算和矩阵运算的首选工具。
Eigen库广泛应用于机器人学、图像处理和物理模拟等领域。
本文将首先介绍Eigen库的基本概念和使用方法,以帮助读者快速上手。
接着,将详细介绍四元数的定义、运算规则和几何意义,以及它们与变换矩阵之间的关系。
最后,将展示如何在Eigen库中使用四元数和变换矩阵,并提供一些实际应用实例,以加深读者对这些概念的理解和应用能力。
总结起来,本文将提供一个关于Eigen库、四元数和变换矩阵的综合指南,旨在帮助读者理解和应用这些重要的数学工具。
阅读本文后,读者将能够在自己的项目中有效地使用Eigen库中的四元数和变换矩阵,并将它们应用于机器人、图形学和相关领域的实际问题中。
1.2 文章结构文章结构部分的内容包括了引言、正文和结论三个部分。
引言部分是整篇文章的开头,用于引入文章的主题和背景,并对文章的主要内容进行概述。
在本篇文章中,引言部分主要包括概述、文章结构、目的和总结四个小节。
概述部分用于简要介绍文章的主题,即Eigen四元数变换矩阵。
文章结构部分用于介绍整篇文章的组织结构和各个部分的内容安排。
目的部分用于说明撰写本文的目的是什么,可以是解决某个问题、讨论某个现象或者介绍某个知识点等。
四元数学习之四元数和矩阵的转换四元数学习之四元数和矩阵的转换四元数是⼀种可以替代矩阵和欧拉⾓的数学⼯具。
他最初是由William Rowan Hamilton发现的(参考维基百科),它的最⼤的特点是不满⾜交换率。
也谈⼀下⾃⼰对这⼀点的体会。
在离散数学中有讲到半群、群、环和域的概念,其中环的定义是具有交换率和分配率(详情参考环的数学定义),⽽域的概念则是在环的基础上加上了交换率。
所以说四元数⽆法满⾜域的定义,它是除法环的⼀种。
何为除法环?其实很简单,被除数和除数都满⾜结合律和分配律,但是如果要满⾜交换律,即被除数和除数交换位置,那么它的结果是不同的(准确地说,如果它们不为0的话,那么结果呈倒数关系)。
⼜由于要在四维解空间上解得i3=-1,所以只能在不满⾜交换率的条件下得出i、j、k。
四元数在计算机图形学的优势在于运算量⼩和利于插值,且旋转没有缺陷。
可是普通的⼀个四元数定义⼜⽐较难懂,⽽且OpenGL的API 中⼜没有带四元数的参数的函数,所以需要我们根据公式做⼀做⼩⼩的转换。
设四元数Q(x, y, z, w)表⽰向量a(x a,y a, z a)经过α⾓旋转后的结果,则x、y、z和w分别为:x= sin(α/2)·x ay= sin(α/2)·y az= sin(α/2)·z aw= cos(α/2)在中讲到了如何⽤⼀个矩阵表⽰坐标系沿向量的旋转,这⾥我直接给出公式:将这两个公式结合起来。
再结合⾼中所学的半⾓公式:sinα = 2sin(α/2)·cos(α/2)cosα = cos2(α/2) - sin2(α/2)cos2(α/2) = (1 +cosα)/2sin2(α/2) = (1 -cosα)/2可以解出⽤四元数表⽰旋转矩阵为:该来验证⼀下公式的正确性,同样地,采⽤OpenGL托管的矩阵来测试,看看使⽤⾃带的glRotatef()函数和我们写的公式相⽐,究竟有没有差距。
glm 四元数转换矩阵-概述说明以及解释1.引言1.1 概述四元数(Quaternion)是数学中的一种扩展复数,广泛应用于3D计算机图形学和空间几何运算等领域。
它由一个实部和三个虚部组成,具有一些独特的性质和优点。
在图形学中,四元数被用于表示和计算物体的旋转,相比其他表示旋转的方法,如欧拉角和旋转矩阵,四元数具有更简洁和高效的计算方式。
本文将首先介绍球面线性插值(Spherical Linear Interpolation, 简称SLERP)的概念及其在计算机图形学中的应用。
接下来,我们将详细探讨四元数的定义和性质,包括四元数的运算法则、单位四元数的特点等。
最后,我们将重点讲解四元数与旋转矩阵之间的相互转换关系,包括如何将一个旋转矩阵转换为对应的四元数表示,以及如何从四元数恢复出旋转矩阵。
通过深入理解四元数与旋转矩阵之间的转换关系,我们可以更好地理解和应用四元数在3D图形学中的作用。
对于计算机图形学从业者来说,这是一个非常重要的基础知识。
此外,我们还将展望四元数在虚拟现实、计算机动画等领域的应用前景,并提出相关讨论和建议。
通过阅读本文,读者将能够理解四元数转换矩阵的原理和算法,并能够应用于实际问题中。
无论是从事计算机图形学研究还是从事相关行业工作的人士,本文的内容都将对他们的工作产生积极的影响和帮助。
总结起来,本文旨在为读者提供一份系统而全面的关于glm四元数转换矩阵的学习材料,并希望能够激发更多人对这一领域的兴趣和研究。
1.2文章结构文章结构部分的内容可以写成以下样式:2. 正文2.1 球面线性插值2.2 四元数的定义和性质2.3 四元数到旋转矩阵的转换在正文部分,我们将着重介绍GLM(OpenGL 数学库)中的四元数转换矩阵的相关知识。
首先,我们将会详细讨论球面线性插值算法的原理和应用,以便更好地理解四元数和矩阵之间的转换关系。
接下来,我们将会介绍四元数的定义和性质。
四元数是一种复数的扩展形式,具有独特的性质和运算规则。
球形四元数和旋转矩阵
球形四元数是一种用来表示三维空间中的旋转的数学工具,它可以与旋转矩阵进行互相转换。
球形四元数通常表示为q = (w, x, y, z),其中w是实部,(x, y, z)是虚部。
它们有以下性质:
1. 平方和等于1:w^2 + x^2 + y^2 + z^2 = 1
2. 与旋转矩阵的关系:给定一个单位四元数q = (w, x, y, z),对应的旋转矩阵R可以表示为:
R = | 1 - 2y^2 - 2z^2 2xy - 2wz 2xz + 2wy | | 2xy + 2wz 1 - 2x^2 - 2z^2 2yz - 2wx |
| 2xz - 2wy 2yz + 2wx 1 - 2x^2 - 2y^2 |
3. 从旋转矩阵到四元数的转换:根据旋转矩阵的信息可以计算出对应的四元数。
四元数和旋转矩阵都是用来描述旋转的数学工具,它们可以用来表示三维空间中的任意旋转。
在实际应用中,它们有着不同的优势和适用场景,可以根据具体情况选择使用其中的一种或者进行相互转换。
四元数乘法矩阵形式
我们要找出四元数乘法的矩阵形式。
首先,我们需要了解四元数的基本定义和乘法规则。
四元数是由一个实数和三个虚部的复数组成的,通常表示为 q = w + xi +
yj + zk,其中 w, x, y, z 是实数,i, j, k 是虚部单位,满足 i^2 = j^2 = k^2 = -1。
四元数的乘法规则如下:
1. 实部之间和虚部之间不进行乘法。
2. 虚部单位 i, j, k 之间进行叉积运算。
用数学公式,我们可以表示为:
(w+xi+yj+zk) × (w'+x'i+y'j+z'k) = (ww' - x'x - y'y - z'z) + (w'x + x'w + y'z - z'y)i + (w'y - x'z + x'y + z'w)j + (w'z + x'y - x'z + y'w)k
为了将这个公式转换为矩阵形式,我们可以将每个虚部单位视为一个列向量,并使用矩阵乘法来表示它们之间的叉积。
计算结果为:(2, 2, -2, 2)
所以,四元数乘法的矩阵形式为:
实部:ww' - x'x - y'y - z'z 虚部i:w'x + x'w + y'z - z'y 虚部j:w'y - x'z + x'y + z'w 虚部k:w'z + x'y - x'z + y'w。
旋转矩阵到四元数的转换python在计算机图形学和计算机视觉中,旋转是一个非常重要的操作。
旋转矩阵是用来描述物体在三维空间中的旋转的一种数学工具。
然而,在某些情况下,我们可能需要使用四元数来表示旋转。
本文将介绍如何将旋转矩阵转换为四元数的方法。
1. 旋转矩阵的表示旋转矩阵是一个3x3的矩阵,用来描述物体绕着某个轴旋转的情况。
旋转矩阵通常用R表示,其中R的每一列都是物体坐标系中的一个基向量在世界坐标系中的表示。
例如,R的第一列表示物体坐标系的x轴在世界坐标系中的表示。
2. 四元数的表示四元数是一种数学工具,可以用来表示三维空间中的旋转。
一个四元数可以写成q = s + xi + yj + zk的形式,其中s是实部,(x, y, z)是虚部。
四元数可以进行加法、减法和乘法运算,还可以进行归一化操作。
3. 旋转矩阵到四元数的转换旋转矩阵到四元数的转换可以通过以下步骤实现:步骤1:计算旋转矩阵的迹旋转矩阵的迹是指对角线元素的和,可以表示为trace(R) = R11 + R22 + R33。
步骤2:根据迹的值选择适当的计算公式根据旋转矩阵的迹的值,可以选择不同的计算公式来计算四元数的各个分量。
当迹的值大于0时,计算公式如下:s = sqrt(trace(R) + 1) / 2x = (R32 - R23) / (4s)y = (R13 - R31) / (4s)z = (R21 - R12) / (4s)当迹的值小于等于0时,需要进一步判断迹的最大元素的位置:如果R11是迹的最大元素,则计算公式如下:s = sqrt(R11 - R22 - R33 + 1) / 2x = (R12 + R21) / (4s)y = (R13 + R31) / (4s)z = (R23 - R32) / (4s)如果R22是迹的最大元素,则计算公式如下:s = sqrt(R22 - R11 - R33 + 1) / 2x = (R21 + R12) / (4s)y = (R23 + R32) / (4s)z = (R31 - R13) / (4s)如果R33是迹的最大元素,则计算公式如下:s = sqrt(R33 - R11 - R22 + 1) / 2x = (R31 + R13) / (4s)y = (R32 + R23) / (4s)z = (R12 - R21) / (4s)步骤3:归一化四元数将四元数的实部和虚部都除以s,即可得到归一化的四元数。
四元数,欧拉角,矩阵的相互转换网上太多的将转换的了,翻来覆去转载没有意义。
奉上源码,TC下直接编译即可~~在附上编译好了的exe可以直接下载运行~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~不华丽的分割~~以下是源码~~~~~~~~~~~~~~~~~~~~~~/* 输入欧拉角,能看到四元数,以及再转换回去成欧拉角Yaw范围(-180~180)Pitch范围(-90~90)Roll范围(-180~180)*/#include "stdio.h"#include "math.h"#include"conio.h"main(){float theta_z , theta_y ,theta_x ;floatcos_z_2;float cos_y_2;float cos_x_2;float sin_z_2;floatsin_y_2;float sin_x_2;float Pitch;float Roll;float Yaw;floatQ[4];float T[3][3];do{printf("/nYaw =");scanf("%f",&theta_z);printf("/nPitch =");scanf("%f",&theta_y);printf("/nRoll =");scanf("%f",&theta_x);theta_z =theta_z*3.1416/180;theta_y = theta_y*3.1416/180;theta_x = theta_x*3.1416/180;cos_z_2 = cos(0.5*theta_z);cos_y_2= cos(0.5*theta_y);cos_x_2 = cos(0.5*theta_x);sin_z_2 = sin(0.5*theta_z);sin_y_2 = sin(0.5*theta_y);sin_x_2 =sin(0.5*theta_x);Q[0] = cos_z_2*cos_y_2*cos_x_2 +sin_z_2*sin_y_2*sin_x_2;Q[1] = cos_z_2*cos_y_2*sin_x_2 - sin_z_2*sin_y_2*cos_x_2;Q[2] = cos_z_2*sin_y_2*cos_x_2 + sin_z_2*cos_y_2*sin_x_2;Q[3] =sin_z_2*cos_y_2*cos_x_2 -cos_z_2*sin_y_2*sin_x_2;printf("/nQ=[ %f %f %f %f]/n/n",Q [0],Q[1],Q[2],Q[3]) ;printf("alpha= %f/n/n",acos(Q[0])*2*180/3.1416) ;T[0][0] =Q[0]*Q[0]+Q[1]*Q[1]-Q[2]*Q[2]-Q[3]*Q[3] ;T[0][1] =2*(Q[1]*Q[2]-Q[0]*Q[3]);T[0][2] =2*(Q[1]*Q[3]+Q[0]*Q[2]);T[1][0] =2*(Q[1]*Q[2]+Q[0]*Q[3]);T[1][1] =Q[0]*Q[0]-Q[1]*Q[1]+Q[2]*Q[2]-Q[3]*Q[3] ;T[1][2] =2*(Q[2]*Q[3]-Q[0]*Q[1]);T[2][0] =2*(Q[1]*Q[3]-Q[0]*Q[2]);T[2][1] =2*(Q[2]*Q[3]+Q[0]*Q[1]);T[2][2] =Q[0]*Q[0]-Q[1]*Q[1]-Q[2]*Q[2]+Q[3]*Q[3] ;printf("T[0][0]= %9f,T[0][1] = %9f,T[0][2]= %9f/n",T[0][0],T[0][1],T[0][2]);printf("T[1][0] = %9f,T[1][1] = %9f,T[1][2] = %9f/n",T[1][0],T[1][1],T[1][2]);printf("T[2][0]= %9f,T[2][1] = %9f,T[2][2]= %9f/n/n",T[2][0],T[2][1],T[2][2]);Pitch = asin(-T[2][0]);Roll = atan( T[2][1]/T[2][2]);Yaw =atan( T[1][0]/T[0][0]);if(T[2][2]{if(Roll {Roll =Roll+3.1416;}else{Roll =Roll-3.1416;}}if(T[0][0]{if(T[1][0]>0){Yaw = Yaw +3.1416;}else{Yaw = Yaw - 3.1416;}}printf("Yaw = %f/nPitch = %f/nRoll= %f/n",Yaw*180/3.1416,Pitch*180/3.1416,Roll*180/3.141 6) ;}while(1);printf("Hello, world/n");getch();}。
欧拉角转四元数转旋转矩阵欧拉角、四元数和旋转矩阵是常用的表示旋转的方法。
在计算机图形学、机器人学等领域广泛应用。
本文将介绍如何将欧拉角转换为四元数,以及如何将四元数转换为旋转矩阵。
欧拉角是表示旋转的一种方式,它包括三个角度值:俯仰角、偏航角和滚转角。
欧拉角的表示方法有很多种,如 XYZ、ZYZ 等。
这里我们以 XYZ 欧拉角为例。
XYZ 欧拉角表示先绕 x 轴旋转一定角度,再绕 y 轴旋转一定角度,最后绕 z 轴旋转一定角度。
欧拉角的旋转顺序是很重要的,不同的旋转顺序会得到不同的旋转结果。
四元数是一种表示旋转的数学工具,它包含一个实部和三个虚部,可以用一个四维向量表示。
四元数的运算比矩阵运算更快,也更容易组合多个旋转。
四元数旋转的基本原理是将旋转轴和旋转角度转换为一个四元数,然后将这个四元数与待旋转向量相乘得到旋转后的向量。
旋转矩阵是一个 3x3 的矩阵,它可以用来将一个向量绕某个轴旋转一定角度。
旋转矩阵的每一列表示旋转后的 x、y、z 轴方向的向量。
旋转矩阵的乘法是不可交换的,即不同的旋转顺序会得到不同的旋转结果。
下面将介绍如何将欧拉角转换为四元数,以及如何将四元数转换为旋转矩阵。
1. 欧拉角转四元数将 XYZ 欧拉角转换为四元数的公式如下:q = cos(roll/2) * cos(pitch/2) * cos(yaw/2) + sin(roll/2)* sin(pitch/2) * sin(yaw/2) * i- sin(roll/2) * cos(pitch/2) * sin(yaw/2) * j + cos(roll/2) * sin(pitch/2) * sin(yaw/2) * k其中,roll、pitch、yaw 分别表示滚转角、俯仰角和偏航角。
i、j、k 分别表示四元数的虚部,它们满足 i^2=j^2=k^2=ijk=-1。
2. 四元数转旋转矩阵将四元数转换为旋转矩阵的公式如下:R = [1-2*(qj^2+qk^2), 2*(qi*qj-qk*qr), 2*(qi*qk+qj*qr)] [2*(qi*qj+qk*qr), 1-2*(qi^2+qk^2), 2*(qj*qk-qi*qr)][2*(qi*qk-qj*qr), 2*(qj*qk+qi*qr), 1-2*(qi^2+qj^2)] 其中,q 是一个四元数,qi、qj、qk、qr 分别表示四元数的虚部和实部。
eigen中旋转矩阵与四元数之间的相互转换Eigen是一个用于线性代数的C++库,其中包含多种表示旋转的方式,包括旋转矩阵和四元数。
Eigen通过提供旋转矩阵和四元数之间的相互转换函数,使得用户可以在两种表示形式之间便捷地转换。
旋转矩阵(Rotation Matrix)是一个3x3的正交矩阵,表示从一个坐标系向另一个坐标系进行旋转的变换矩阵。
旋转矩阵的每一行(或每一列)都是一个单位向量并且互相垂直,因此旋转矩阵具有正交性质。
在Eigen中,旋转矩阵可以用Matrix3d或MatrixXd表示,其中Matrix3d表示一个静态大小的3x3矩阵,而MatrixXd表示一个动态大小的矩阵。
四元数(Quaternion)是一个具有实部和虚部的扩充复数,其中虚部是一个三维向量。
四元数可以用来表示任意旋转,其旋转轴与旋转角度可以从虚部中获取。
在Eigen中,四元数可以用Quaterniond 或QuaternionXd表示,其中Quaterniond表示双精度精度的四元数,而QuaternionXd可以表示任意大小的四元数。
旋转矩阵到四元数的转换可以使用RotationMatrixToQuaternion 函数,该函数可以将一个旋转矩阵转换为一个四元数。
同时,四元数到矩阵的转换可以使用QuaternionToRotationMatrix函数,该函数可以将一个四元数转换为一个旋转矩阵。
下面是旋转矩阵到四元数的转换代码示例:```c++Eigen::Matrix3d rotation_matrix;Eigen::Quaterniond quaternion;// 将旋转矩阵赋值给rotation_matrix// ...quaternion = Eigen::Quaterniond(rotation_matrix);```下面是四元数到旋转矩阵的转换代码示例:```c++Eigen::Quaterniond quaternion;Eigen::Matrix3d rotation_matrix;// 将四元数赋值给quaternion// ...rotation_matrix = quaternion.toRotationMatrix();```使用Eigen中的旋转矩阵和四元数相互转换函数可以方便地在两个不同的表示形式之间进行转换,从而适应不同的应用需求。
//公式都是网上搜罗的,下面这些经过简单的测试,确认可用。
//ps: x,y,z,w 分别是四元素的四个值。
稍微修改下就可以用。
// 由旋转矩阵创建四元数
inline CQuaternion(const_Matrix4& m)
{
float tr, s, q[4];
int i, j, k;
int nxt[3] = {1, 2, 0 };
// 计算矩阵轨迹
tr = m._11 + m._22 + m._33;
// 检查矩阵轨迹是正还是负
if(tr>0.0f)
{
s = sqrt(tr + 1.0f);
this->w = s / 2.0f;
s = 0.5f / s;
this->x = (m._23 - m._32) * s;
this->y = (m._31 - m._13) * s;
this->z = (m._12 - m._21) * s;
}
else
{
// 轨迹是负
// 寻找m11 m22 m33中的最大分量
i = 0;
if(m.m[1][1]>m.m[0][0]) i = 1;
if(m.m[2][2]>m.m[i][i]) i = 2;
j = nxt[i];
k = nxt[j];
s = sqrt((m.m[i][i] - (m.m[j][j] + m.m[k][k])) + 1.0f); q[i] = s * 0.5f;
if( s!= 0.0f) s = 0.5f / s;
q[3] = (m.m[j][k] - m.m[k][j]) * s;
q[j] = (m.m[i][j] - m.m[j][i]) * s;
q[k] = (m.m[i][k] - m.m[k][i]) * s;
this->x = q[0];
this->y = q[1];
this->z = q[2];
this->w = q[3];
}
};
// 由欧拉角创建四元数
inline CQuaternion(const_Vector3& angle)
{
float cx = cos(angle.x/2);
float sx = sin(angle.x/2);
float cy = cos(angle.y/2);
float sy = sin(angle.y/2);
float cz = cos(angle.z/2);
float sz = sin(angle.z/2);
this->w = cx*cy*cz + sx*sy*sz;
this->x = sx*cy*cz - cx*sy*sz;
this->y = cx*sy*cz + sx*cy*sz;
this->z = cx*cy*sz - sx*sy*cz;
};
// 给定角度和轴创建四元数
inline CQuaternion(_Vector3 anxi, const float& angle) {
CVector3 t;
t.x = anxi.x;
t.y = anxi.y;
t.z = anxi.z;
t.Normalize();
float cosa = cos(angle);
float sina = sin(angle);
this->w = cosa;
this->x = sina * t.x;
this->y = sina * t.y;
this->z = sina * t.z;
};
// 由旋转四元数推导出矩阵
inline CMatrix4 GetMatrixLH()
{
CMatrix4 ret;
float xx = x*x;
float yy = y*y;
float zz = z*z;
float xy = x*y;
float wz = w*z;
float wy = w*y;
float xz = x*z;
float yz = y*z;
float wx = w*x;
ret._11 = 1.0f-2*(yy+zz);
ret._12 = 2*(xy-wz);
ret._13 = 2*(wy+xz);
ret._14 = 0.0f;
ret._21 = 2*(xy+wz);
ret._22 = 1.0f-2*(xx+zz);
ret._23 = 2*(yz-wx);
ret._24 = 0.0f;
ret._31 = 2*(xy-wy);
ret._32 = 2*(yz+wx);
ret._33 = 1.0f-2*(xx+yy);
ret._34 = 0.0f;
ret._41 = 0.0f;
ret._42 = 0.0f;
ret._43 = 0.0f;
ret._44 = 1.0f;
return ret;
};
inline CMatrix4 GetMatrixRH() {
CMatrix4 ret;
float xx = x*x;
float yy = y*y;
float zz = z*z;
float xy = x*y;
float wz = -w*z;
float wy = -w*y;
float xz = x*z;
float yz = y*z;
float wx = -w*x;
ret._11 = 1.0f-2*(yy+zz);
ret._12 = 2*(xy-wz);
ret._13 = 2*(wy+xz);
ret._14 = 0.0f;
ret._21 = 2*(xy+wz);
ret._22 = 1.0f-2*(xx+zz);
ret._23 = 2*(yz-wx);
ret._24 = 0.0f;
ret._31 = 2*(xy-wy);
ret._32 = 2*(yz+wx);
ret._33 = 1.0f-2*(xx+yy);
ret._34 = 0.0f;
ret._41 = 0.0f;
ret._42 = 0.0f;
ret._43 = 0.0f;
ret._44 = 1.0f;
return ret;
};
// 由四元数返回欧拉角(主要是这个dx api里没有提供)
inline CVector3 GetEulerAngle()
{
CVector3 ret;
float test = y*z + x*w;
if(test > 0.4999f)
{
ret.z = 2.0f * atan2(y, w);
ret.y = PIOver2;
ret.x = 0.0f;
return ret;
}
if(test < -0.4999f)
{
ret.z = 2.0f * atan2(y, w);
ret.y = -PIOver2;
ret.x = 0.0f;
return ret;
}
float sqx = x * x;
float sqy = y * y;
float sqz = z * z;
ret.z = atan2(2.0f * z * w - 2.0f * y * x, 1.0f - 2.0f * sqz - 2.0f * sqx);
ret.y = asin(2.0f * test);
ret.x = atan2(2.0f * y * w - 2.0f * z * x, 1.0f - 2.0f * sqy - 2.0f * sqx);
return ret;
};。