c++ - 如何在 3D 世界中获取对象相对于父对象的绝对位置?
问题描述
我正在构建一个仅用于学习的 3D 引擎,并且我有一个对象是另一个对象的父对象。并且这个父对象具有相对于父对象的位置。所以我想知道如果父对象有旋转和缩放,如何获得该对象的位置。
我已经进入 Unity 看看情况如何,我看到他们使用正弦和余弦进行旋转,但我不知道如何。我想使用父母的前锋,但这只有在孩子的位置是(0,0,1)时才有效,对吧?因为如果它的位置不是那应该是什么?空间中两个位置之间唯一剩下的操作是将它们相乘(矩阵乘法)(因为添加它们不会得到我们想要的),但我想我只是放弃了这些选项。
我没有代码,因为我不知道从哪里开始,但主要是我复制了 Unity 的类和结构名称。为了对相机进行投影,我使用了 Wikipedia 中的 3D 投影页面 ( https://en.wikipedia.org/wiki/3D_projection#Perspective_projection )。
我使用四元数进行旋转(也与讨论欧拉到四元数和四元数到欧拉的维基百科页面:https ://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles#Source_Code )。
我认为这是关于代码的足够信息。
最后,我想让 (0,0,1) 中的孩子成为 (0,0,0) 中旋转到 (0,90,0) 的对象的父级,孩子的世界位置是 (1 ,0,0)。
我将这种方法与 3x3 矩阵而不是 4x4 一起使用,这些是功能:
inline Vector3 getPositionAbsoluteFromRelative(const Vector3 &relativeposition, const Vector3 &parentrotation) {
return Vector3(
relativeposition.x * (1 - 2 * (parentrotation.y * parentrotation.y + parentrotation.z * parentrotation.z)) + relativeposition.y * 2 * (parentrotation.x * parentrotation.y - parentrotation.w * parentrotation.z) + relativeposition.z * 2 * (parentrotation.x * parentrotation.z + parentrotation.w * parentrotation.y),
relativeposition.x * 2 * (parentrotation.x * parentrotation.y + parentrotation.w * parentrotation.z) + relativeposition.y * (1 - 2 * (parentrotation.x * parentrotation.x + parentrotation.z * parentrotation.z)) + relativeposition.z * 2 * (parentrotation.y * parentrotation.z - parentrotation.w * parentrotation.x),
relativeposition.x * 2 * (parentrotation.x * parentrotation.z - parentrotation.w * parentrotation.y) + relativeposition.y * 2 * (parentrotation.y * parentrotation.z + parentrotation.w * parentrotation.x) + relativeposition.z * (1 - 2 * (parentrotation.x * parentrotation.x + parentrotation.y * parentrotation.y))
);
};
inline Vector3 getPositionAbsoluteFromRelative(const Vector3 &relativeposition, const Vector3 &parentrotation, const Vector3 &parentposition) {
Vector3 absolute(
relativeposition.x * (1 - 2 * (parentrotation.y * parentrotation.y + parentrotation.z * parentrotation.z)) + relativeposition.y * 2 * (parentrotation.x * parentrotation.y - parentrotation.w * parentrotation.z) + relativeposition.z * 2 * (parentrotation.x * parentrotation.z + parentrotation.w * parentrotation.y),
relativeposition.x * 2 * (parentrotation.x * parentrotation.y + parentrotation.w * parentrotation.z) + relativeposition.y * (1 - 2 * (parentrotation.x * parentrotation.x + parentrotation.z * parentrotation.z)) + relativeposition.z * 2 * (parentrotation.y * parentrotation.z - parentrotation.w * parentrotation.x),
relativeposition.x * 2 * (parentrotation.x * parentrotation.z - parentrotation.w * parentrotation.y) + relativeposition.y * 2 * (parentrotation.y * parentrotation.z + parentrotation.w * parentrotation.x) + relativeposition.z * (1 - 2 * (parentrotation.x * parentrotation.x + parentrotation.y * parentrotation.y))
);
return Vector3(
absolute.x + parentposition.x,
absolute.y + parentposition.y,
absolute.z + parentposition.z
);
};
inline Vector3 getPositionAbsoluteFromRelative(const Vector3 &relativeposition, const Vector3 &parentrotation, const Vector3 &parentposition, const Vector3 &parentscale) {
Vector3 absolute(
relativeposition.x * (1 - 2 * (parentrotation.y * parentrotation.y + parentrotation.z * parentrotation.z)) + relativeposition.y * 2 * (parentrotation.x * parentrotation.y - parentrotation.w * parentrotation.z) + relativeposition.z * 2 * (parentrotation.x * parentrotation.z + parentrotation.w * parentrotation.y),
relativeposition.x * 2 * (parentrotation.x * parentrotation.y + parentrotation.w * parentrotation.z) + relativeposition.y * (1 - 2 * (parentrotation.x * parentrotation.x + parentrotation.z * parentrotation.z)) + relativeposition.z * 2 * (parentrotation.y * parentrotation.z - parentrotation.w * parentrotation.x),
relativeposition.x * 2 * (parentrotation.x * parentrotation.z - parentrotation.w * parentrotation.y) + relativeposition.y * 2 * (parentrotation.y * parentrotation.z + parentrotation.w * parentrotation.x) + relativeposition.z * (1 - 2 * (parentrotation.x * parentrotation.x + parentrotation.y * parentrotation.y))
);
return Vector3(
absolute.x * parentscale.x + parentposition.x,
absolute.y * parentscale.y + parentposition.y,
absolute.z * parentscale.z + parentposition.z
);
};
inline Vector3 getPositionRelativeFromAbsolute(const Vector3 &absoluteposition, const Vector3 &parentrotation) {
return Vector3(
absoluteposition.x * (1 - 2 * (parentrotation.y * parentrotation.y + parentrotation.z * parentrotation.z)) + absoluteposition.y * 2 * (parentrotation.x * parentrotation.y + parentrotation.w * parentrotation.z) + absoluteposition.z * 2 * (parentrotation.x * parentrotation.z - parentrotation.w * parentrotation.y),
absoluteposition.x * 2 * (parentrotation.x * parentrotation.y - parentrotation.w * parentrotation.z) + absoluteposition.y * (1 - 2 * (parentrotation.x * parentrotation.x + parentrotation.z * parentrotation.z)) + absoluteposition.z * 2 * (parentrotation.y * parentrotation.z + parentrotation.w * parentrotation.x),
absoluteposition.x * 2 * (parentrotation.x * parentrotation.z + parentrotation.w * parentrotation.y) + absoluteposition.y * 2 * (parentrotation.y * parentrotation.z - parentrotation.w * parentrotation.x) + absoluteposition.z * (1 - 2 * (parentrotation.x * parentrotation.x + parentrotation.y * parentrotation.y))
);
};
inline Vector3 getPositionRelativeFromAbsolute(const Vector3 &absoluteposition, const Vector3 &parentrotation, const Vector3 &parentposition) {
Vector3 __position = {
absoluteposition.x - parentposition.x,
absoluteposition.y - parentposition.y,
absoluteposition.z - parentposition.z
};
return Vector3(
__position.x * (1 - 2 * (parentrotation.y * parentrotation.y + parentrotation.z * parentrotation.z)) + __position.y * 2 * (parentrotation.x * parentrotation.y + parentrotation.w * parentrotation.z) + __position.z * 2 * (parentrotation.x * parentrotation.z - parentrotation.w * parentrotation.y),
__position.x * 2 * (parentrotation.x * parentrotation.y - parentrotation.w * parentrotation.z) + __position.y * (1 - 2 * (parentrotation.x * parentrotation.x + parentrotation.z * parentrotation.z)) + __position.z * 2 * (parentrotation.y * parentrotation.z + parentrotation.w * parentrotation.x),
__position.x * 2 * (parentrotation.x * parentrotation.z + parentrotation.w * parentrotation.y) + __position.y * 2 * (parentrotation.y * parentrotation.z - parentrotation.w * parentrotation.x) + __position.z * (1 - 2 * (parentrotation.x * parentrotation.x + parentrotation.y * parentrotation.y))
);
};
inline Vector3 getPositionRelativeFromAbsolute(const Vector3 &absoluteposition, const Vector3 &parentrotation, const Vector3 &parentposition, const Vector3 &parentscale) {
Vector3 __position = {
absoluteposition.x - parentposition.x,
absoluteposition.y - parentposition.y,
absoluteposition.z - parentposition.z
};
Vector3 relative(
__position.x * (1 - 2 * (parentrotation.y * parentrotation.y + parentrotation.z * parentrotation.z)) + __position.y * 2 * (parentrotation.x * parentrotation.y + parentrotation.w * parentrotation.z) + __position.z * 2 * (parentrotation.x * parentrotation.z - parentrotation.w * parentrotation.y),
__position.x * 2 * (parentrotation.x * parentrotation.y - parentrotation.w * parentrotation.z) + __position.y * (1 - 2 * (parentrotation.x * parentrotation.x + parentrotation.z * parentrotation.z)) + __position.z * 2 * (parentrotation.y * parentrotation.z + parentrotation.w * parentrotation.x),
__position.x * 2 * (parentrotation.x * parentrotation.z + parentrotation.w * parentrotation.y) + __position.y * 2 * (parentrotation.y * parentrotation.z - parentrotation.w * parentrotation.x) + __position.z * (1 - 2 * (parentrotation.x * parentrotation.x + parentrotation.y * parentrotation.y))
);
return Vector3(relative.x / parentscale.x, relative.y / parentscale.y, relative.z / parentscale.z);
};
解决方案
3d 对象的位置和方向可以组合并存储在 4x4 矩阵中,同时
- 位置是从世界原点到对象原点的平移
- 方向是围绕对象原点的旋转。
为什么是 4x4 矩阵?
所有常见的变换(平移、旋转、缩放、剪切、投影)都可以表示为这样的矩阵。
变换的串联等价于相应矩阵的乘法。
虽然将一系列转换函数组合成一个可能很困难(而且不灵活),但很容易预乘矩阵(表示转换序列),以便可以一次应用所有转换(通过一次矩阵乘法)。
这就是 4×4 矩阵在 3D 合成中如此普遍的原因。图形。
OP的例子:
pos child ' = M parent · pos child
尽管
M父母= T父母· R父母。
在代码中:
#include <iostream>
#include "linmath.h"
int main()
{
Vec3f posChild(0.0f, 0.0f, 1.0f);
Vec3f posParent(0.0f, 0.0f, 0.0f);
float abcParent[] = { 0.0f, 90.0f, 0.0f };
// child pos as homogeneous coordinate
Vec4f posChildH(posChild, 1.0f);
// compose parent matrix of pos and ori
Mat4x4f matParent
= Mat4x4f(InitTrans, posParent)
* makeEuler(RotZYX,
degToRad(abcParent[0]),
degToRad(abcParent[1]),
degToRad(abcParent[2]));
// make posChildH global
Vec4f posChildHW = matParent * posChildH;
// homogeneous coordinate -> pos in 3d
Vec3f posChildW(
posChildHW.x / posChildHW.w,
posChildHW.y / posChildHW.w,
posChildHW.z / posChildHW.w);
// print result
std::cout << "posChild in WCS: " << std::fixed << posChildW << '\n';
}
输出:
posChild in WCS: ( 1.000000, 0.000000, -0.000000 )
linmath
可以在github 上找到:linmath.h,github:linmath.cc。
相关部分:
3d 矢量:
template <typename VALUE>
struct Vec3T {
typedef VALUE Value;
Value x, y, z;
Vec3T(Value x, Value y, Value z): x(x), y(y), z(z) { }
};
typedef Vec3T<float> Vec3f;
4d 向量(用于齐次坐标):
template <typename VALUE>
struct Vec4T {
typedef VALUE Value;
Value x, y, z, w;
Vec4T(const Vec3T<Value> &xyz, Value w):
x(xyz.x), y(xyz.y), z(xyz.z), w(w)
{ }
};
typedef Vec4T<float> Vec4f;
4×4矩阵:
enum ArgInitTrans { InitTrans };
enum ArgInitRot { InitRot };
template <typename VALUE>
struct Mat4x4T {
union {
VALUE comp[4 * 4];
struct {
VALUE _00, _01, _02, _03;
VALUE _10, _11, _12, _13;
VALUE _20, _21, _22, _23;
VALUE _30, _31, _32, _33;
};
};
// constructor to build a matrix for translation
Mat4x4T(ArgInitTrans, const Vec3T<VALUE> &t):
_00((VALUE)1), _01((VALUE)0), _02((VALUE)0), _03((VALUE)t.x),
_10((VALUE)0), _11((VALUE)1), _12((VALUE)0), _13((VALUE)t.y),
_20((VALUE)0), _21((VALUE)0), _22((VALUE)1), _23((VALUE)t.z),
_30((VALUE)0), _31((VALUE)0), _32((VALUE)0), _33((VALUE)1)
{ }
// constructor to build a matrix for rotation about axis
Mat4x4T(ArgInitRot, const Vec3T<VALUE> &axis, VALUE angle):
_03((VALUE)0), _13((VALUE)0), _23((VALUE)0),
_30((VALUE)0), _31((VALUE)0), _32((VALUE)0), _33((VALUE)1)
{
//axis.normalize();
const VALUE sinAngle = sin(angle), cosAngle = cos(angle);
const VALUE xx = axis.x * axis.x, xy = axis.x * axis.y;
const VALUE xz = axis.x * axis.z, yy = axis.y * axis.y;
const VALUE yz = axis.y * axis.z, zz = axis.z * axis.z;
_00 = xx + cosAngle * ((VALUE)1 - xx) /* + sinAngle * 0 */;
_01 = xy - cosAngle * xy - sinAngle * axis.z;
_02 = xz - cosAngle * xz + sinAngle * axis.y;
_10 = xy - cosAngle * xy + sinAngle * axis.z;
_11 = yy + cosAngle * ((VALUE)1 - yy) /* + sinAngle * 0 */;
_12 = yz - cosAngle * yz - sinAngle * axis.x;
_20 = xz - cosAngle * xz - sinAngle * axis.y;
_21 = yz - cosAngle * yz + sinAngle * axis.x;
_22 = zz + cosAngle * ((VALUE)1 - zz) /* + sinAngle * 0 */;
}
// multiply matrix with matrix -> matrix
Mat4x4T operator * (const Mat4x4T &mat) const
{
return Mat4x4T(
_00 * mat._00 + _01 * mat._10 + _02 * mat._20 + _03 * mat._30,
_00 * mat._01 + _01 * mat._11 + _02 * mat._21 + _03 * mat._31,
_00 * mat._02 + _01 * mat._12 + _02 * mat._22 + _03 * mat._32,
_00 * mat._03 + _01 * mat._13 + _02 * mat._23 + _03 * mat._33,
_10 * mat._00 + _11 * mat._10 + _12 * mat._20 + _13 * mat._30,
_10 * mat._01 + _11 * mat._11 + _12 * mat._21 + _13 * mat._31,
_10 * mat._02 + _11 * mat._12 + _12 * mat._22 + _13 * mat._32,
_10 * mat._03 + _11 * mat._13 + _12 * mat._23 + _13 * mat._33,
_20 * mat._00 + _21 * mat._10 + _22 * mat._20 + _23 * mat._30,
_20 * mat._01 + _21 * mat._11 + _22 * mat._21 + _23 * mat._31,
_20 * mat._02 + _21 * mat._12 + _22 * mat._22 + _23 * mat._32,
_20 * mat._03 + _21 * mat._13 + _22 * mat._23 + _23 * mat._33,
_30 * mat._00 + _31 * mat._10 + _32 * mat._20 + _33 * mat._30,
_30 * mat._01 + _31 * mat._11 + _32 * mat._21 + _33 * mat._31,
_30 * mat._02 + _31 * mat._12 + _32 * mat._22 + _33 * mat._32,
_30 * mat._03 + _31 * mat._13 + _32 * mat._23 + _33 * mat._33);
}
// constructor to build a matrix for rotation about axis
Mat4x4T(ArgInitRot, const Vec3T<VALUE> &axis, VALUE angle):
_03((VALUE)0), _13((VALUE)0), _23((VALUE)0),
_30((VALUE)0), _31((VALUE)0), _32((VALUE)0), _33((VALUE)1)
{
//axis.normalize();
const VALUE sinAngle = sin(angle), cosAngle = cos(angle);
const VALUE xx = axis.x * axis.x, xy = axis.x * axis.y;
const VALUE xz = axis.x * axis.z, yy = axis.y * axis.y;
const VALUE yz = axis.y * axis.z, zz = axis.z * axis.z;
_00 = xx + cosAngle * ((VALUE)1 - xx) /* + sinAngle * 0 */;
_01 = xy - cosAngle * xy - sinAngle * axis.z;
_02 = xz - cosAngle * xz + sinAngle * axis.y;
_10 = xy - cosAngle * xy + sinAngle * axis.z;
_11 = yy + cosAngle * ((VALUE)1 - yy) /* + sinAngle * 0 */;
_12 = yz - cosAngle * yz - sinAngle * axis.x;
_20 = xz - cosAngle * xz - sinAngle * axis.y;
_21 = yz - cosAngle * yz + sinAngle * axis.x;
_22 = zz + cosAngle * ((VALUE)1 - zz) /* + sinAngle * 0 */;
}
// multiply matrix with vector -> vector
Vec4T<VALUE> operator * (const Vec4T<VALUE> &vec) const
{
return Vec4T<VALUE>(
_00 * vec.x + _01 * vec.y + _02 * vec.z + _03 * vec.w,
_10 * vec.x + _11 * vec.y + _12 * vec.z + _13 * vec.w,
_20 * vec.x + _21 * vec.y + _22 * vec.z + _23 * vec.w,
_30 * vec.x + _31 * vec.y + _32 * vec.z + _33 * vec.w);
}
};
typedef Mat4x4T<float> Mat4x4f;
度数为弧度:
extern const double Pi;
template <typename VALUE>
inline VALUE degToRad(VALUE angle)
{
return (VALUE)Pi * angle / (VALUE)180;
}
Euler(和 Tait-Bryan)角:
// enumeration of rotation axes
enum RotAxis {
RotX, // rotation about x axis
RotY, // rotation about y axis
RotZ // rotation about z axis
};
// enumeration of possible Euler angles
enum EulerAngle {
RotXYX = RotX + 3 * RotY + 9 * RotX, // 0 + 3 + 0 = 3
RotXYZ = RotX + 3 * RotY + 9 * RotZ, // 0 + 3 + 18 = 21
RotXZX = RotX + 3 * RotZ + 9 * RotX, // 0 + 6 + 0 = 6
RotXZY = RotX + 3 * RotZ + 9 * RotY, // 0 + 6 + 9 = 15
RotYXY = RotY + 3 * RotX + 9 * RotY, // 1 + 0 + 9 = 10
RotYXZ = RotY + 3 * RotX + 9 * RotZ, // 1 + 0 + 18 = 19
RotYZX = RotY + 3 * RotZ + 9 * RotX, // 1 + 6 + 0 = 7
RotYZY = RotY + 3 * RotZ + 9 * RotY, // 1 + 6 + 9 = 16
RotZXY = RotZ + 3 * RotX + 9 * RotY, // 2 + 0 + 9 = 11
RotZXZ = RotZ + 3 * RotX + 9 * RotZ, // 2 + 0 + 18 = 20
RotZYX = RotZ + 3 * RotY + 9 * RotX, // 2 + 3 + 0 = 5
RotZYZ = RotZ + 3 * RotY + 9 * RotZ, // 2 + 3 + 18 = 23
RotHPR = RotZXY, // used in OpenGL Performer
RotABC = RotZYX // used in German engineering
};
/* decomposes the combined EULER angle type into the corresponding
* individual EULER angle axis types.
*/
inline void decompose(
EulerAngle type, RotAxis &axis1, RotAxis &axis2, RotAxis &axis3)
{
unsigned type_ = (unsigned)type;
axis1 = (RotAxis)(type_ % 3); type_ /= 3;
axis2 = (RotAxis)(type_ % 3); type_ /= 3;
axis3 = (RotAxis)type_;
}
欧拉角到 4×4 矩阵:
template <typename VALUE>
Mat4x4T<VALUE> makeEuler(
EulerAngle mode, VALUE rot1, VALUE rot2, VALUE rot3)
{
RotAxis axis1, axis2, axis3;
decompose(mode, axis1, axis2, axis3);
const static VALUE axes[3][3] = {
{ (VALUE)1, (VALUE)0, (VALUE)0 },
{ (VALUE)0, (VALUE)1, (VALUE)0 },
{ (VALUE)0, (VALUE)0, (VALUE)1 }
};
return
Mat4x4T<VALUE>(InitRot,
Vec3T<VALUE>(axes[axis1][0], axes[axis1][1], axes[axis1][2]),
rot1)
* Mat4x4T<VALUE>(InitRot,
Vec3T<VALUE>(axes[axis2][0], axes[axis2][1], axes[axis2][2]),
rot2)
* Mat4x4T<VALUE>(InitRot,
Vec3T<VALUE>(axes[axis3][0], axes[axis3][1], axes[axis3][2]),
rot3);
}
glm提供了一个类似但更全面的库。
推荐阅读
- linux - 通过 ssh 连接到外部服务器
- python - 如何在 python 3.7 中安装pyramid-arima
- java - 如何从 Xamarin 打开从 Android Studio 创建的活动?
- python - python将位置从一个def导入另一个def以从天气API打印位置
- node.js - Express:用子资源定义 REST API?
- docker - Docker:如何将带有数据的容器从一台主机传输到另一台主机
- fortran - 在 Fortran90 内在函数上使用常规指令
- node.js - 将请求结果的第一部分通过管道传输到另一台主机
- javascript - Js错误:“未捕获(承诺)SyntaxError:输入意外结束”
- python - 在熊猫中按两列分组