引言
我们的应用程序几乎达到了实际上不需要调用任何类型的原始 OpenGL 来渲染我们的矩形和三角形。今天我们的主题是 Draw Call。
什么是 Draw Call?简单来说,它是在屏幕上实际绘制东西的调用,这是我们告诉显卡的指令:我已经给了你一堆信息,你需要继续运行这种具有几何约束的顶点着色器、像素着色器和片段着色器来计算屏幕上的像素。
有很多不同的方法来实际调用绘图指令,但目前这种情况下我们的 Draw Call 是 OpenGL 的 glDrawElements()
,它仍然内嵌在我们的应用程序代码中没有解耦。
再谈渲染架构与流程
现在我们需要一个渲染器,所以渲染器做些什么呢?来放下我们的三角形,考虑如何正确渲染一个 3D 世界的几何图形。我们可能需要这些组件:
这是我能想到的最低限度的组件。除此之外,我们还需要:
我们需要上面所述的一切。可以看到这些东西开始分为两个不同的方向:与环境有关的东西和与我们正在渲染的实际对象有关的东西。
总的渲染流程将会是:“嘿,渲染器我要从这个场景开始”并提供相机环境;然后渲染所有的网格,每个网格通常会有不同的变换,所以我们会使用一些变换矩阵;最终它会执行 Shader 并结束场景。
它们是一种相同的信号,所有的东西都没有立即渲染,它只是被提交到类似命令队列的渲染中。我们花所有事件提交所有内容(一种渲染类型的命令队列),稍后便可以在单独的渲染管线上执行,这就是多线程游戏引擎实际所做的事情,也是我们将来要做的事情。
渲染API重构
Renderer::BeginScene(); Renderer::Submit(xxx); Renderer::EndScene();
|
渲染器作为高度抽象类不会处理诸如清理屏幕这样的事情,它需要更加关注场景、网格以及之前所说的一切。
Renderer.h
定义虚函数方法:
@@ -1,20 +1,19 @@ #pragma once
-namespace Infinite { +#include "RenderCommand.h"
- enum class RendererAPI - { - None = 0, - OpenGL = 1 - }; +namespace Infinite {
class Renderer { public: - inline static RendererAPI GetAPI() { return s_RendererAPI; } + static void BeginScene(); + static void EndScene(); + + static void Submit(const std::shared_ptr<VertexArray>& vertexArray);
- private: - static RendererAPI s_RendererAPI; + inline static RendererAPI::API GetAPI() { return RendererAPI::GetAPI(); } };
|
Renderer.cpp
实现方法:
@@ -3,5 +3,17 @@
namespace Infinite {
- RendererAPI Renderer::s_RendererAPI = RendererAPI::OpenGL; + void Renderer::BeginScene() + { + } + + void Renderer::EndScene() + { + } + + void Renderer::Submit(const std::shared_ptr<VertexArray>& vertexArray) + { + vertexArray->Bind(); + RenderCommand::DrawIndexed(vertexArray); + } }
|
与此相对的,我们可以使用渲染命令来处理清理屏幕:
RendererCommand::SetColor(); RendererCommand::Clear();
|
在 Infinite/Renderer/
目录下新建 RendererCommand.h
和 RendererCommand.cpp
:
#include "ifnpch.h" #include "RenderCommand.h"
#include "Platform/OpenGL/OpenGLRendererAPI.h"
namespace Infinite {
RendererAPI* RenderCommand::s_RendererAPI = new OpenGLRendererAPI;
}
|
RendererCommand.cpp
实现屏幕清理和绘制调用:
#pragma once
#include "RendererAPI.h"
namespace Infinite {
class RenderCommand { public: inline static void SetClearColor(const glm::vec4& color) { s_RendererAPI->SetClearColor(color); }
inline static void Clear() { s_RendererAPI->Clear(); }
inline static void DrawIndexed(const std::shared_ptr<VertexArray>& vertexArray) { s_RendererAPI->DrawIndexed(vertexArray); } private: static RendererAPI* s_RendererAPI; };
}
|
而 API 的选择则是在 Infinite/Renderer/
目录下新建 RendererAPI.h
和 RendererAPI.cpp
:
@@ -0,0 +1,29 @@ #pragma once
#include <glm/glm.hpp> #include "VertexArray.h"
namespace Infinite {
class RendererAPI { public: enum class API { None = 0, OpenGL = 1 };
public: virtual void SetClearColor(const glm::vec4& color) = 0; virtual void Clear() = 0;
virtual void DrawIndexed(const std::shared_ptr<VertexArray>& vertexArray) = 0;
inline static API GetAPI() { return s_API; } private: static API s_API;
}; }
|
RendererAPI.cpp
选择 OpenGL 作为引擎的图形 API:
#include "ifnpch.h" #include "RendererAPI.h"
namespace Infinite {
RendererAPI::API RendererAPI::s_API = RendererAPI::API::OpenGL;
}
|
调整应用类
我们终于可以解耦所有有关 OpenGL 的代码,替换为封装好的抽象类:
#include "Events/ApplicationEvent.h" #include "Log.h"
-#include <glad/glad.h> +#include "./Renderer/Renderer.h"
#include "Input.h"
@@ -164,16 +164,19 @@ namespace Infinite { { while (m_Running) { - glClearColor(0.1f, 0.1f, 0.1f, 1); - glClear(GL_COLOR_BUFFER_BIT); + RenderCommand::SetClearColor({ 0.1f, 0.1f, 0.1f, 1 }); + RenderCommand::Clear(); + + Renderer::BeginScene();
m_BlueShader->Bind(); - m_SquareVertexArray->Bind(); - glDrawElements(GL_TRIANGLES, m_SquareVertexArray->GetIndexBuffer()->GetCount(), GL_UNSIGNED_INT, nullptr); + Renderer::Submit(m_SquareVertexArray);
m_Shader->Bind(); - m_VertexArray->Bind(); - glDrawElements(GL_TRIANGLES, m_VertexArray->GetIndexBuffer()->GetCount(), GL_UNSIGNED_INT, nullptr); + Renderer::Submit(m_VertexArray); + + Renderer::EndScene();
for (Layer* layer : m_LayerStack) layer->OnUpdate();
|
调试