IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> 【Overload游戏引擎】源码分析之一:OvMaths函数库(上) -> 正文阅读

[游戏开发]【Overload游戏引擎】源码分析之一:OvMaths函数库(上)

2021SC@SDUSC?

OvMaths函数库主要包含了关于矩阵、向量等操作的函数,同时作为游戏引擎的数学库,OvMaths中还包含了计算机图形渲染所需的转化矩阵等的计算方法。以下是正文部分。

1.向量操作函数

该函数库中的向量操作函数包括2维、3维、4维这三种,以下仅对3维向量进行分析,4维向量由于其在计算机图形学中的特殊性,将会单独分析。

1.1 3维向量

namespace OvMaths
{
	/**
	* Mathematic representation of a 3D vector of floats
	*/
	struct FVector3
	{
		static const FVector3 One;
		static const FVector3 Zero;
		static const FVector3 Forward;
		static const FVector3 Right;
		static const FVector3 Up;

		float x;
		float y;
		float z;

首先简单看看,3维向量定义了一个全1向量与一个全0向量,方便在下文的函数中直接调用。

然后是3个比较重要的向量:forward,up,right。下文简单介绍一下相关的图形学知识。

#include <utility>
#include <stdexcept>
#include <cmath>

#include "OvMaths/FVector3.h"

const OvMaths::FVector3 OvMaths::FVector3::One(1.0f, 1.0f, 1.0f);
const OvMaths::FVector3 OvMaths::FVector3::Zero(0.0f, 0.0f, 0.0f);
const OvMaths::FVector3 OvMaths::FVector3::Forward(0.0f, 0.0f, 1.0f);
const OvMaths::FVector3 OvMaths::FVector3::Right(1.0f, 0.0f, 0.0f);
const OvMaths::FVector3 OvMaths::FVector3::Up(0.0f, 1.0f, 0.0f);

在计算机图形学中,LookAt矩阵被用于定义摄像机的视角坐标系,而LookAt矩阵就是由forward,up,right这三个向量构成的。

其中forward向量为摄像机当前朝向的反方向,在初始世界坐标系中指向z轴的正半轴,因为摄像机会朝向z轴负半轴。

而right向量一般会由y轴叉乘forward向量获得,指明摄像机右侧方向。

最后将right向量与forward叉乘得到up向量。

(上图来自于LearnOpenGL官网)?

往后是一系列的运算符重载函数与3维向量的基本运算法则,涉及基础的线性代数知识,这里不作详细讲解。

顺带一提,这部分计算的函数先定义了基础操作函数,例如Add,之后在运算符重载中直接复用,极大精简了代码。


OvMaths::FVector3 OvMaths::FVector3::operator+(const FVector3& p_other) const
{
	return Add(*this, p_other);
}

OvMaths::FVector3 OvMaths::FVector3::Add(const FVector3& p_left, const FVector3& p_right)
{
	return FVector3
	(
		p_left.x + p_right.x,
		p_left.y + p_right.y,
		p_left.z + p_right.z
	);
}

1.2 4维向量

值得一提的是,在2维与3维向量中都存在以下这个夹角求值函数,但在4维向量中就消失了。这涉及到图形学中的一个概念——齐次坐标系。

float OvMaths::FVector3::AngleBetween(const FVector3& p_from, const FVector3& p_to)

关于齐次坐标系的详细内容将会留到4维矩阵处一同作答,但是在此有一处要点先提前指出:

在OvMaths中,4维向量在进行算术操作后并未除以w值,这点需要注意。

2.矩阵操作函数

2.1 3维矩阵

矩阵这部分的内容与向量类似,只介绍3维与4维齐次矩阵。

在3维矩阵中,采用了一个一维数组进行存储,这样方便了矩阵元素的顺序查找。

同时,结构体中定义了单位矩阵identity,将会在下文的矩阵计算中发挥重要作用。

#include <stdint.h>

#include "OvMaths/FVector3.h"
#include "OvMAths/FVector2.h"

namespace OvMaths
{
	/**
	* Mathematic representation of a 3x3 Matrix of floats
	*/
	struct FMatrix3
	{
		float data[9];
		static const FMatrix3 Identity;

矩阵的操作函数与向量的架构类似,都是先定义独立的算术操作,之后在运算符重载中复用。

但是在这类函数中,出现了这样一种特殊的函数(如下图)。

OvMaths::FMatrix3 OvMaths ::FMatrix3::Add(const OvMaths::FMatrix3 & p_left, float p_scalar)

众所周知,矩阵是不能与单独的数字进行加减法操作的,但是在函数库中出现矩阵与一个浮点数的算术操作。具体操作内容如下:

{
	FMatrix3 result(p_left);
	for (uint8_t i = 0; i < 9; ++i)
		result.data[i] += p_scalar;
	return result;
}

可知该操作为矩阵的每个元素加或减去一个相同的浮点数,具体实用留待日后观察。

同时,与向量不同的是,矩阵中增加了图形坐标的转化操作(如下)

	static FMatrix3 Translation(const FVector2& p_translation);

	static FMatrix3 Translate(const FMatrix3& p_matrix, const FVector2& p_translation);

	static FMatrix3 Rotation(float p_rotation);

	static FMatrix3 Rotate(const FMatrix3& p_matrix, float p_rotation);

	static FMatrix3 Scaling(const FVector2& p_scale);

	static FMatrix3 Scale(const FMatrix3& p_matrix, const FVector2& p_scale);

平移(translate)、旋转(rotate)、缩放(scale)是平面与空间坐标系中最常见的变化操作,该部分的函数分为两类:前半部分是求变换矩阵,后半部分进行变换操作。

对于3维矩阵,只能进行2维平面的图形变换,对于三维空间的图形变换,以及相关的函数将在4维矩阵中详细讲解。

2.2 4维矩阵

4维矩阵是图形学中用于实现图形坐标变换的重要途径,以下将结合代码详细讲解。

2.2.1纺射变换矩阵

平移函数(translate)

OvMaths::FMatrix4 OvMaths::FMatrix4::Translation(const FVector3& p_translation)
{
	return FMatrix4(1, 0, 0, p_translation.x,
					0, 1, 0, p_translation.y,
					0, 0, 1, p_translation.z,
					0, 0, 0, 1);
}

在4×4矩阵上有几个特别的位置用来执行特定的操作,对于位移来说它们是第四列最上面的3个值。如果我们把位移向量表示为(Tx,Ty,Tz),我们就能把位移矩阵定义为:

齐次坐标(Homogeneous Coordinates)

向量的w分量也叫齐次坐标。想要从齐次向量得到3D向量,我们可以把x、y和z坐标分别除以w坐标。我们通常不会注意这个问题,因为w分量通常是1.0。

使用齐次坐标有几点好处:它允许我们在3维向量上进行位移。如果没有w分量我们是不能位移向量的,因为平移操作是非线性的,3维空间无法通过一个3维矩阵实现平移。

如果一个向量的齐次坐标是0,这个坐标就是方向向量(Direction Vector),因为w坐标是0,这个向量就不能位移。

缩放函数(scale)

OvMaths::FMatrix4 OvMaths::FMatrix4::Scaling(const FVector3& p_scale)
{
	return FMatrix4(p_scale.x, 0, 0, 0,
					0, p_scale.y, 0, 0,
					0, 0, p_scale.z, 0,
					0, 0, 0, 1);
}

?我们只需要在矩阵对角线上前3个位置填上缩放的倍数,经过如下变换之后就能得到缩放后的坐标。由于缩放操作是线性的,所以齐次坐标对其没有影响。

旋转函数(rotate)

OvMaths::FMatrix4 OvMaths::FMatrix4::RotationOnAxisX(float p_rotation)
{
	return FMatrix4(1, 0, 0, 0,
		0, std::cos(p_rotation), -std::sin(p_rotation), 0,
		0, std::sin(p_rotation), std::cos(p_rotation), 0,
		0, 0, 0, 1);
}


OvMaths::FMatrix4 OvMaths::FMatrix4::RotationOnAxisY(float p_rotation)
{
	return FMatrix4(std::cos(p_rotation), 0, -std::sin(p_rotation), 0,
					0, 1, 0, 0,
					std::sin(p_rotation), 0, std::cos(p_rotation), 0,
					0, 0, 0, 1);
}


OvMaths::FMatrix4 OvMaths::FMatrix4::RotationOnAxisZ(float p_rotation)
{
	return FMatrix4(std::cos(p_rotation), -std::sin(p_rotation), 0, 0,
					  std::sin(p_rotation), std::cos(p_rotation), 0, 0,
					  0, 0, 1, 0,
					  0, 0, 0, 1);
}

根据代码我们可以看出,图形旋转的3种主要形式分别为沿x、y、z轴旋转,根据旋转轴的不同,旋转矩阵的形式也不同,下图列出了相关的旋转矩阵。

利用旋转矩阵我们可以把任意位置向量沿一个单位旋转轴进行旋转。也可以将多个矩阵复合,比如先沿着x轴旋转再沿着y轴旋转再沿着z轴旋转。

OvMaths::FMatrix4 OvMaths::FMatrix4::RotationYXZ(float p_rotation)
{
	FMatrix4 Xrot = RotationOnAxisX(p_rotation);
	FMatrix4 Yrot = RotationOnAxisY(p_rotation);
	FMatrix4 Zrot = RotationOnAxisZ(p_rotation);

	return Yrot * Xrot * Zrot;
}

但是这会很快导致一个问题——万向节死锁(Gimbal Lock)。在这里我们不讨论它的细节,但是对于3D空间中的旋转,一个更好的模型是沿着任意的一个轴,比如单位向量(0.662, 0.2, 0.7222)旋转,而不是对一系列旋转矩阵进行复合。这样的一个(超级麻烦的)矩阵是存在的,见下面这个公式,其中(Rx,Ry,Rz)(Rx,Ry,Rz)代表任意旋转轴:

但是即使这样一个矩阵也不能完全解决万向节死锁问题(尽管会极大地避免)。避免万向节死锁的真正解决方案是使用四元数(Quaternion),它不仅更安全,而且计算会更有效率。有关四元数的知识在这里就不多加介绍。

下一节,我将介绍OvMaths库中最重要的内容——投影变换。

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-10-07 14:07:54  更:2021-10-07 14:07:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/28 2:37:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码