Math

引言

上一节我们为引擎提供了自己的键鼠映射,今天我们来聊聊之后的渲染部分的数学基础。

渲染是非常依赖数学的,所以如果想在屏幕上画些什么我们必须拥有一个数学库。

老实说现在有许多优秀的第三方数学库,在我的专业工作中也常常用到。从技术上讲,自己编写的数学库维持最低限度的启动和运行实际上并不难,你可能会编写基础的类如向量、矩阵等等;显然它也需要一些实用方法如基本的运算。

自己做起来很容易,但是如果您想编写一个好的数学库是非常困难的。因为为了使运算快速、为了以一种高效的方式编写它,我们需要非常特殊的编程技巧或者底层功底(涉及到寄存器和汇编代码)。

GLM 库

遗憾的是对于这个系列而言我们谈论的大多是 C++,如何建立一个高效数学库并不在我们的考量范围内。因此我们会使用称为 OpenGL Mathematics(简称 glm)的支持库:

它是一个用于图形的 C++ 数学库,虽然名字带着 OpenGL,但请注意它与 OpenGL 无关,只是语法近似于 OpenGL 的数学库。

现在让我们引入该库:

1
2
cd Infinite
git submodule add https://github.com/g-truc/glm Infinite/vendor/glm

预生成文件

在项目 Infinite 的预生成文件 premake5.lua 中加入 glm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
workspace "Infinite"
architecture "x64"
startproject "Sandbox"

configurations
{
"Debug",
"Release",
"Dist"
}

outputdir = "%{cfg.buildcfg}-%{cfg.system}-%{cfg.architecture}"

-- Include directories relative to root folder (solution directory)
IncludeDir = {}
IncludeDir["GLFW"] = "Infinite/vendor/GLFW/include"
IncludeDir["GLAD"] = "Infinite/vendor/GLAD/include"
IncludeDir["ImGui"] = "Infinite/vendor/imgui/include"
+IncludeDir["glm"] = "Infinite/vendor/glm"

include "Infinite/vendor/GLFW"
include "Infinite/vendor/GLAD"
include "Infinite/vendor/imgui"

project "Infinite"
location "Infinite"
kind "SharedLib"
language "C++"
staticruntime "off"

targetdir ("bin/" .. outputdir .. "/%{prj.name}")
objdir ("bin-int/" .. outputdir .. "/%{prj.name}")

pchheader "ifnpch.h"
pchsource "Infinite/src/ifnpch.cpp"

files
{
"%{prj.name}/src/**.h",
"%{prj.name}/src/**.cpp",
+ "%{prj.name}/vendor/glm/glm/**.hpp",
+ "%{prj.name}/vendor/glm/glm/**.inl",
}

includedirs
{
"%{prj.name}/src",
"%{prj.name}/vendor/spdlog/include",
"%{IncludeDir.GLFW}",
"%{IncludeDir.GLAD}",
"%{IncludeDir.ImGui}",
+ "%{IncludeDir.glm}"
}

links
{
"GLFW",
"GLAD",
"ImGui",
"opengl32.lib"
}

filter "system:windows"
cppdialect "C++17"
systemversion "latest"

defines
{
"IFN_PLATFORM_WINDOWS",
"IFN_BUILD_DLL",
"GLFW_INCLUDE_NONE"
}

postbuildcommands
{
("{COPY} %{cfg.buildtarget.relpath} \"../bin/" .. outputdir .. "/Sandbox/\"")
}

filter "configurations:Debug"
defines "IFN_DEBUG"
runtime "Debug"
symbols "On"

filter "configurations:Release"
defines "IFN_RELEASE"
runtime "Release"
optimize "On"

filter "configurations:Dist"
defines "IFN_DIST"
runtime "Release"
optimize "On"



project "Sandbox"
location "Sandbox"
kind "ConsoleApp"
staticruntime "off"

language "C++"

targetdir ("bin/" .. outputdir .. "/%{prj.name}")
objdir ("bin-int/" .. outputdir .. "/%{prj.name}")

files
{
"%{prj.name}/src/**.h",
"%{prj.name}/src/**.cpp"
}

includedirs
{
"Infinite/vendor/spdlog/include",
"Infinite/src/Infinite",
"Infinite/src/Events",
"Infinite/src/**.h",
"Infinite/src/**.cpp",
+ "%{IncludeDir.glm}"
}

links
{
"Infinite"
}

filter "system:windows"
cppdialect "C++17"
systemversion "latest"

defines
{
"IFN_PLATFORM_WINDOWS"
}

filter "configurations:Debug"
defines "IFN_DEBUG"
runtime "Debug"
symbols "On"

filter "configurations:Release"
defines "IFN_RELEASE"
runtime "Release"
optimize "On"

filter "configurations:Dist"
defines "IFN_DIST"
runtime "Release"
optimize "On"

重新生成项目后打开 VisualStudio,在 vendor 下可以看到 glm

所有的 .hpp.inl 文件都显示可用:

调试

我们在 SandboxApp.cpp 中放置一段 glm 示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <glm/vec3.hpp> // glm::vec3
#include <glm/vec4.hpp> // glm::vec4
#include <glm/mat4x4.hpp> // glm::mat4
#include <glm/gtc/matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale, glm::perspective
glm::mat4 camera(float Translate, glm::vec2 const & Rotate)
{
glm::mat4 Projection = glm::perspective(glm::radians(45.0f), 4.0f / 3.0f, 0.1f, 100.f);
glm::mat4 View = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -Translate));
View = glm::rotate(View, Rotate.y, glm::vec3(-1.0f, 0.0f, 0.0f));
View = glm::rotate(View, Rotate.x, glm::vec3(0.0f, 1.0f, 0.0f));
glm::mat4 Model = glm::scale(glm::mat4(1.0f), glm::vec3(0.5f));
return Projection * View * Model;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <Infinite.h>

+#include <glm/vec3.hpp> // glm::vec3
+#include <glm/vec4.hpp> // glm::vec4
+#include <glm/mat4x4.hpp> // glm::mat4
+#include <glm/gtc/matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale, glm::perspective
+glm::mat4 camera(float Translate, glm::vec2 const & Rotate)
+{
+ glm::mat4 Projection = glm::perspective(glm::radians(45.0f), 4.0f / 3.0f, 0.1f, 100.f);
+ glm::mat4 View = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -Translate));
+ View = glm::rotate(View, Rotate.y, glm::vec3(-1.0f, 0.0f, 0.0f));
+ View = glm::rotate(View, Rotate.x, glm::vec3(0.0f, 1.0f, 0.0f));
+ glm::mat4 Model = glm::scale(glm::mat4(1.0f), glm::vec3(0.5f));
+ return Projection * View * Model;
+}


class ExampleLayer : public Infinite::Layer
{
public:
ExampleLayer()
: Layer("Example")
{
+ auto cam = camera(5.0f, { 0.5f, 0.5f });
}

void OnUpdate() override
{
// IFN_INFO("ExampleLayer::Update");

if (Infinite::Input::IsKeyPressed(IFN_KEY_TAB))
IFN_INFO("Tab key is pressed (poll)!");
}

void OnEvent(Infinite::Event& event) override
{
if (event.GetEventType() == Infinite::EventType::KeyPressed)
{
Infinite::KeyPressedEvent& e = (Infinite::KeyPressedEvent&)event;
if (Infinite::Input::IsKeyPressed(IFN_KEY_TAB))
IFN_INFO("Tab key is pressed (event)!");

IFN_TRACE("{0}", (char)e.GetKeyCode());
}
}

};

class Sandbox : public Infinite::Application {
public:
Sandbox()
{
PushLayer(new ExampleLayer());
PushOverlay(new Infinite::ImGuiLayer());
}
~Sandbox() {}
};

Infinite::Application* Infinite::CreateApplication() {
return new Sandbox();
}

加入示例代码后如成功启动则代表引入成功,测试完毕删除示例代码。