使用向上向量查看四元数 [英] Look-at quaternion using up vector

查看:52
本文介绍了使用向上向量查看四元数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个相机(在自定义 3D 引擎中),它接受旋转变换的四元数.我有两个 3D 点代表一个相机和一个要查看的对象.我想计算从相机到物体的四元数,同时尊重世界上轴.

与向量相乘相当于使用向量的分量作为相机基础中的坐标.

3x3 旋转矩阵可以转换为四元数转换为角度/使用昂贵的三角函数.下面是一个数值稳定的 C++ 代码片段,它返回一个标准化的四元数:

inline void CalculateRotation( Quaternion& q ) const {浮动跟踪 = a[0][0] + a[1][1] + a[2][2];如果(跟踪> 0){浮动 s = 0.5f/sqrtf(trace + 1.0f);q.w = 0.25f/s;q.x = ( a[2][1] - a[1][2] ) * s;q.y = ( a[0][2] - a[2][0] ) * s;q.z = ( a[1][0] - a[0][1] ) * s;} 别的 {如果 ( a[0][0] > a[1][1] && a[0][0] > a[2][2] ) {浮动 s = 2.0f * sqrtf( 1.0f + a[0][0] - a[1][1] - a[2][2]);q.w = (a[2][1] - a[1][2])/s;q.x = 0.25f * s;q.y = (a[0][1] + a[1][0])/s;q.z = (a[0][2] + a[2][0])/s;} else if (a[1][1] > a[2][2]) {浮动 s = 2.0f * sqrtf( 1.0f + a[1][1] - a[0][0] - a[2][2]);q.w = (a[0][2] - a[2][0])/s;q.x = (a[0][1] + a[1][0])/s;q.y = 0.25f * s;q.z = (a[1][2] + a[2][1])/s;} 别的 {浮动 s = 2.0f * sqrtf( 1.0f + a[2][2] - a[0][0] - a[1][1] );q.w = (a[1][0] - a[0][1])/s;q.x = (a[0][2] + a[2][0])/s;q.y = (a[1][2] + a[2][1])/s;q.z = 0.25f * s;}}}

来源:http://www.euclideanspace.com/maths/geometry/旋转/转换/matrixToQuaternion

将其转换为适合您的情况当然只是将矩阵元素与相应的向量分量交换的问题:

//你之前的代码F = 标准化(目标 - 相机);//看着R = normalize(cross(F, worldUp));//侧轴U = 交叉(R,F);//向上旋转//注意 R 需要重新归一化//因为 F 和 worldUp 不需要垂直//所以必须去除叉积的 sin(angle) 因子//U 不是这样,因为 dot(R, F) = 0//适配源四元数 q;双迹 = R.x + U.y + F.z;如果(跟踪> 0.0){双 s = 0.5/sqrt(trace + 1.0);q.w = 0.25/s;q.x = (U.z - F.y) * s;q.y = (F.x - R.z) * s;q.z = (R.y - U.x) * s;} 别的 {如果 (R.x > U.y && R.x > F.z) {双 s = 2.0 * sqrt(1.0 + R.x - U.y - F.z);q.w = (U.z - F.y)/s;q.x = 0.25 * s;q.y = (U.x + R.y)/s;q.z = (F.x + R.z)/s;} else if (U.y > F.z) {双 s = 2.0 * sqrt(1.0 + U.y - R.x - F.z);q.w = (F.x - R.z)/s;q.x = (U.x + R.y)/s;q.y = 0.25 * s;q.z = (F.y + U.z)/s;} 别的 {双 s = 2.0 * sqrt(1.0 + F.z - R.x - U.y);q.w = (R.y - U.x)/s;q.x = (F.x + R.z)/s;q.y = (F.y + U.z)/s;q.z = 0.25 * s;}}

(如果你使用 OpenGL,不用说交换 yz.)

I have a camera (in a custom 3D engine) that accepts a quaternion for the rotation transform. I have two 3D points representing a camera and an object to look at. I want to calculate the quaternion that looks from the camera to the object, while respecting the world up axis.

This question asks for the same thing without the "up" vector. All three answers result in the camera pointing in the correct direction, but rolling (as in yaw/pitch/roll; imagine leaning your head onto your ear while looking at something).

I can calculate an orthonormal basis of vectors that match the desired coordinate system by:

lookAt = normalize(target - camera)
sideaxis = cross(lookAt, worldUp)
rotatedup = cross(sideaxis, lookAt)

How can I create a quaternion from those three vectors? This question asks for the same thing...but unfortunately the only and accepted answer says ~"let's assume you don't care about roll", and then goes about ignoring the up axis. I do care about roll. I don't want to ignore the up axis.

解决方案

A previous answer has given a valid solution using angles. This answer will present an alternative method.

The orthonormal basis vectors, renaming them F = lookAt, R = sideaxis, U = rotatedup, directly form the columns of the 3x3 rotation matrix which is equivalent to your desired quaternion:

Multiplication with a vector is equivalent to using said vector's components as the coordinates in the camera's basis.

A 3x3 rotation matrix can be converted into a quaternion without conversion to angles / use of costly trigonometric functions. Below is a numerically stable C++ snippet which does this, returning a normalized quaternion:

inline void CalculateRotation( Quaternion& q ) const {
  float trace = a[0][0] + a[1][1] + a[2][2];
  if( trace > 0 ) {
    float s = 0.5f / sqrtf(trace + 1.0f);
    q.w = 0.25f / s;
    q.x = ( a[2][1] - a[1][2] ) * s;
    q.y = ( a[0][2] - a[2][0] ) * s;
    q.z = ( a[1][0] - a[0][1] ) * s;
  } else {
    if ( a[0][0] > a[1][1] && a[0][0] > a[2][2] ) {
      float s = 2.0f * sqrtf( 1.0f + a[0][0] - a[1][1] - a[2][2]);
      q.w = (a[2][1] - a[1][2] ) / s;
      q.x = 0.25f * s;
      q.y = (a[0][1] + a[1][0] ) / s;
      q.z = (a[0][2] + a[2][0] ) / s;
    } else if (a[1][1] > a[2][2]) {
      float s = 2.0f * sqrtf( 1.0f + a[1][1] - a[0][0] - a[2][2]);
      q.w = (a[0][2] - a[2][0] ) / s;
      q.x = (a[0][1] + a[1][0] ) / s;
      q.y = 0.25f * s;
      q.z = (a[1][2] + a[2][1] ) / s;
    } else {
      float s = 2.0f * sqrtf( 1.0f + a[2][2] - a[0][0] - a[1][1] );
      q.w = (a[1][0] - a[0][1] ) / s;
      q.x = (a[0][2] + a[2][0] ) / s;
      q.y = (a[1][2] + a[2][1] ) / s;
      q.z = 0.25f * s;
    }
  }
}

Source: http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion

Converting this to suit your situation is of course just a matter of swapping the matrix elements with the corresponding vector components:

// your code from before
F = normalize(target - camera);   // lookAt
R = normalize(cross(F, worldUp)); // sideaxis
U = cross(R, F);                  // rotatedup

// note that R needed to be re-normalized
// since F and worldUp are not necessary perpendicular
// so must remove the sin(angle) factor of the cross-product
// same not true for U because dot(R, F) = 0

// adapted source
Quaternion q;
double trace = R.x + U.y + F.z;
if (trace > 0.0) {
  double s = 0.5 / sqrt(trace + 1.0);
  q.w = 0.25 / s;
  q.x = (U.z - F.y) * s;
  q.y = (F.x - R.z) * s;
  q.z = (R.y - U.x) * s;
} else {
  if (R.x > U.y && R.x > F.z) {
    double s = 2.0 * sqrt(1.0 + R.x - U.y - F.z);
    q.w = (U.z - F.y) / s;
    q.x = 0.25 * s;
    q.y = (U.x + R.y) / s;
    q.z = (F.x + R.z) / s;
  } else if (U.y > F.z) {
    double s = 2.0 * sqrt(1.0 + U.y - R.x - F.z);
    q.w = (F.x - R.z) / s;
    q.x = (U.x + R.y) / s;
    q.y = 0.25 * s;
    q.z = (F.y + U.z) / s;
  } else {
    double s = 2.0 * sqrt(1.0 + F.z - R.x - U.y);
    q.w = (R.y - U.x) / s;
    q.x = (F.x + R.z) / s;
    q.y = (F.y + U.z) / s;
    q.z = 0.25 * s;
  }
}

(And needless to say swap y and z if you're using OpenGL.)

这篇关于使用向上向量查看四元数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆