Vertex Arrays
Vertex Array 顾名思义就是存储顶点数据的数组,但是实际上其并没有存储真正的数据内容,Vertex Array 做的实际上只是 VertexBuffer 和 IndexBuffer 的 reference,我们对 VertexBuffer 和 IndexBuffer 进行操作的时候,都要确定已经绑定到了 VertexArray 上,才能进行其他操作。
本节进行的内容就是将 VertexArray 抽象出来,也就是让这部分代码能够抽象到 VertexArray 中:
glGenVertexArrays(1, &m_VertexArray);
glBindVertexArray(m_VertexArray);
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++;
}
新建VertexArray.h
VertexArray.cpp
这里 Cherno 说由于其他 API 可能没有这个概念,所以单独放到两个文件中。
对于 VertexArray 我们想要实现的操作有: Bind,Unbind,以及对 VertexBuffer 和 IndexBuffer 的引用,
class VertexArray
{
public:
virtual ~VertexArray() = default;
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>>& GetVertexBuffer() const = 0;
virtual const std::shared_ptr<IndexBuffer>& GetIndexuffer() const = 0;
static VertexArray* Create();
};
然后针对 OpenGL , 来实现上面的几个函数:
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>>& GetVertexBuffer() const override { return m_VertexBuffers; }
virtual const std::shared_ptr<IndexBuffer>& GetIndexuffer() const override { return m_IndexBuffer; }
private:
std::vector<std::shared_ptr<VertexBuffer>> m_VertexBuffers;
std::shared_ptr<IndexBuffer> m_IndexBuffer;
uint32_t m_RendererID;
};
可以看到,在 VertexArray 中,存储了一个 VertexBuffer 的容器,但是只存储了一个 IndexBuffer 的指针,也就谁说可以有多个 VAO 但是只有一个 IBO,这是为什么呢?
经过查阅资料之后:
The index buffer contains integers, three for each triangle in the mesh, which reference the various attribute buffers (position, colour, UV coordinates, other UV coordinates, normal, …). It’s a little bit like in the OBJ file format, with one huge difference : there is only ONE index buffer. This means that for a vertex to be shared between two triangles, all attributes must be the same.
函数定义:
static GLenum ShaderDataTypeToOpenGLBaseType(ShaderDataType type)
{
switch (type)
{
case Hazel::ShaderDataType::Float: return GL_FLOAT;
case Hazel::ShaderDataType::Float2: return GL_FLOAT;
case Hazel::ShaderDataType::Float3: return GL_FLOAT;
case Hazel::ShaderDataType::Float4: return GL_FLOAT;
case Hazel::ShaderDataType::Mat3: return GL_FLOAT;
case Hazel::ShaderDataType::Mat4: return GL_FLOAT;
case Hazel::ShaderDataType::Int: return GL_INT;
case Hazel::ShaderDataType::Int2: return GL_INT;
case Hazel::ShaderDataType::Int3: return GL_INT;
case Hazel::ShaderDataType::Int4: return GL_INT;
case Hazel::ShaderDataType::Bool: return GL_BOOL;
}
HZ_CORE_ASSERT(false, "Unknown ShaderDataType!");
return 0;
}
OpenGLVertexArray::OpenGLVertexArray()
{
glGenVertexArrays(1, &m_RendererID);
}
OpenGLVertexArray::~OpenGLVertexArray()
{
glDeleteVertexArrays(1, &m_RendererID);
}
void OpenGLVertexArray::Bind() const
{
glBindVertexArray(m_RendererID);
}
void OpenGLVertexArray::Unbind() const
{
glBindVertexArray(0);
}
void OpenGLVertexArray::AddVertexBuffer(const std::shared_ptr<VertexBuffer>& vertexBuffer)
{
glBindVertexArray(m_RendererID);
vertexBuffer->Bind();
HZ_CORE_ASSERT(vertexBuffer->GetLayout().GetElements().size(), "Vertex Buffer has no layout!");
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_RendererID);
indexBuffer->Bind();
m_IndexBuffer = indexBuffer;
}
在这个部分,我们将 glEnableVertexAttribArray
以及 glVertexAttribPointer
的实现放到了 AddVertexBuffer 的部分,每次进行添加 VertexBuffer 的时候,进行布局的指定并将当前 buffer 存到 vector 中,在设置 IndexBuffer 的时候,将 indexbuffer 赋值给 成员变量即可。
同时,将 ShaderDataTypeToOpenGLBaseType
静态函数也放到了这里,便于glVertexAttribPointer
调用。
之后可以在 Application 中进行使用,首先创建成员变量 m_VertexArray ,然后在构造函数中进行初始化和使用:
m_VertexArray.reset(VertexArray::Create());
之后将原本的成员变量 m_VertexBuffer 和 m_IndexBuffer 删除,直接在构造函数中定义并且和 m_VertexArray 进行链接
std::shared_ptr<VertexBuffer> vertexBuffer;
vertexBuffer.reset(VertexBuffer::Create(vertices, sizeof(vertices)));
BufferLayout layout = {
{ ShaderDataType::Float3, "a_Position", false},
{ ShaderDataType::Float4, "a_Color", false}
};
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);
为了进行测试,Cherno 多创建了一个 squareArray ,并添加了 buffer 和 shader
m_SquareVA.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> squareVB;
squareVB.reset(VertexBuffer::Create(squareVertices,sizeof(squareVertices)));
squareVB->SetLayout({
{ ShaderDataType::Float3, "a_Position", false }
});
m_SquareVA->AddVertexBuffer(squareVB);
uint32_t squareIndices[6] = { 0, 1, 2, 2, 3, 0};
std::shared_ptr<IndexBuffer> squareIB;
squareIB.reset(IndexBuffer::Create(squareIndices, sizeof(squareIndices) / sizeof(uint32_t)));
m_SquareVA->SetIndexBuffer(squareIB);
std::string blue_vertexSrc = 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 blue_fragmentSrc = 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(blue_vertexSrc, blue_fragmentSrc));
m_blueShader.reset(new Shader(blue_vertexSrc, blue_fragmentSrc));
Runloop 中测试:
glClearColor(0.1f, 0.1f, 0.1f, 1);
glClear(GL_COLOR_BUFFER_BIT);
m_blueShader->Bind();
m_SquareVA->Bind();
glDrawElements(GL_TRIANGLES, m_SquareVA->GetIndexuffer()->GetCount(), GL_UNSIGNED_INT, nullptr);
m_Shader->Bind();
m_VertexArray->Bind();
glDrawElements(GL_TRIANGLES, m_VertexArray->GetIndexuffer()->GetCount(), GL_UNSIGNED_INT, nullptr);
Cherno 在创建 shared_ptr 的时候出现了下面的报错:
然后定位到下面的代码:
然后改成了这样之后就能跑了:
std::shared_ptr<IndexBuffer> indexBuffer;
indexBuffer.reset(IndexBuffer::Create(indices, sizeof(indices) / sizeof(uint32_t)));
具体原因由于目前 C++还不是很精通,先放在这里,之后有时间再回来学习