ソースを参照

feat: 添加 Uniform Buffer Object 的使用

NiceTry12138 9 ヶ月 前
コミット
c0894719e9

+ 85 - 0
图形学/OpenGL学习/OpenGLDemo.md

@@ -918,3 +918,88 @@ glDrawArrays(GL_TRIANGLES, 0, 36);
 ```
 > 使用案例
 
+### 高级 GLSL
+
+`Uniform` 缓冲对象,按照之前写过的着色器来看,我们需要对 模型、灯光、天空盒 等物体的着色器设置 model、view、projection 三个矩阵,但是 view 和 projection 矩阵都是相同的
+
+为了避免重复设置, `OpenGL` 提供了 Uniform 缓冲对象
+
+首先需要在 着色器中定义 Uniform 块
+
+```glsl
+// GLSL 着色器中定义的 Uniform 块
+layout(std140) uniform MyUBO {
+    mat4 viewMatrix;
+    mat4 projectionMatrix;
+    vec3 lightPosition;
+    float lightIntensity;
+};
+```
+
+Uniform 缓冲对象(Uniform Buffer Object) 也是一种缓冲,使用 `glGenBuffer` 来创建它
+
+```cpp
+GLuint ubo;
+glGenBuffers(1, &ubo);
+glBindBuffer(GL_UNIFORM_BUFFER, ubo);
+
+// 根据 MyUBO 的定义 计算总大小(需手动对齐)
+const GLsizeiptr uboSize = 
+    sizeof(glm::mat4) * 2 +  // viewMatrix + projectionMatrix
+    sizeof(glm::vec4) +      // lightPosition(vec3 按 vec4 对齐)
+    sizeof(float);           // lightIntensity
+
+glBufferData(GL_UNIFORM_BUFFER, uboSize, NULL, GL_DYNAMIC_DRAW);
+```
+
+使用之前提到过的 `glMapBuffer` + `memcpy` 来填入数据
+
+```cpp
+// 假设使用 glm 数学库
+glm::mat4 view = ...;
+glm::mat4 projection = ...;
+glm::vec3 lightPos = ...;
+float intensity = ...;
+
+// 映射 UBO 并写入数据
+glBindBuffer(GL_UNIFORM_BUFFER, ubo);
+GLvoid* ptr = glMapBuffer(GL_UNIFORM_BUFFER, GL_WRITE_ONLY);
+
+// 写入 viewMatrix(偏移 0)
+memcpy(ptr, &view, sizeof(glm::mat4));
+
+// 写入 projectionMatrix(偏移 sizeof(mat4))
+memcpy((char*)ptr + sizeof(glm::mat4), &projection, sizeof(glm::mat4));
+
+// 写入 lightPosition(偏移 sizeof(mat4)*2,按 vec4 对齐)
+glm::vec4 lightPosAligned(lightPos, 0.0f); // 填充为 vec4
+memcpy((char*)ptr + sizeof(glm::mat4)*2, &lightPosAligned, sizeof(glm::vec4));
+
+// 写入 lightIntensity(偏移 sizeof(mat4)*2 + sizeof(vec4))
+memcpy((char*)ptr + sizeof(glm::mat4)*2 + sizeof(glm::vec4), &intensity, sizeof(float));
+
+glUnmapBuffer(GL_UNIFORM_BUFFER);
+```
+
+与 `Texture` 类似,将 Uniform Buffer Object 绑定到指定的槽上
+
+```cpp
+// 将 UBO 对象关联到绑定点 0
+glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo);
+
+// 对 shaderProgramA
+GLuint uboIndexA = glGetUniformBlockIndex(shaderProgramA, "MyUBO");
+glUniformBlockBinding(shaderProgramA, uboIndexA, 0); // 绑定到绑定点 0
+
+// 对 shaderProgramB
+GLuint uboIndexB = glGetUniformBlockIndex(shaderProgramB, "MyUBO");
+glUniformBlockBinding(shaderProgramB, uboIndexB, 0); // 同样绑定到绑定点 0
+```
+
+这里有一个点需要注意到,那就是着色器中定义 Uniform 块时使用的 `std140`
+
+`std140` 是 OpenGL 中用于定义 Uniform 缓冲对象(Uniform Buffer Object) 和 着色器缓冲对象(SSBO) **内存布局**的规则
+
+目的是**显式控制**,使得 CPU 和 GPU 能够正确读取数据,避免因为硬件差异导致的数据错位
+
+

+ 6 - 2
图形学/OpenGL学习/src/OpenGLDemo/OpenGLDemo/res/shader/SkyBox/model.vert

@@ -5,8 +5,12 @@ layout (location = 1) in vec3 aNormal;
 layout (location = 2) in vec2 aTexCoords;
 
 uniform mat4 model;
-uniform mat4 view;
-uniform mat4 projection;
+
+layout (std140) uniform Matrices
+{
+    mat4 view;
+    mat4 projection;
+};
 
 out vec2 TexCoords;
 

+ 11 - 0
图形学/OpenGL学习/src/OpenGLDemo/OpenGLDemo/src/Util/Shader.cpp

@@ -174,6 +174,17 @@ void Shader::SetUniform3f(const std::string& inName, float v0, float v1, float v
 	GL_CALL(glUniform3f(location, v0, v1, v2));
 }
 
+void Shader::BindUBO(const std::string& inName, int inSlot)
+{
+	Bind();
+	GLint location = glGetUniformBlockIndex(m_ShaderID, inName.c_str());
+	if (location == -1) {
+		std::cout << "Can't Find Uniform Buffer Object: " << inName << " In Shader";
+		return;
+	}
+	GL_CALL(glUniformBlockBinding(m_ShaderID, location, inSlot));
+}
+
 void Shader::Bind()
 {
 	if (s_CurrentBindShader == m_ShaderID)

+ 1 - 0
图形学/OpenGL学习/src/OpenGLDemo/OpenGLDemo/src/Util/Shader.h

@@ -13,6 +13,7 @@ public:
 	void SetUniform4f(const std::string& inName, float v0, float v1, float v2, float v3);
 	void SetUniformMat4f(const std::string& inName, const glm::mat4& inMat4);
 	void SetUniform3f(const std::string& inName, float v0, float v1, float v2);
+	void BindUBO(const std::string& inName, int inSlot);
 
 	void Bind();
 	void UnBind();

+ 27 - 3
图形学/OpenGL学习/src/OpenGLDemo/OpenGLDemo/src/testModule/TestSkyBox.cpp

@@ -4,6 +4,8 @@
 
 TestSkyBox TestSkyBox::_self;
 
+static GLuint UBOSLOT = 2;
+
 void TestSkyBox::OnEnter(GLFWwindow* window)
 {	// 启动深度测试
 	glEnable(GL_DEPTH_TEST);
@@ -16,6 +18,7 @@ void TestSkyBox::OnEnter(GLFWwindow* window)
 	BindMouse(window);
 
 	InitSkyBox();
+	InitUBO();
 }
 
 void TestSkyBox::OnExit(GLFWwindow* window)
@@ -32,6 +35,8 @@ void TestSkyBox::UpdateLogic(float delayTime)
 
 	// 可能会更新窗口视口大小 每帧更新一下
 	m_proj = glm::perspective(glm::radians(45.0f), (float)RSI->ViewportHeight / (float)RSI->ViewportWidth, 0.1f, 100.0f);
+
+	UpdateUBO();
 }
 
 void TestSkyBox::ClearRender(GLFWwindow* window)
@@ -41,6 +46,8 @@ void TestSkyBox::ClearRender(GLFWwindow* window)
 
 void TestSkyBox::Render(GLFWwindow* window)
 {
+	glBindBufferBase(GL_UNIFORM_BUFFER, UBOSLOT, m_UBO);
+
 	m_ModelShader.Bind();
 
 	auto CameraLocation = m_Camera.GetCameraLocation();
@@ -49,9 +56,8 @@ void TestSkyBox::Render(GLFWwindow* window)
 	model = glm::rotate(model, glm::radians(-90.0f), glm::vec3(1, 0, 0));
 
 	m_ModelShader.SetUniformMat4f("model", model);
-	m_ModelShader.SetUniformMat4f("view", m_view);
-	m_ModelShader.SetUniformMat4f("projection", m_proj);
-
+	m_ModelShader.BindUBO("Matrices", UBOSLOT);
+	
 	m_packageModel.Draw(m_ModelShader);
 
 	m_sky.Draw(m_view, m_proj);
@@ -105,3 +111,21 @@ void TestSkyBox::InitSkyBox()
 {
 	m_sky.Init();
 }
+
+void TestSkyBox::InitUBO()
+{
+	GL_CALL(glGenBuffers(1, &m_UBO));
+	GL_CALL(glBindBuffer(GL_UNIFORM_BUFFER, m_UBO));
+	GL_CALL(glBufferData(GL_UNIFORM_BUFFER, sizeof(glm::mat4) * 2, NULL, GL_DYNAMIC_DRAW));
+}
+
+void TestSkyBox::UpdateUBO()
+{
+	GL_CALL(glBindBuffer(GL_UNIFORM_BUFFER, m_UBO));
+	GLvoid* ptr = glMapBuffer(GL_UNIFORM_BUFFER, GL_WRITE_ONLY);
+
+	memcpy(ptr, &m_view, sizeof(m_view));
+	memcpy((char*)ptr + sizeof(m_view), &m_proj, sizeof(m_proj));
+
+	glUnmapBuffer(GL_UNIFORM_BUFFER);
+}

+ 5 - 0
图形学/OpenGL学习/src/OpenGLDemo/OpenGLDemo/src/testModule/TestSkyBox.h

@@ -34,6 +34,9 @@ public:
 
 protected:
 	void InitSkyBox();
+	
+	void InitUBO();
+	void UpdateUBO();
 
 private:
 	glm::mat4 m_model = glm::mat4(1.0f);		// 模型矩阵
@@ -45,6 +48,8 @@ private:
 
 	SkyBox m_sky;
 
+	GLuint m_UBO;								// Uniform Buffer Obejct
+
 	Shader m_ModelShader;
 	Model m_packageModel;