瀏覽代碼

feat: 添加 VBLayout 和 VertexArray 的封装

nicetry12138 1 年之前
父節點
當前提交
775239b226

二進制
图形学/OpenGL学习/Image/016.png


+ 90 - 0
图形学/OpenGL学习/README.md

@@ -978,3 +978,93 @@ public:
 
 接下来就是抽象顶点数组 `VAO` ,一个顶点数组需要做的就是将一个顶点缓冲区与某种布局绑定在一起
 
+因为一个 `VertexBuffer` 的 `data` 数组中的一个元素可能存在多种布局 (比如 `RGBA` 是 4 个 `unsigned int`、 `Position` 是 3 个 `float`),所以需要一个布局数组来表示这个关系
+
+```cpp
+struct VertexBufferElement
+{
+	unsigned int type;
+	unsigned int count;
+	unsigned int normalized;
+};
+
+typedef std::vector<VertexBufferElement> VBElemnts;
+
+class VertexBufferLayout
+{
+private:
+	VBElemnts m_Elements;
+	GLuint m_Stride{ 0 };
+
+public:
+
+	template<typename T>
+	void Push(unsigned int count) {
+		//static_assert(false);
+	}
+
+	template<>
+	void Push<GLfloat>(unsigned int count) {
+		m_Elements.push_back({ GL_FLOAT, count, GL_FALSE });
+		m_Stride += sizeof(GLfloat);
+	}
+
+	template<>
+	void Push<GLuint>(unsigned int count) {
+		m_Elements.push_back({ GL_UNSIGNED_INT, count, GL_FALSE });
+		m_Stride += sizeof(GLuint);
+	}
+
+	template<>
+	void Push<GLubyte>(unsigned int count) {
+		m_Elements.push_back({ GL_UNSIGNED_BYTE, count, GL_FALSE });
+		m_Stride += sizeof(GLubyte);
+	}
+
+	inline const VBElemnts GetElements() const { return m_Elements; }
+
+	inline GLuint GetStride() const { return m_Stride; }
+};
+```
+
+使用 `VertexBufferElement` 来表示一个布局。如果表示 `RGBA` 的话,那么 `type` 就是 `GL_UNSIGNED_CHAR`, `count` 就是 4, `normalized` 可以是 `GL_FALSE`;如果表示 `Position2D` 的话,那么 `type` 就是 `GL_FLOAT`,`count` 就是 2,`normalized` 就是 `GL_FALSE`
+
+总之,使用 `VertexBufferElement` 可以表示一种数据布局
+
+使用 `VertexBufferLayout` 来维护一个 `VertexBufferElement` 数组。如前面所说,一个顶点可以有多种数据,包括颜色、坐标、法线等,那么就需要多个 `VertexBufferElement` 来表示这些数据,所以一个顶点布局 `VertexBufferLayout` 本质就是维护 `VertexBufferElement` 数组
+
+![](Image/016.png)
+
+大致如上图所示, `data` 是一块内存区域,如何解析这块区域就得靠布局来解释,假设地址的起始指针为 `data`
+
+| 元素 | 起始地址 |
+| --- | --- |
+| vertex[0].RGBA | data |
+| vertex[0].Vec2 | data + sizeof(RGBA) |
+| vertex[1].RGBA | data + sizeof(Vertex) |
+| vertex[1].Vec2 | data + sizeof(Vertex) + sizeof(RGBA) |
+
+假设 RGBA 是 4 个 `unsigned char` 组成,那么 `sizeof(RGBA)` 大小就是 `4 * sizeof(unsigned char)`,这个大小就是 `offset` 地址偏移
+
+所以之前一直说,知道了**布局**、**起始地址**、**数组长度**就知道了怎么遍历这个 `Vertex` 数组
+
+那么在 `VertexArray` 中要做的就是将其对应的 `VertexBuffer` 通过 `VertexBufferLayout` 进行解释说明
+
+```cpp
+void VertexArray::AddBuffer(const VertexBuffer& vb, const VertexBufferLayout& layout)
+{
+	Bind();
+	vb.Bind();
+
+	const auto& elements = layout.GetElements();
+	unsigned int offset = 0;
+
+	for (unsigned int index = 0; index < elements.size(); ++index) {
+		const auto& element = elements[index];
+		GL_CALL(glEnableVertexAttribArray(0));
+		GL_CALL(glVertexAttribPointer(index, element.count, element.type, element.normalized, layout.GetStride(), (const void*)offset));
+		offset += element.count * VertexBufferElement::GetSizeOfType(element.type);
+	}
+}
+```
+

+ 9 - 6
图形学/OpenGL学习/src/OpenGLStudy/OpenGLStudy/src/Application.cpp

@@ -6,6 +6,8 @@
 #include "Renderer.h"
 #include "VertexBuffer.h"
 #include "IndexBuffer.h"
+#include "VertexArray.h"
+#include "VertexBufferLayout.h"
 
 static GLuint CompiledShader(const std::string& source, GLenum inType) {
 	GLuint id = glCreateShader(inType);
@@ -101,9 +103,6 @@ int main(void)
 
 	std::cout << glGetString(GL_VERSION) << std::endl;
 
-	GLuint vao;
-	glGenVertexArrays(1, &vao);
-	glBindVertexArray(vao);
 	{
 		float positions[] = {
 			-0.5f, -0.5f,
@@ -117,10 +116,14 @@ int main(void)
 			2, 3, 0
 		};
 
+		VertexArray va;
+
 		VertexBuffer vb(positions, sizeof(float) * 2 * 4);
 
-		GL_CALL(glEnableVertexAttribArray(0));
-		GL_CALL(glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, 0));
+		VertexBufferLayout layout;
+		layout.Push<float>(2);
+
+		va.AddBuffer(vb, layout);
 
 		IndexBuffer ibo(indeices, 6);
 
@@ -151,7 +154,7 @@ int main(void)
 			//glDrawElements(GL_TRIANGLES, 6, GL_UNIFORM, 0);
 			//GL_CHECK_ERROR; 
 
-			glBindVertexArray(vao);
+			va.Bind();
 			ibo.Bind();
 
 			r += increment;

+ 38 - 1
图形学/OpenGL学习/src/OpenGLStudy/OpenGLStudy/src/VertexArray.cpp

@@ -1,3 +1,40 @@
 #include "VertexArray.h"
 #include "VertexBuffer.h"
-#include "IndexBuffer.h"
+#include "IndexBuffer.h"
+#include "VertexBufferLayout.h"
+
+VertexArray::VertexArray()
+{
+	GL_CALL(glGenVertexArrays(1, &m_RendererId));
+}
+
+VertexArray::~VertexArray()
+{
+	GL_CALL(glDeleteVertexArrays(1, &m_RendererId));
+}
+
+void VertexArray::AddBuffer(const VertexBuffer& vb, const VertexBufferLayout& layout)
+{
+	Bind();
+	vb.Bind();
+
+	const auto& elements = layout.GetElements();
+	unsigned int offset = 0;
+
+	for (unsigned int index = 0; index < elements.size(); ++index) {
+		const auto& element = elements[index];
+		GL_CALL(glEnableVertexAttribArray(0));
+		GL_CALL(glVertexAttribPointer(index, element.count, element.type, element.normalized, layout.GetStride(), (const void*)offset));
+		offset += element.count * VertexBufferElement::GetSizeOfType(element.type);
+	}
+}
+
+void VertexArray::Bind()
+{
+	GL_CALL(glBindVertexArray(m_RendererId));
+}
+
+void VertexArray::Unbind()
+{
+	GL_CALL(glBindVertexArray(0));
+}

+ 5 - 0
图形学/OpenGL学习/src/OpenGLStudy/OpenGLStudy/src/VertexArray.h

@@ -1,4 +1,5 @@
 #pragma once
+#include "Renderer.h"
 
 class VertexBuffer;
 class VertexBufferLayout;
@@ -6,11 +7,15 @@ class VertexBufferLayout;
 class VertexArray
 {
 private:
+	GLuint m_RendererId;
 
 public:
 	VertexArray();
 	~VertexArray();
 
 	void AddBuffer(const VertexBuffer& vb, const VertexBufferLayout& layout);
+
+	void Bind();
+	void Unbind();
 };
 

+ 41 - 11
图形学/OpenGL学习/src/OpenGLStudy/OpenGLStudy/src/VertexBufferLayout.h

@@ -4,33 +4,63 @@
 
 struct VertexBufferElement
 {
-	unsigned int type;
-	unsigned int count;
-	bool normalized;
+	GLuint type;
+	GLuint count;
+	GLuint normalized;
+
+	static GLuint GetSizeOfType(GLuint type) {
+		switch (type)
+		{
+		case GL_FLOAT:
+			return sizeof(GLfloat);
+			break;
+		case GL_UNSIGNED_INT:
+			return sizeof(GLuint);
+			break;
+		case GL_UNSIGNED_BYTE:
+			return sizeof(GLubyte);
+			break;;
+		}
+		//ASSERT(false);
+		return 0;
+	}
 };
 
+typedef std::vector<VertexBufferElement> VBElemnts;
+
 class VertexBufferLayout
 {
 private:
-	std::vector<VertexBufferElement> m_Elements;
+	VBElemnts m_Elements;
+	GLuint m_Stride{ 0 };
 
 public:
-	VertexBufferLayout();
-	~VertexBufferLayout();
 
 	template<typename T>
 	void Push(unsigned int count) {
-		static_assert(false);
+		//static_assert(false);
+	}
+
+	template<>
+	void Push<GLfloat>(unsigned int count) {
+		m_Elements.push_back({ GL_FLOAT, count, GL_FALSE });
+		m_Stride += VertexBufferElement::GetSizeOfType(GL_FLOAT) * count;
 	}
 
 	template<>
-	void Push<float>(unsigned int count) {
-		m_Elements.push_back({GL_FLOAT, count, false});
+	void Push<GLuint>(unsigned int count) {
+		m_Elements.push_back({ GL_UNSIGNED_INT, count, GL_FALSE });
+		m_Stride += VertexBufferElement::GetSizeOfType(GL_UNSIGNED_INT) * count;
 	}
 
 	template<>
-	void Push<int>(unsigned int count) {
-		m_Elements.push_back({ GL_UNSIGNED_INT, count, false });
+	void Push<GLubyte>(unsigned int count) {
+		m_Elements.push_back({ GL_UNSIGNED_BYTE, count, GL_FALSE });
+		m_Stride += VertexBufferElement::GetSizeOfType(GL_UNSIGNED_BYTE) * count;
 	}
+
+	inline const VBElemnts GetElements() const { return m_Elements; }
+
+	inline GLuint GetStride() const { return m_Stride; }
 };