Math

引言

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

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

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

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

GLM 库

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

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

现在让我们引入该库:

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

预生成文件

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

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 示例代码:

#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;
}

#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();
}

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