Jelajahi Sumber

feat: 添加 Shader 抽象封装类

nicetry12138 1 tahun lalu
induk
melakukan
01b41d21d1

+ 48 - 1
图形学/OpenGL学习/README.md

@@ -929,6 +929,8 @@ glBindVertexArray(vao);
 
 ## 简单封装抽象类
 
+### 抽象顶点缓冲和索引缓冲
+
 对于一个复杂的模型,会有一个顶点缓冲区,包括模型的每个顶点;可能会有多个索引缓冲区来绘制飞船的部分,因为不同部分可能会是不同材质,比如飞船的舱门可能是金属和玻璃两种材质
 
 ```cpp
@@ -976,6 +978,8 @@ public:
 
 然后替换掉 `Application.cpp` 中对应的部分,就算初步完成了顶点、索引缓冲区的抽象
 
+### 抽象顶点数组
+
 接下来就是抽象顶点数组 `VAO` ,一个顶点数组需要做的就是将一个顶点缓冲区与某种布局绑定在一起
 
 因为一个 `VertexBuffer` 的 `data` 数组中的一个元素可能存在多种布局 (比如 `RGBA` 是 4 个 `unsigned int`、 `Position` 是 3 个 `float`),所以需要一个布局数组来表示这个关系
@@ -1068,4 +1072,47 @@ void VertexArray::AddBuffer(const VertexBuffer& vb, const VertexBufferLayout& la
 }
 ```
 
-> offset 就是一个属性 (例:RGBA) 的大小,通过 offset 计算属性的地址偏移
+> offset 就是一个属性 (例:RGBA) 的大小,通过 offset 计算属性的地址偏移
+
+### 抽象 Shader
+
+抽象 Shader 目前只用做三件事情:
+
+1. 提供文件路径或者源码,将其编译成着色器
+2. 绑定和解绑着色器
+3. 为着色器设置所有不同的 `uniform`
+
+其实就是把之前写过的 `CreateShaderWithFile` 等一系列函数封装到同一个类中
+
+```cpp
+class Shader
+{
+private:
+	std::string m_VertexFilePath;
+	std::string m_FragmentFilePath;
+	GLuint m_ShaderId;
+
+	// 缓存 uniform 的 Location
+	std::unordered_map<std::string, GLint> m_Locations;
+
+public:
+	Shader(const std::string& vertexFile, const std::string& fragmentFile);
+	~Shader();
+
+	void Bind() const;
+	void Unbind() const;
+
+	void SetUniform4f(const std::string& name, float v0, float v1, float v2, float v3);
+
+private:
+	GLuint GetUniformLocation(const std::string& name);
+	GLuint CreateShaderWithFile();
+	GLuint CreateShader(const std::string& vertexSource, const std::string& fragmentSource);
+	GLuint CompileShader(const std::string& source, GLenum inType);
+};
+```
+
+这里使用 `m_Locations` 来缓存 `uniform` 的 `Location`,防止重复寻找
+
+> 后续可以扩展 `SetUniform` 一系列函数,比如 `1f`、`2f`、`3f` 等
+

+ 2 - 0
图形学/OpenGL学习/src/OpenGLStudy/OpenGLStudy/OpenGLStudy.vcxproj

@@ -136,6 +136,7 @@
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClCompile Include="src\Shader.cpp" />
     <ClCompile Include="src\IndexBuffer.cpp" />
     <ClCompile Include="src\Renderer.cpp" />
     <ClCompile Include="src\Application.cpp" />
@@ -148,6 +149,7 @@
     <None Include="src\Vertex.vert" />
   </ItemGroup>
   <ItemGroup>
+    <ClInclude Include="src\Shader.h" />
     <ClInclude Include="src\IndexBuffer.h" />
     <ClInclude Include="src\Renderer.h" />
     <ClInclude Include="src\VertexBuffer.h" />

+ 8 - 83
图形学/OpenGL学习/src/OpenGLStudy/OpenGLStudy/src/Application.cpp

@@ -8,70 +8,7 @@
 #include "IndexBuffer.h"
 #include "VertexArray.h"
 #include "VertexBufferLayout.h"
-
-static GLuint CompiledShader(const std::string& source, GLenum inType) {
-	GLuint id = glCreateShader(inType);
-	const char* src = source.c_str();
-	glShaderSource(id, 1, &src, nullptr);
-	glCompileShader(id);
-
-	// Shader 错误处理
-	GLint result;
-	glGetShaderiv(id, GL_COMPILE_STATUS, &result);
-	if (result == GL_FALSE) {
-		int length;
-		glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
-		// alloca 在栈上申请内存,不需要 free ,在作用域结束后自动释放
-		char* msg = (char*)alloca(length * sizeof(char));
-		glGetShaderInfoLog(id, length, &length, msg);
-		std::cout << "Shader Compile " << (inType == GL_VERTEX_SHADER ? "vertex shader" : "fragment shader") << " Fail" << std::endl;
-		std::cout << msg << std::endl;
-		
-		glDeleteShader(id);
-		return GL_ZERO;
-	}
-
-	return id;
-}
-
-static unsigned int CreateShader(const std::string& vertexShader, const std::string& fragmentShader) {
-	GLuint program = glCreateProgram();
-	GLuint vs = CompiledShader(vertexShader, GL_VERTEX_SHADER);
-	GLuint fs = CompiledShader(fragmentShader, GL_FRAGMENT_SHADER);
-
-	glAttachShader(program, vs);	// 绑定顶点着色器
-	glAttachShader(program, fs);	// 绑定片段着色器
-	glLinkProgram(program);			// 链接程序,将所有着色器合并为一个可执行的程序
-	glValidateProgram(program);		// 验证程序对象是否可以在当前的 OpenGL 状态下执行
-
-	glDeleteShader(fs);				// 删除着色器对象 因为一旦着色器被链接到程序对象,着色器的代码已经被链接到程序中,所以可以安全地删除着色器对象
-	glDeleteShader(vs);
-
-	return program;
-}
-
-static GLuint CreateShaderWithFile(const std::string& vertexShaderFilePath, const std::string& fragmentShaderFilePath) {
-	std::ifstream ifs;
-	ifs.open(vertexShaderFilePath, std::ios::in);
-	if (!ifs.is_open()) {
-		std::cout << "Compile Shader Fail: Can't Open File" << std::endl;
-		return GL_ZERO;
-	}
-
-	std::string vertextShaderSrouce((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
-	ifs.close();
-
-	ifs.open(fragmentShaderFilePath, std::ios::in);
-	if (!ifs.is_open()) {
-		std::cout << "Compile Shader Fail: Can't Open File" << std::endl;
-		return GL_ZERO;
-	}
-
-	std::string fragmentShaderSource((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
-	ifs.close();
-
-	return CreateShader(vertextShaderSrouce, fragmentShaderSource);
-}
+#include "Shader.h"
 
 int main(void)
 {
@@ -127,19 +64,13 @@ int main(void)
 
 		IndexBuffer ibo(indeices, 6);
 
-		GLuint shader = CreateShaderWithFile("src/Vertex.vert", "src/Fragment.frag");
-		glUseProgram(shader);
-
-		GLint location = -1;
-		GL_CALL(location = glGetUniformLocation(shader, "u_Color"));
-		GL_CALL(glUniform4f(location, 1, 1, 0, 0));
+		auto shader = Shader("src/Vertex.vert", "src/Fragment.frag");
 
 		// 清除所有绑定关系
-		glBindVertexArray(0);
-		glUseProgram(0);
-		glBindBuffer(GL_ARRAY_BUFFER, 0);
-		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
-
+		va.Unbind();
+		shader.Unbind();
+		vb.UnBind();
+		ibo.Unbind();
 
 		GLfloat r = 0.0f;
 		GLfloat increment = 0.05f;
@@ -149,17 +80,12 @@ int main(void)
 			/* Render here */
 			glClear(GL_COLOR_BUFFER_BIT);
 
-			//glDrawArrays(GL_TRIANGLES, 0, 6);
-			//GL_CLEAR_ERROR;
-			//glDrawElements(GL_TRIANGLES, 6, GL_UNIFORM, 0);
-			//GL_CHECK_ERROR; 
-
 			va.Bind();
 			ibo.Bind();
 
 			r += increment;
-			GL_CALL(glUseProgram(shader));
-			GL_CALL(glUniform4f(location, r, .5f, .5f, 1.0f));
+			shader.Bind();
+			shader.SetUniform4f("u_Color", r, .5f, .5f, 1.0f);
 
 			if (r > 1.0f || r < 0.0f) {
 				increment *= -1;
@@ -173,7 +99,6 @@ int main(void)
 			/* Poll for and process events */
 			glfwPollEvents();
 		}
-		glDeleteProgram(shader);
 	}
 	glfwTerminate();
 	return 0;

+ 114 - 0
图形学/OpenGL学习/src/OpenGLStudy/OpenGLStudy/src/Shader.cpp

@@ -0,0 +1,114 @@
+#include "Shader.h"
+#include <fstream>
+#include <iostream>
+
+Shader::Shader(const std::string& vertexFile, const std::string& fragmentFile)
+{
+	m_VertexFilePath = vertexFile;
+	m_FragmentFilePath = fragmentFile;
+
+	m_ShaderId = CreateShaderWithFile();
+}
+
+Shader::~Shader()
+{
+	glDeleteProgram(m_ShaderId);
+}
+
+void Shader::Bind() const
+{
+	glUseProgram(m_ShaderId);
+}
+
+void Shader::Unbind() const
+{
+	glUseProgram(0);
+}
+
+void Shader::SetUniform4f(const std::string& name, float v0, float v1, float v2, float v3)
+{
+	GLint location = GetUniformLocation(name);
+	GL_CALL(glUniform4f(location, v0, v1, v2, v3));
+}
+
+GLuint Shader::GetUniformLocation(const std::string& name)
+{
+	if (m_Locations.find(name) != m_Locations.end()) {
+		return m_Locations[name];
+	}
+
+	GLint location = -1;
+	GL_CALL(location = glGetUniformLocation(m_ShaderId, "u_Color"));
+	if (location == -1) {
+		std::cout << "Warning: uniform " << name << " doesn't exist" << std::endl;
+	}
+
+	m_Locations[name] = location;	// 如果没找到 会一直找不到 所以不用 else 判断
+	return location;
+}
+
+GLuint Shader::CreateShaderWithFile() {
+	std::ifstream ifs;
+	ifs.open(m_VertexFilePath, std::ios::in);
+	if (!ifs.is_open()) {
+		std::cout << "Compile Shader Fail: Can't Open File" << std::endl;
+		return GL_ZERO;
+	}
+
+	std::string vertextShaderSrouce((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
+	ifs.close();
+
+	ifs.open(m_FragmentFilePath, std::ios::in);
+	if (!ifs.is_open()) {
+		std::cout << "Compile Shader Fail: Can't Open File" << std::endl;
+		return GL_ZERO;
+	}
+
+	std::string fragmentShaderSource((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
+	ifs.close();
+
+	return CreateShader(vertextShaderSrouce, fragmentShaderSource);
+}
+
+GLuint Shader::CreateShader(const std::string& vertexSource, const std::string& fragmentSource)
+{
+	GLuint program = glCreateProgram();
+	GLuint vs = CompileShader(vertexSource, GL_VERTEX_SHADER);
+	GLuint fs = CompileShader(fragmentSource, GL_FRAGMENT_SHADER);
+
+	glAttachShader(program, vs);	// 绑定顶点着色器
+	glAttachShader(program, fs);	// 绑定片段着色器
+	glLinkProgram(program);			// 链接程序,将所有着色器合并为一个可执行的程序
+	glValidateProgram(program);		// 验证程序对象是否可以在当前的 OpenGL 状态下执行
+
+	glDeleteShader(fs);				// 删除着色器对象 因为一旦着色器被链接到程序对象,着色器的代码已经被链接到程序中,所以可以安全地删除着色器对象
+	glDeleteShader(vs);
+
+	return program;
+}
+
+GLuint Shader::CompileShader(const std::string& source, GLenum inType)
+{
+	GLuint id = glCreateShader(inType);
+	const char* src = source.c_str();
+	glShaderSource(id, 1, &src, nullptr);
+	glCompileShader(id);
+
+	// Shader 错误处理
+	GLint result;
+	glGetShaderiv(id, GL_COMPILE_STATUS, &result);
+	if (result == GL_FALSE) {
+		int length;
+		glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
+		// alloca 在栈上申请内存,不需要 free ,在作用域结束后自动释放
+		char* msg = (char*)alloca(length * sizeof(char));
+		glGetShaderInfoLog(id, length, &length, msg);
+		std::cout << "Shader Compile " << (inType == GL_VERTEX_SHADER ? "vertex shader" : "fragment shader") << " Fail" << std::endl;
+		std::cout << msg << std::endl;
+
+		glDeleteShader(id);
+		return GL_ZERO;
+	}
+
+	return id;
+}

+ 31 - 0
图形学/OpenGL学习/src/OpenGLStudy/OpenGLStudy/src/Shader.h

@@ -0,0 +1,31 @@
+#pragma once
+#include "Renderer.h"
+#include <string>
+#include <unordered_map>
+
+class Shader
+{
+private:
+	std::string m_VertexFilePath;
+	std::string m_FragmentFilePath;
+	GLuint m_ShaderId;
+
+	// »º´æ uniform µÄ Location
+	std::unordered_map<std::string, GLint> m_Locations;
+
+public:
+	Shader(const std::string& vertexFile, const std::string& fragmentFile);
+	~Shader();
+
+	void Bind() const;
+	void Unbind() const;
+
+	void SetUniform4f(const std::string& name, float v0, float v1, float v2, float v3);
+
+private:
+	GLuint GetUniformLocation(const std::string& name);
+	GLuint CreateShaderWithFile();
+	GLuint CreateShader(const std::string& vertexSource, const std::string& fragmentSource);
+	GLuint CompileShader(const std::string& source, GLenum inType);
+};
+