Cameras and How They Work

引言

上次我们构建了渲染命令并重构了渲染 API 架构,今天我们来看看相机。

相机设计

为了在引擎中构建合适的相机系统,我们需要考虑相机的架构以及它们如何正常工作,需要真正了解渲染的工作原理(不仅包括渲染方面,但包括 GamePlay 方面)。相机真的很有趣,它们对于渲染至关重要。

相机被定义为如何看待一个场景,它有些像真实世界中的相机,为我们提供了一种正确查看世界的方法。所以我们在 3D 场景中有一堆对象,我们想做的是从一个特定的角度来看待它们,像现实世界一样可以拿着相机。

proj x view(TRS) x model x vertexPos

Infinite/Renderer/ 新建 OrthographicCamera.hOrthographicCame.cpp

头文件 OrthographicCamera.h 包含相机位置、角度和所需的投影矩阵及其访问器:

#pragma once

#include <glm/glm.hpp>

namespace Infinite {

class OrthographicCamera
{
public:
OrthographicCamera(float left, float right, float bottom, float top);

const glm::vec3& GetPosition() const { return m_Position; }
void SetPosition(const glm::vec3& position) { m_Position = position; RecalculateViewMatrix(); }

float GetRotation() const { return m_Rotation; }
void SetRotation(float rotation) { m_Rotation = rotation; RecalculateViewMatrix(); }

const glm::mat4& GetProjectionMatrix() const { return m_ProjectionMatrix; }
const glm::mat4& GetViewMatrix() const { return m_ViewMatrix; }
const glm::mat4& GetViewProjectionMatrix() const { return m_ViewProjectionMatrix; }
private:
void RecalculateViewMatrix();

private:
glm::mat4 m_ProjectionMatrix;
glm::mat4 m_ViewMatrix;
glm::mat4 m_ViewProjectionMatrix;

glm::vec3 m_Position = {0.0f, 0.0f, 0.0f};
float m_Rotation = 0.0f;
};

}

OrthographicCamera.cpp 实现 RecalculateViewMatrix() 方法,实时更新我们的 VP 矩阵:

#include "ifnpch.h"
#include "OrthographicCamera.h"

#include <glm/gtc/matrix_transform.hpp>

namespace Infinite {

OrthographicCamera::OrthographicCamera(float left, float right, float bottom, float top)
:m_ProjectionMatrix(glm::ortho(left, right, bottom, top, -1.0f, 1.0f)), m_ViewMatrix(1.0f)
{
m_ViewProjectionMatrix = m_ProjectionMatrix * m_ProjectionMatrix;
}

void OrthographicCamera::RecalculateViewMatrix()
{
glm::mat4 transform = glm::translate(glm::mat4(1.0f), m_Position) *
glm::rotate(glm::mat4(1.0f), m_Rotation, glm::vec3(0, 0, 1));

m_ViewMatrix = glm::inverse(transform);
m_ViewProjectionMatrix = m_ProjectionMatrix * m_ViewMatrix;
}

}

应用类调整

Application.h 加入相机:

#include "./Renderer/Buffer.h"
#include "./Renderer/VertexArray.h"

+#include "./Renderer/OrthographicCamera.h"

namespace Infinite {
class INFINITE_API Application
{
@@ -41,6 +43,8 @@ namespace Infinite {

std::shared_ptr<Shader> m_BlueShader;
std::shared_ptr<VertexArray> m_SquareVertexArray;

+ OrthographicCamera m_Camera;
private:
static Application* s_Instance;
};

Application.cpp 首先先调整 Shader:

	Application* Application::s_Instance = nullptr;

- Application::Application()
+ Application::Application()
+ : m_Camera(-1.6f, 1.6f, -0.9f, 0.9f)
{
IFN_CORE_ASSERT(!s_Instance, "Application already exists!");
s_Instance = this;
@@ -78,14 +79,16 @@ namespace Infinite {
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec4 a_Color;
+ uniform mat4 u_ViewProjection;
out vec3 v_Position;
out vec4 v_Color;
void main()
{
v_Position = a_Position;
v_Color = a_Color;
- gl_Position = vec4(a_Position, 1.0);
+ gl_Position = u_ViewProjection * vec4(a_Position, 1.0);
}
)";

@@ -112,11 +115,14 @@ namespace Infinite {
#version 330 core

layout(location = 0) in vec3 a_Position;
+ uniform mat4 u_ViewProjection;
out vec3 v_Position;
void main()
{
v_Position = a_Position;
- gl_Position = vec4(a_Position, 1.0);
+ gl_Position = u_ViewProjection * vec4(a_Position, 1.0);
}
)";

其次是调整 Submit() 方法:

@@ -167,14 +173,13 @@ namespace Infinite {
RenderCommand::SetClearColor({ 0.1f, 0.1f, 0.1f, 1 });
RenderCommand::Clear();

- Renderer::BeginScene();
-
- m_BlueShader->Bind();
- Renderer::Submit(m_SquareVertexArray);
+ m_Camera.SetPosition({ 0.5f, 0.5f, 0.0f });
+ m_Camera.SetRotation(45.0f);

+ Renderer::BeginScene(m_Camera);

- m_Shader->Bind();
- Renderer::Submit(m_VertexArray);
+ Renderer::Submit(m_BlueShader, m_SquareVertexArray);
+ Renderer::Submit(m_Shader, m_VertexArray);

Renderer::EndScene();

Submit() 方法涉及到 Renderer.hRenderer.cpp

#include "RenderCommand.h"

+#include "OrthographicCamera.h"
+#include "Shader.h"

namespace Infinite {

class Renderer
{
public:
- static void BeginScene();
+ static void BeginScene(OrthographicCamera& camera);
static void EndScene();

- static void Submit(const std::shared_ptr<VertexArray>& vertexArray);
+ static void Submit(const std::shared_ptr<Shader>& shader, const std::shared_ptr<VertexArray>& vertexArray);


inline static RendererAPI::API GetAPI() { return RendererAPI::GetAPI(); }

+ private:
+ struct SceneData
+ {
+ glm::mat4 ViewProjectionMatrix;
+ };
+
+ static SceneData* m_SceneData;
};

头文件定义场景数据集,目前暂时包含 VP 矩阵。

#include <glad/glad.h>

+#include <glm/gtc/type_ptr.hpp>

namespace Infinite {

Shader::Shader(const std::string& vertexSrc, const std::string& fragmentSrc)
@@ -137,4 +139,10 @@ namespace Infinite {
glUseProgram(0);
}

+ void Shader::UploadUniformMat4(const std::string& name, const glm::mat4& matrix)
+ {
+ GLint location = glGetUniformLocation(m_RendererID, name.c_str());
+ glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(matrix));
+ }

}

调试

Application::Application()
: m_Camera(1.0f, 1.0f, -1.0f, 1.0f)

Application::Application()
: m_Camera(-1.6f, 1.6f, -0.9f, 0.9f)