Vertex Arrays

引言

上一节我们讨论了顶点缓冲区布局,设计了一个类可以优雅地为我们处理所有事情。今天我们来看看顶点数组。

从本质上讲,顶点数组是一种包含实体的状态。它们最好实际上包含所有数据,例如你的顶点缓冲区和索引缓冲区,这两部分是你存储数据的实际缓冲区。

而实际上顶点数组只有链接,它们仅有指向现有顶点缓冲区和索引缓冲区的指针。

顶点数组抽象

Infinite/Renderer/ 目录下新建 VertexArray.hVertexArray.cpp

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
#pragma once

#include <memory>
#include "../Renderer/Buffer.h"

namespace Infinite {


class VertexArray
{
public:
virtual ~VertexArray() {}

virtual void Bind() const = 0;
virtual void Unbind() const = 0;

virtual void AddVertexBuffer(const std::shared_ptr<VertexBuffer>& vertexBuffer) = 0;
virtual void SetIndexBuffer(const std::shared_ptr<IndexBuffer>& indexBuffer) = 0;

virtual const std::vector<std::shared_ptr<VertexBuffer>>& GetVertexBuffers() const = 0;
virtual const std::shared_ptr<IndexBuffer>& GetIndexBuffer() const = 0;

static VertexArray* Create();
};


}
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
#include "ifnpch.h"
#include "VertexArray.h"

#include "Renderer.h"
#include "./Platform/OpenGL/OpenGLVertexArray.h"

namespace Infinite {

VertexArray* VertexArray::Create()
{
switch (Renderer::GetAPI())
{
case RendererAPI::None:
IFN_ASSERT(false, "RendererAPI::None is currently not supported!");
return nullptr;
case RendererAPI::OpenGL:
return new OpenGLVertexArray();
}

IFN_ASSERT(false, "Unknown RendererAPI!");
return nullptr;
}


}

OpenGL顶点数组实现

src/Platform/OpenGL/ 目录下新建 OpenGLVertexArray.hOpenGLVertexArray.cpp

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
#pragma once

#include "Infinite/Renderer/VertexArray.h"

namespace Infinite {

class OpenGLVertexArray : public VertexArray
{
public:
OpenGLVertexArray();

virtual ~OpenGLVertexArray() {}

virtual void Bind() const override;
virtual void Unbind() const override;

virtual void AddVertexBuffer(const std::shared_ptr<VertexBuffer>& vertexBuffer) override;
virtual void SetIndexBuffer(const std::shared_ptr<IndexBuffer>& indexBuffer) override;

virtual const std::vector<std::shared_ptr<VertexBuffer>>& GetVertexBuffers() const { return m_VertexBuffers; }
virtual const std::shared_ptr<IndexBuffer>& GetIndexBuffer() const { return m_IndexBuffer; }

private:
uint32_t m_RedererID;
std::vector<std::shared_ptr<VertexBuffer>> m_VertexBuffers;
std::shared_ptr<IndexBuffer> m_IndexBuffer;
};

}
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
#include "ifnpch.h"
#include "OpenGLVertexArray.h"

#include <glad/glad.h>

namespace Infinite {

static GLenum ShaderDataTypeToOpenGLBaseType(ShaderDataType type)
{
switch (type)
{
case Infinite::ShaderDataType::Float: return GL_FLOAT;
case Infinite::ShaderDataType::Float2: return GL_FLOAT;
case Infinite::ShaderDataType::Float3: return GL_FLOAT;
case Infinite::ShaderDataType::Float4: return GL_FLOAT;
case Infinite::ShaderDataType::Mat3: return GL_FLOAT;
case Infinite::ShaderDataType::Mat4: return GL_FLOAT;
case Infinite::ShaderDataType::Int: return GL_INT;
case Infinite::ShaderDataType::Int2: return GL_INT;
case Infinite::ShaderDataType::Int3: return GL_INT;
case Infinite::ShaderDataType::Int4: return GL_INT;
case Infinite::ShaderDataType::Bool: return GL_BOOL;
}

IFN_CORE_ASSERT(false, "Unknown ShaderDataType!");
return 0;
}

OpenGLVertexArray::OpenGLVertexArray()
{
glCreateVertexArrays(1, &m_RedererID);
}

void OpenGLVertexArray::Bind() const
{
glBindVertexArray(m_RedererID);
}

void OpenGLVertexArray::Unbind() const
{
glBindVertexArray(0);
}

void OpenGLVertexArray::AddVertexBuffer(const std::shared_ptr<VertexBuffer>& vertexBuffer)
{
IFN_CORE_ASSERT(vertexBuffer->GetLayout().GetElements().size(), "Vertex buffer has no layout!");

glBindVertexArray(m_RedererID);
vertexBuffer->Bind();

uint32_t index = 0;
const auto& layout = vertexBuffer->GetLayout();
for (const auto& element : layout)
{
glEnableVertexAttribArray(index);
glVertexAttribPointer(index,
element.GetComponentCount(),
ShaderDataTypeToOpenGLBaseType(element.Type),
element.Normalized ? GL_TRUE : GL_FALSE,
layout.GetStride(),
(const void*)element.Offset);
index++;
}

m_VertexBuffers.push_back(vertexBuffer);
}

void OpenGLVertexArray::SetIndexBuffer(const std::shared_ptr<IndexBuffer>& indexBuffer)
{
glBindVertexArray(m_RedererID);
indexBuffer->Bind();

m_IndexBuffer = indexBuffer;
}


}

应用类调整

Application.h 用顶点数组取代之前的缓冲:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "./Renderer/Shader.h"
#include "./Renderer/Buffer.h"
+#include "./Renderer/VertexArray.h"

namespace Infinite {
class INFINITE_API Application
@@ -35,10 +36,11 @@ namespace Infinite {
bool m_Running = true;
LayerStack m_LayerStack;

- unsigned int m_VertexArray;
- std::unique_ptr<Shader> m_Shader;
- std::unique_ptr<VertexBuffer> m_VertexBuffer;
- std::unique_ptr<IndexBuffer> m_IndexBuffer;
+ std::shared_ptr<Shader> m_Shader;
+ std::shared_ptr<VertexArray> m_VertexArray;
+
+ std::shared_ptr<Shader> m_BlueShader;
+ std::shared_ptr<VertexArray> m_SquareVertexArray;
private:
static Application* s_Instance;
};

Application.cpp 继续解耦:

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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
@@ -14,27 +14,6 @@ namespace Infinite {

Application* Application::s_Instance = nullptr;

- static GLenum ShaderDataTypeToOpenGLBaseType(ShaderDataType type)
- {
- switch (type)
- {
- case Infinite::ShaderDataType::Float: return GL_FLOAT;
- case Infinite::ShaderDataType::Float2: return GL_FLOAT;
- case Infinite::ShaderDataType::Float3: return GL_FLOAT;
- case Infinite::ShaderDataType::Float4: return GL_FLOAT;
- case Infinite::ShaderDataType::Mat3: return GL_FLOAT;
- case Infinite::ShaderDataType::Mat4: return GL_FLOAT;
- case Infinite::ShaderDataType::Int: return GL_INT;
- case Infinite::ShaderDataType::Int2: return GL_INT;
- case Infinite::ShaderDataType::Int3: return GL_INT;
- case Infinite::ShaderDataType::Int4: return GL_INT;
- case Infinite::ShaderDataType::Bool: return GL_BOOL;
- }
-
- IFN_CORE_ASSERT(false, "Unknown ShaderDataType!");
- return 0;
- }

Application::Application()
{
IFN_CORE_ASSERT(!s_Instance, "Application already exists!");
@@ -46,9 +25,7 @@ namespace Infinite {
m_ImGuiLayer = new ImGuiLayer();
PushOverlay(m_ImGuiLayer);

- // Vertex Array
- glGenVertexArrays(1, &m_VertexArray);
- glBindVertexArray(m_VertexArray);
- m_VertexArray.reset(VertexArray::Create());


float vertices[3 * 7] = {
@@ -57,34 +34,42 @@ namespace Infinite {
0.0f, 0.5f, 0.0f, 0.8f, 0.8f, 0.2f, 1.0f
};

- m_VertexBuffer.reset(VertexBuffer::Create(vertices, sizeof(vertices)));
+ std::shared_ptr<VertexBuffer> vertexBuffer;
+ vertexBuffer.reset(VertexBuffer::Create(vertices, sizeof(vertices)));
+ BufferLayout layout = {
+ { ShaderDataType::Float3, "a_Position" },
+ { ShaderDataType::Float4, "a_Color" }
+ };

- {
- BufferLayout layout = {
- { ShaderDataType::Float3, "a_Position" },
- { ShaderDataType::Float4, "a_Color" }
- };
+ vertexBuffer->SetLayout(layout);
+ m_VertexArray->AddVertexBuffer(vertexBuffer);

+ uint32_t indices[3] = { 0, 1, 2 };
+ std::shared_ptr<IndexBuffer> indexBuffer;
+ indexBuffer.reset(IndexBuffer::Create(indices, sizeof(indices) / sizeof(uint32_t)));
+ m_VertexArray->SetIndexBuffer(indexBuffer);

- m_VertexBuffer->SetLayout(layout);
- }

- uint32_t index = 0;
- const auto& layout = m_VertexBuffer->GetLayout();
- for (const auto& element: layout)
- {
- glEnableVertexAttribArray(index);
- glVertexAttribPointer(index,
- element.GetComponentCount(),
- ShaderDataTypeToOpenGLBaseType(element.Type),
- element.Normalized ? GL_TRUE : GL_FALSE,
- layout.GetStride(),
- (const void*)element.Offset);
- index++;
- }

+ m_SquareVertexArray.reset(VertexArray::Create());
+
+ float squareVertices[3 * 4] = {
+ -0.75f, -0.75f, 0.0f,
+ 0.75f, -0.75f, 0.0f,
+ 0.75f, 0.75f, 0.0f,
+ -0.75f, 0.75f, 0.0f
+ };
+ std::shared_ptr<VertexBuffer> squareVertexBuffer;
+ squareVertexBuffer.reset(VertexBuffer::Create(squareVertices, sizeof(squareVertices)));
+ squareVertexBuffer->SetLayout({
+ { ShaderDataType::Float3, "a_Position" }
+ });
+ m_SquareVertexArray->AddVertexBuffer(squareVertexBuffer);
+
+ uint32_t squareIndices[6] = { 0, 1, 2, 2, 3, 0 };
+ std::shared_ptr<IndexBuffer> squareIndexBuffer;
+ squareIndexBuffer.reset(IndexBuffer::Create(squareIndices, sizeof(squareIndices) / sizeof(uint32_t)));
+ m_SquareVertexArray->SetIndexBuffer(squareIndexBuffer);

- uint32_t indices[3] = { 0, 1, 2 };
- m_IndexBuffer.reset(IndexBuffer::Create(indices, sizeof(indices) / sizeof(uint32_t)));


std::string vertexSrc = R"(
@@ -119,9 +104,35 @@ namespace Infinite {
}
)";

+ m_Shader.reset(new Shader(vertexSrc, fragmentSrc));


- m_Shader.reset(new Shader(vertexSrc, fragmentSrc));

+ std::string blueShaderVertexSrc = R"(
+ #version 330 core
+
+ layout(location = 0) in vec3 a_Position;
+ out vec3 v_Position;
+ void main()
+ {
+ v_Position = a_Position;
+ gl_Position = vec4(a_Position, 1.0);
+ }
+ )";
+
+ std::string blueShaderFragmentSrc = R"(
+ #version 330 core
+
+ layout(location = 0) out vec4 color;
+ in vec3 v_Position;
+ void main()
+ {
+ color = vec4(0.2, 0.3, 0.8, 1.0);
+ }
+ )";
+
+ m_BlueShader.reset(new Shader(blueShaderVertexSrc, blueShaderFragmentSrc));


}

@@ -156,16 +167,17 @@ namespace Infinite {
glClearColor(0.1f, 0.1f, 0.1f, 1);
glClear(GL_COLOR_BUFFER_BIT);

- m_Shader->Bind();
- glBindVertexArray(m_VertexArray);
- glDrawElements(GL_TRIANGLES, m_IndexBuffer->GetCount(), GL_UNSIGNED_INT, nullptr);
+ m_BlueShader->Bind();
+ m_SquareVertexArray->Bind();
+ glDrawElements(GL_TRIANGLES, m_SquareVertexArray->GetIndexBuffer()->GetCount(), GL_UNSIGNED_INT, nullptr);

+ m_Shader->Bind();
+ m_VertexArray->Bind();
+ glDrawElements(GL_TRIANGLES, m_VertexArray->GetIndexBuffer()->GetCount(), 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)

加入一个矩形 Shader 观察效果。

调试