Our First Triangle

引言

上一节我们为不同的图形 API 创建了 Context 中间层。今天我们将绘制引擎第一个三角形。

更改背景颜色

之前我们的背景一直是粉色:

让我们做一些改变!打开 Application.cpp,更改背景颜色参数为浅黑色:

绘制三角形

绘制三角形一般要包含这四件东西:

  • Vertex Array
  • Vertex Buffer
  • Index Buffer
  • Shader

其实着色器并不是必须的,GPU 驱动程序通常会提供一种默认的着色器(例如我使用的 NVIDIA GPU 提供这项功能)。首先我们在 Application.h 先声明顶点缓冲区、索引缓冲区和顶点数组:

#pragma once

#include "Core.h"
#include "../Window.h"
#include "LayerStack.h"
#include "Events/Event.h"
#include "Events/ApplicationEvent.h"
#include "./ImGui/ImGuiLayer.h"


namespace Infinite {
class INFINITE_API Application
{
public:
Application();
virtual ~Application();

void Run();
void OnEvent(Event& e);

void PushLayer(Layer* layer);
void PushOverlay(Layer* layer);

inline Window& GetWindow() { return *m_Window; }

inline static Application& Get() { return *s_Instance; }
private:
bool OnWindowClose(WindowCloseEvent& e);

std::unique_ptr<Window> m_Window;
ImGuiLayer* m_ImGuiLayer;
bool m_Running = true;
LayerStack m_LayerStack;

+ unsigned int m_VertexArray, m_VertexBuffer, m_IndexBuffer;
private:
static Application* s_Instance;
};

//To be define in CLIENT
Application* CreateApplication();
}

在创建窗口之后我们在 Application.cpp 中调用内置函数绑定顶点数组和顶点缓冲区:

glGenVertexArrays(1, &m_VertexArray);
glBindVertexArray(m_VertexArray);

glGenBuffers(1, &m_VertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_VertexBuffer);

接着我们创建一个三维顶点数组:

float vertices[3 * 3] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};

如果我们没有任何类型的投影矩阵,OpenGL 默认的裁剪空间边界为 [-1, 1]。现在我们需要将这些数据上传到 GPU:

glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr);

然后我们绑定索引缓冲区:

glGenBuffers(1, &m_IndexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IndexBuffer);

ok,现在要做的事每一帧绑定顶点数组:

unsigned int indices[3] = { 0, 1, 2 };
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

完整源码如下:

#include "ifnpch.h"
#include "Application.h"

#include "Events/ApplicationEvent.h"
#include "Log.h"

#include <glad/glad.h>

#include "Input.h"

namespace Infinite {

#define BIND_EVENT_FN(x) std::bind(&Application::x, this, std::placeholders::_1)

Application* Application::s_Instance = nullptr;

Application::Application()
{
IFN_CORE_ASSERT(!s_Instance, "Application already exists!");
s_Instance = this;

m_Window = std::unique_ptr<Window>(Window::Create());
m_Window->SetEventCallback(BIND_EVENT_FN(OnEvent));

m_ImGuiLayer = new ImGuiLayer();
PushOverlay(m_ImGuiLayer);

// Vertex Array
+ glGenVertexArrays(1, &m_VertexArray);
+ glBindVertexArray(m_VertexArray);

// Vertex Buffer
+ glGenBuffers(1, &m_VertexBuffer);
+ glBindBuffer(GL_ARRAY_BUFFER, m_VertexBuffer);


+ float vertices[3 * 3] = {
+ -0.5f, -0.5f, 0.0f,
+ 0.5f, -0.5f, 0.0f,
+ 0.0f, 0.5f, 0.0f
+ };

+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr);

+ glGenBuffers(1, &m_IndexBuffer);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IndexBuffer);

+ unsigned int indices[3] = { 0, 1, 2 };
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);


}

Application::~Application() {}

void Application::PushLayer(Layer* layer)
{
m_LayerStack.PushLayer(layer);
}

void Application::PushOverlay(Layer* layer)
{
m_LayerStack.PushOverlay(layer);
}


void Application::OnEvent(Event& e)
{
EventDispatcher dispatcher(e);
dispatcher.Dispatch<WindowCloseEvent>(BIND_EVENT_FN(OnWindowClose));

for (auto it = m_LayerStack.end(); it != m_LayerStack.begin();)
{
(*--it)->OnEvent(e);
if (e.Handled)
break;
}
}

void Application::Run()
{
while (m_Running)
{
glClearColor(0.1f, 0.1f, 0.1f, 1);
glClear(GL_COLOR_BUFFER_BIT);

glBindVertexArray(m_VertexArray);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, nullptr);


for (Layer* layer : m_LayerStack)
layer->OnUpdate();

// auto [x, y] = Input::GetMousePosition();
// IFN_CORE_TRACE("{0}, {1}", x, y);

m_ImGuiLayer->Begin();
for (Layer* layer : m_LayerStack)
layer->OnImGuiRender();
m_ImGuiLayer->End();

m_Window->OnUpdate();
}
}

bool Application::OnWindowClose(WindowCloseEvent& e)
{
m_Running = false;
return true;
}

}

调试信息

OpenGLContext.cpp 添加硬件调试信息:

#include "ifnpch.h"
#include "OpenGLContext.h"

#include <GLFW/glfw3.h>
#include <glad/glad.h>
#include <GL/GL.h>

namespace Infinite {


OpenGLContext::OpenGLContext(GLFWwindow* windowHandle)
: m_WindowHandle(windowHandle)
{
IFN_CORE_ASSERT(windowHandle, "Window handle is null!")
}

void OpenGLContext::Init()
{
glfwMakeContextCurrent(m_WindowHandle);
int status = gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
IFN_CORE_ASSERT(status, "Failed to initialize Glad!");


+ IFN_CORE_INFO("OpenGL Info:");
+ IFN_CORE_INFO(" Vendor: {0}", glGetString(GL_VENDOR));
+ IFN_CORE_INFO(" Renderer: {0}", glGetString(GL_RENDERER));
+ IFN_CORE_INFO(" Version: {0}", glGetString(GL_VERSION));
}

void OpenGLContext::SwapBuffers()
{
glfwSwapBuffers(m_WindowHandle);
}

}

调试

打开 NVIDIA 设置面板,将我们的引擎程序设置为高性能模式:

可以看到调试信息显示为当前机器的显卡配置: