在之前的代码中,生成、绑定顶点数组对象,生成、绑定顶点缓冲对象,启用顶点属性数组,写了很长一部分,很麻烦。因此集成为类。
主义理清vao与vbo之间的关系
VertexArray va;
VertexBuffer vb(positions, 4 * 2 * sizeof(float));
VertexBufferLayout layout;
layout.Push<float>(2);// layout主要是数据的格式
va.AddBuffer(vb, layout);
分析一下代码,va是顶点数组 vertex array object,vb是顶点缓冲对象 vertex buffer object 。positions是顶点数组,存有二维顶点数据,共有四个点,用来绘制矩形。
先抛开layout不谈。
VertexArray声明
#pragma once
#include "VertexBuffer.h"
#include "VertexBufferLayout.h"
class VertexArray
{
private:
unsigned int m_RendererID;
public:
VertexArray();
~VertexArray();
void AddBuffer(const VertexBuffer& vb, const VertexBufferLayout& layout);
void Bind() const;
void UnBind() const;
};
定义
#include "VertexArray.h"
#include "Renderer.h"
VertexArray::VertexArray()
{
/* generate vertex array object names,指定个数1,存储名字到m_RendererID */
GLCall(glGenVertexArrays(1, &m_RendererID));
}
VertexArray::~VertexArray()
{
GLCall(glDeleteVertexArrays(1, &m_RendererID));
}
void VertexArray::AddBuffer(const VertexBuffer& vb, const VertexBufferLayout& layout)
{
Bind();/*绑定到当前vao*/
vb.Bind();/*绑定vbo。绑定顶点缓冲区*/
const auto& elements = layout.GetElements(); /*设置布局*/
unsigned int offset = 0;
for (unsigned int i = 0; i < elements.size(); i++)
{
const auto& element = elements[i];
GLCall(glEnableVertexAttribArray(i));/* 0位置, 1颜色*/
/*例如:0-位置,2-二维;1-颜色,4-rgbx四个属性确定一个颜色
stride-数据步长,pointer-指针指向当前index对应的起始偏移量*/
GLCall(glVertexAttribPointer(i, element.count, element.type,
element.normalized , layout.GetStride(), (const void*)offset));/* (const void)*/
// offset += element.count;
offset += element.count * VertexBufferElement::GetSizeOfType(element.type);
}
}
void VertexArray::Bind() const
{
/*参数array
Specifies the name of the vertex array to bind.*/
GLCall(glBindVertexArray(m_RendererID));
}
void VertexArray::UnBind() const
{
GLCall(glBindVertexArray(0));
}
可以看到,初始化时生成了 vao,并存储了名字到 m_RendererID
VertexArray::VertexArray()
{
/* generate vertex array object names,指定个数1,存储名字到m_RendererID */
GLCall(glGenVertexArrays(1, &m_RendererID));
}
后面使用时还需要绑定,因此在函数AddBuffer
中先调用 Bind
:
Bind();/*绑定到当前vao*/
void VertexArray::Bind() const
{
/*参数array
Specifies the name of the vertex array to bind.*/
GLCall(glBindVertexArray(m_RendererID));
}
对于 vbo
同样的,初始化函数里生成并存储名字,接着使用前需要绑定:vb.Bind();/*绑定vbo。绑定顶点缓冲区*/
现在绑定好了vao vbo,接着就是启用顶点数组 glEnableVertexAttribArray
,接着规定数据格式 glVertexAttribPointer
主要是这几句:
for (unsigned int i = 0; i < elements.size(); i++)
{
const auto& element = elements[i];
GLCall(glEnableVertexAttribArray(i));/* 0位置, 1颜色*/
/*例如:0-位置,2-二维;1-颜色,4-rgbx四个属性确定一个颜色
stride-数据步长,pointer-指针指向当前index对应的起始偏移量*/
GLCall(glVertexAttribPointer(i, element.count, element.type,
element.normalized , layout.GetStride(), (const void*)offset));/* (const void)*/
// offset += element.count;
offset += element.count * VertexBufferElement::GetSizeOfType(element.type);
}
根据 glVertexAttribPointer
的参数,我们就可以分析出 layout 的作用。
VertexBufferLayout.h
#pragma once
#include
#include
#include
#include "Renderer.h"
/*如果你不想因为使用一小部分glew的功能而引入 glew.h
可以进入定义,查看例如 GL_FLOAT的 定义,把你需要的部分 define 粘贴过来*/
struct VertexBufferElement
{
unsigned int type;
unsigned int count;
// bool normalized;
unsigned char normalized;
static unsigned int GetSizeOfType(unsigned int type)
{
switch (type)
{
case GL_FLOAT: return 4;
case GL_UNSIGNED_INT: return 4;
case GL_UNSIGNED_BYTE: return 1;
}
ASSERT(false);
return 0;
}
};
class VertexBufferLayout
{
private:
std::vector<VertexBufferElement> m_Elements;
unsigned int m_Stride;
public:
VertexBufferLayout()
: m_Stride(0) {};
template<typename T>
void Push(unsigned int count)
{/*
@lichform
1年前
If you're on VS2022 and having an issue with the static_assert getting tripped in the
unspecialized template, it's because VS2022 will trigger the assert when it's parsed,
rather than when it's instantiated. I changed it to a std::runtime_error which of course
isn't checked at compile time but at least I get a verbose error when I try to create a
VertexBufferLayout with an unsupported type.
@dr_nyt4041
1年前
Thanks, that helped!
#include
template
void Push(unsigned int count) {
std::runtime_error(false);
}*/
// static_assert(false);/*不知道为什么弹出这里的错误提示 ?*/
std::runtime_error(false);
}
template<>
void Push<float>(unsigned int count)
{
m_Elements.push_back({ GL_FLOAT, count, GL_FALSE });
// m_Stride += 4; /* sizeof(float) */
// m_Stride += sizeof(GLfloat);
m_Stride += count * VertexBufferElement::GetSizeOfType(GL_FLOAT);
}
template<>
void Push<unsigned int>(unsigned int count)
{
m_Elements.push_back({ GL_UNSIGNED_INT, count, GL_FALSE });
// m_Stride += sizeof(GLuint);
m_Stride += count * VertexBufferElement::GetSizeOfType(GL_UNSIGNED_INT);
}
template<>
void Push<unsigned char>(unsigned int count)
{
m_Elements.push_back({ GL_UNSIGNED_BYTE, count, GL_TRUE });
// m_Stride += sizeof(GLbyte);
m_Stride += count * VertexBufferElement::GetSizeOfType(GL_UNSIGNED_BYTE);
}
//inline const std::vector GetElements() const { return m_Elements; }
//inline unsigined int GetStride() const { return m_Stride; }
inline const std::vector<VertexBufferElement>& GetElements() const {
return m_Elements;
}
inline unsigned int GetStride() const {
return m_Stride;
}
};
总的代码:
仓库地址