DX11(七):Transform类

06/10/2020


通常来说,大多是引擎都保存物体三个基本信息,缩放,旋转,和位置。下面使用DirectX提供的数学库作为数据类型书写的Transform类, 下面是虚幻引擎4的Transform类的形式

UE4 Transform类

转换类/变形类

主要研究世界矩阵与视图矩阵的关系
三个基础属性:

  • 缩放系数
  • 旋转欧拉角
  • 位置
DirectX::XMFLOAT3 m_Scale = { 1.0f, 1.0f, 1.0f };				// 缩放
DirectX::XMFLOAT3 m_Rotation = {};								// 旋转欧拉角(弧度制)
DirectX::XMFLOAT3 m_Position = {};								// 位置

物体坐标系

在物体坐标系中,一个物体有缩放,位置和朝向三个基本属性,分别对应

  • 物体缩放系数
  • 物体的在世界坐标的位置
  • 物体基于Roll-Pitch-Yaw欧拉角(Z-X-Y)的旋转弧度制
    • Roll-Pitch-Yaw 先旋转Z轴,再旋转X轴,最后旋转Y轴
    • 其他表示法

在物体坐标系中,物体的方位或者朝向很重要,它可以用矩阵,欧拉角和四元数表示

矩阵表示

矩阵如何表示朝向,DirectX是使用的行主序矩阵,R1表示 x \vec x ,R2表示 y \vec y ,R3表示 z \vec z ,它们相互垂直
[ x 1 x 2 x 3 < y 1 y 2 y 3 < z 1 z 2 z 3 < ] \left [ \begin{matrix} x_{1} & x_{2} & x_{3} <--- 右方向\\ y_{1} & y_{2} & y_{3} <---上方向\\ z_{1} & z_{2} & z_{3} <---前方向 \end{matrix} \right ]
所以说在物体坐标系里面,旋转矩阵的三个分量可以表示当前x,y,z轴的情况

平移 — 位移或者加速度

相对于世界坐标系中的位置属性

右方向可以改变左右平移

XMFLOAT3 Transform::GetRightAxis() const //物体当前的x轴情况
{
	XMMATRIX R = XMMatrixRotationRollPitchYawFromVector(XMLoadFloat3(&m_Rotation));
	XMFLOAT3 right;
	XMStoreFloat3(&right, R.r[0]);
	return right;
}

上方向可以改变起飞或者降落

前方向可以改变前进和后退

平移函数(Translate)

  • 物体沿着某个方向移动,并且移动多少距离
  • 基本用来求路程和速度
  • XMVectorMultplyAdd(V1,V2,V3); newPosition = V1 * V2 + V3; 各个分量上先乘法再加法,即y = kx + b;

条件

  • 方向代表向量,比如两点位置相减就是一个向量的方向
  • 单位向量乘一个标量,表示向量的长度等于标量值
  • 最后加上之前位置的值
void Transform::Translate(const XMFLOAT3& direction, float magnitude)
{
	XMVECTOR directionVec = XMVector3Normalize(XMLoadFloat3(&direction));
	XMVECTOR newPosition = XMVectorMultiplyAdd(XMVectorReplicate(magnitude), directionVec, XMLoadFloat3(&m_Position));
	XMStoreFloat3(&m_Position, newPosition);
}

直行

直行表示不受到y轴或者上方向影响,**即在同一个平面上做平移 **

void FirstPersonCamera::Walk(float d)
{
	XMVECTOR rightVec = XMLoadFloat3(&m_Transform.GetRightAxis()); // 右轴
	XMVECTOR frontVec = XMVector3Normalize(XMVector3Cross(rightVec, g_XMIdentityR1)); //右轴叉乘世界坐标的上轴,等于直行方向,即向量的y分量等于0 ----(x,0.0f,z)
	XMFLOAT3 front;
	XMStoreFloat3(&front, frontVec);
	m_Transform.Translate(front, d);
}

前进

前进表示前方向移动,如果朝向朝上,物体可以飞起来,朝下可以钻地

void FirstPersonCamera::MoveForward(float d)
{
	m_Transform.Translate(m_Transform.GetForwardAxis(), d);
}

旋转 – 与角位移或者角速度

欧拉角旋转

首先最简单的就是基于旋转欧拉角的旋转了,只需要更新欧拉角即可:


void Transform::Rotate(const XMFLOAT3& eulerAnglesInRadian)
{
	XMVECTOR newRotationVec = XMVectorAdd(XMLoadFloat3(&m_Rotation), XMLoadFloat3(&eulerAnglesInRadian));
	XMStoreFloat3(&m_Rotation, newRotationVec);
}

绕轴旋转,使用原点

绕轴旋转,先根据当前欧拉角得到旋转矩阵,然后更新,最后还原欧拉角

void Transform::RotateAxis(const XMFLOAT3& axis, float radian)
{
	XMVECTOR rotationVec = XMLoadFloat3(&m_Rotation);
	XMMATRIX R = XMMatrixRotationRollPitchYawFromVector(rotationVec) * 
		XMMatrixRotationAxis(XMLoadFloat3(&axis), XMConvertToRadians(radian));
	XMFLOAT4X4 rotMatrix;
	XMStoreFloat4x4(&rotMatrix, R);
	m_Rotation = GetEulerAnglesFromRotationMatrix(rotMatrix);
}

以其他点作为原点,绕轴旋转

基于某一点为旋转中心进行绕轴旋转的实现过程稍微有点复杂。首先根据已有变换算出旋转矩阵*平移矩阵,然后将旋转中心平移到原点(这两步平移可以合并),再进行旋转,最后再平移回旋转中心:

void Transform::RotateAround(const XMFLOAT3& point, const XMFLOAT3& axis, float radian)
{
	XMVECTOR rotationVec = XMLoadFloat3(&m_Rotation);
	XMVECTOR positionVec = XMLoadFloat3(&m_Position);
	XMVECTOR centerVec = XMLoadFloat3(&point);

	// 以point作为原点进行旋转
	XMMATRIX RT = XMMatrixRotationRollPitchYawFromVector(rotationVec) * XMMatrixTranslationFromVector(positionVec - centerVec);
	RT *= XMMatrixRotationAxis(XMLoadFloat3(&axis), XMConvertToRadians(radian));
	RT *= XMMatrixTranslationFromVector(centerVec);
	XMFLOAT4X4 rotMatrix;
	XMStoreFloat4x4(&rotMatrix, RT);
}

构造变换矩阵

世界矩阵

  • 世界矩阵便于最后绘图
  • 逆世界矩阵便于求出视图矩阵,或者便于旋转(getWorldToLocalMatrixXM)
// World = SRT
XMMATRIX Transform::GetLocalToWorldMatrixXM() const
{
	XMVECTOR scaleVec = XMLoadFloat3(&m_Scale);
	XMVECTOR rotationVec = XMLoadFloat3(&m_Rotation);
	XMVECTOR positionVec = XMLoadFloat3(&m_Position);
	XMMATRIX World = XMMatrixScalingFromVector(scaleVec) * 
						XMMatrixRotationRollPitchYawFromVector(rotationVec) * XMMatrixTranslationFromVector(positionVec);
	return World;
}


世界矩阵的逆矩阵

XMMATRIX Transform::GetWorldToLocalMatrixXM() const
{
	XMMATRIX InvWorld = XMMatrixInverse(nullptr,GetLocalToWorldMatrixXM());
	return InvWorld;
}

视图矩阵转欧拉角表示

通常视图矩阵由摄像机位置和朝向决定,当我们确定了物体位置,或者摄像机看的方向,就可以获得视图矩阵,但是通常我们最好以Transform形式保存,所以最后还需要从视图矩阵提取出欧拉角和位置属性

观察某一点 (第三人称摄像头)

如何从视图矩阵转换为欧拉角,并存储起来

void Transform::LookAt(const XMFLOAT3& target, const XMFLOAT3& up)
{
	XMMATRIX View = XMMatrixLookAtLH(XMLoadFloat3(&m_Position), XMLoadFloat3(&target), XMLoadFloat3(&up));
	XMMATRIX InvView = XMMatrixInverse(nullptr, View);
	XMFLOAT4X4 rotMatrix;
	XMStoreFloat4x4(&rotMatrix, InvView);
	m_Rotation = GetEulerAnglesFromRotationMatrix(rotMatrix);
}

观察某一个方向 (第一人称)

void Transform::LookTo(const XMFLOAT3& direction, const XMFLOAT3& up)
{
	XMMATRIX View = XMMatrixLookToLH(XMLoadFloat3(&m_Position), XMLoadFloat3(&direction), XMLoadFloat3(&up));
	XMMATRIX InvView = XMMatrixInverse(nullptr, View);
	XMFLOAT4X4 rotMatrix;
	XMStoreFloat4x4(&rotMatrix, InvView);
	m_Rotation = GetEulerAnglesFromRotationMatrix(rotMatrix);
}

最后以欧拉角形式保存

X_Jun的博客园

猜你喜欢

转载自blog.csdn.net/weixin_44200074/article/details/106628172
今日推荐