Modern OpenGL – 与着色器Shader相关的API函数总结以及如何创建、编译与使用Shader
1 OpenGL着色器Shader的创建、编译与使用
1.1 使用Shader的大致过程
在OpenGL中,如果在程序中需要使用Shader为基础图元进行着色,首先需要创建一个Shader对象,然后创建一个着色器程序关联当前Shader,然后再进行使用。
对于每一个Shader对象,都需要:
- 创建一个Shader对象
- 编译Shader代码
- 验证Shader代码是否编译成功
在创建完Shader对象之后,如果我们需要使用这个Shader对象对图元进行操作,都需要:
- 创建一个着色器程序
- 将之前创建的Shader对象关联到当前的着色器程序
- 链接着色器程序
- 判断着色器程序链接过程是否完成
- 使用着色器程序处理顶点和片段
1.2 相关的OpenGL API函数
1.2.1 glCreateShader
函数作用
创建和分配一个着色器对象。
函数形式
<code class="language-glsl line-numbers">GLuint glCreateShader(GLenum type) </code>
函数参数
- type:我们可以通过
glCreateShader
函数创建多种类型的Shader对象,比如顶点着色器、片段着色器、细分着色器、几何着色器等等。所以type
可以是以下枚举GL_VERTEX_SHADER
、GL_FRAGMENT_SHADER
、GL_TESS_CONTROL_SHADER
、GL_TESS_EVALUATION_SHADER
、GL_GEOMETRY_SHADER
函数返回值
如果成功返回一个非零的整数值,这个整数值就是所创建的Shader 对象的ID;如果失败返回0。
1.2.2 glShaderSource
函数作用
将着色器源代码关联到一个着色器对象上。
函数形式
<code class="language-glsl line-numbers">void glShaderSource(GLuint shader,GLsizei count,const GLchar** string,const GLint* length) </code>
函数参数
- shader:所需关联的着色器对象ID
- count:源代码行数
- string:由count行GLchar类型字符串组成的数组,用于表示着色器的源代码数据,string可以是NULL结尾的也可以不是NULL结尾的
- length:length的取值可以有以下几种。第一种,如果length是NULL,则假设string给出的每行字符串都是NULL结尾的,否则length中必须有count个元素,它们分别表示string中对应行的长度。第二种,如果length数组中的某一个值是一个整数,那么它表示对应的字符串中的字符数。如果某个值是负数,那么string中对应行假设为NULL结尾。
函数返回值
无
1.2.3 glCompileShader
函数作用
编译指定shader对象的源代码
函数形式
<code class="language-glsl line-numbers">void glCompileShader(GLuint shader) </code>
函数参数
- shader:所需编译的着色器ID
函数返回值
无
1.2.4 glCreateProgram
函数作用
创建一个空的着色器程序
函数形式
<code class="language-gls line-numbers">GLuint glCreateProgram(void) </code>
函数参数
无
函数返回值
如果成功返回一个非零的整数值,这个整数值就是所创建的着色器程序的ID;如果失败返回0。
1.2.5 glAttachShader
函数作用
将Shader对象关联到着色器程序program上
函数形式
<code class="language-glsl line-numbers">void glAttachShader(Gluint program,GLuint shader) </code>
函数参数
- program:着色器程序ID
- shader:shader对象ID
函数返回值
无
1.2.6 glDetachShader
函数作用
移除Shader对象与着色器程序program的关联
函数形式
<code class="language-glsl line-numbers">void glDetachShader(Gluint program,GLuint shader) </code>
函数参数
- program:着色器程序ID
- shader:shader对象ID
函数返回值
无
1.2.7 glLinkProgram
函数作用
在所有需要的Shader关联到着色器程序program之后,链接所有的Shader对象生成一个完整的着色器程序
函数形式
<code class="language-glsl line-numbers">void glLinkProgram(GLuint program) </code>
函数参数
- program:着色器程序ID
函数返回值
无
1.2.8 glUseProgram
函数作用
使用链接好的着色器程序program
函数形式
<code class="language-glsl line-numbers">void glUseProgram(GLuint program) </code>
函数参数
- program:着色器程序ID,如果program为零,则当前所有的使用的着色器程序都会被清除。如果没有绑定任何着色器,那么OpenGL的操作结果是未定义的,虽然操作结果是未定义的,但是不会产生错误。
如果已经启用了一个着色器程序,而它需要关联新的着色器对象,或者解除之前关联的对象,那么我们需要重新对它进行链接。如果链接过程成功,那么新的着色器程序会直接替代之前启用的着色器程序。如果链接失败,那么当前绑定的着色器程序依然是可用的,不会被替代,直到我们重新链接或者使用glUseProgram
指定了新的着色器程序为止。
函数返回值
无
1.2.9 glDeleteShader
函数作用
删除着色器Shader对象
函数形式
<code class="language-glsl line-numbers">void glDeleteShader(GLuint shader) </code>
函数参数
- shader:需要删除的shader对象的ID
函数返回值
无
1.2.10 glDeleteProgram
函数作用
立即删除一个当前没有在任何环境中使用的着色器程序
函数形式
<code class="language-glsl line-numbers">void glDeleteProgram(GLuint program) </code>
函数参数
- program:需要删除的着色器程序的ID
函数返回值
无
1.2.11 glIsShader
函数作用
判断某个Shader对象是否存在
函数形式
<code class="language-glsl line-numbers">GLboolean glIsShader(GLuint shader) </code>
函数参数
- shader:需要判断的shader对象的ID
函数返回值
如果shader
是一个通过glCreateShader()
生成的Shader对象,并且没有被删除,则返回GL_TRUE
;如果shader
为零或者不是Shader对象ID,则返回GL_FALSE
。
1.2.12 glIsProgram
函数作用
判断某个着色器程序是否存在
函数形式
<code class="language-glsl line-numbers">GLboolean glIsProgram(GLuint program) </code>
函数参数
- program:需要判断的着色器对象的ID
函数返回值
如果program
是一个通过glCreateProgram()
生成的着色器对象,并且没有被删除,则返回GL_TRUE
;如果program
为零或者不是着色器对象ID,则返回GL_FALSE
。
1.3 封装的Shader类
在上一节中总结了opengl中几乎所有与shader操作相关的api函数,为了更好的使用上述的api,我们将上述api使用面向对象的思想封装为Shader类。
Shader.h
<code class="language-cpp line-numbers">#ifndef ENGINE_GRAPHICS_BASE_SHADER_H #define ENGINE_GRAPHICS_BASE_SHADER_H #include <string> #include <vector> #include "EngineUtils/PancakeEngineProjectHeader.h" namespace PancakeEngine { class Shader { public: Shader(); Shader( const std::string& shader_name, const std::string& vertex_shader_path, const std::string& fragment_shader_path ); Shader( const std::string& shader_name, const std::string& vertex_shader_path, const std::string& fragment_shader_path, const std::string& geometry_shader_path ); virtual~Shader(); public: void Use(); static void UnUse(); unsigned int GetShaderIndex(); std::string GetShaderName(); void SetBool(const std::string& name, bool value) const; void SetInt(const std::string& name, int value) const; void SetFloat(const std::string& name, float value) const; void SetVec2(const std::string& name, const glm::vec2& value) const; void SetVec2(const std::string& name, float x, float y) const; void SetVec3(const std::string& name, const glm::vec3& value) const; void SetVec3(const std::string& name, float x, float y, float z) const; void SetVec4(const std::string& name, const glm::vec4& value) const; void SetVec4(const std::string& name, float x, float y, float z, float w); void SetMat2(const std::string& name, const glm::mat2& mat) const; void SetMat3(const std::string& name, const glm::mat3& mat) const; void SetMat4(const std::string& name, const glm::mat4& mat) const; void SetMat4Vector(const std::string& name, const std::vector<glm::mat4>& mat4Vector); void SetFloatVector(const std::string& name, const std::vector<float>& floatArray, unsigned int length)const; private: std::string ReadShaderFromFile(const std::string& path); void CheckCompileErrors(unsigned int shader, std::string type); private: unsigned int m_ShaderId; std::string m_ShaderName; }; } #endif // !ENGINE_GRAPHICS_BASE_SHADER_H </code>
Shader.cpp
<code class="language-cpp line-numbers">#include "Shader.h" #include <fstream> #include <sstream> #include "EngineUtils/PancakeEngineProjectHeader.h" namespace PancakeEngine { Shader::Shader() :m_ShaderId(-1) { } Shader::Shader( const std::string& shader_name, const std::string& vertex_shader_path, const std::string& fragment_shader_path) :m_ShaderId(-1) { // 1 从文件中读取顶点着色器和片段着色器源码 std::string vertex_shader_code; std::string fragment_shader_code; vertex_shader_code = ReadShaderFromFile(vertex_shader_path); fragment_shader_code = ReadShaderFromFile(fragment_shader_path); const char* vertext_shader_code_char = vertex_shader_code.c_str(); const char* fragment_shader_code_char = fragment_shader_code.c_str(); // 2 编译shader unsigned int vertex_shader_id; unsigned int fragment_shader_id; vertex_shader_id = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertex_shader_id, 1, &vertext_shader_code_char, nullptr); glCompileShader(vertex_shader_id); CheckCompileErrors(vertex_shader_id, "VERTEX"); fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragment_shader_id, 1, &fragment_shader_code_char, nullptr); glCompileShader(fragment_shader_id); CheckCompileErrors(fragment_shader_id, "FRAGMENT"); // 3 创建着色器程序 m_ShaderId = glCreateProgram(); glAttachShader(m_ShaderId, vertex_shader_id); glAttachShader(m_ShaderId, fragment_shader_id); glLinkProgram(m_ShaderId); CheckCompileErrors(m_ShaderId, "PROGRAM"); // 4 删除着色器 glDeleteShader(vertex_shader_id); glDeleteShader(fragment_shader_id); } Shader::Shader( const std::string& shader_name, const std::string& vertex_shader_path, const std::string& fragment_shader_path, const std::string& geometry_shader_path) { // 1 从文件中读取顶点着色器和片段着色器源码 std::string vertex_shader_code; vertex_shader_code = ReadShaderFromFile(vertex_shader_path); const char* vertext_shader_code_char = vertex_shader_code.c_str(); std::string fragment_shader_code; fragment_shader_code = ReadShaderFromFile(fragment_shader_path); const char* fragment_shader_code_char = fragment_shader_code.c_str(); std::string geometry_shader_code; geometry_shader_code = ReadShaderFromFile(geometry_shader_path); const char* geometry_shader_code_char = geometry_shader_code.c_str(); // 2 编译shader unsigned int vertex_shader_id; unsigned int fragment_shader_id; unsigned int geometry_shader_id; vertex_shader_id = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertex_shader_id, 1, &vertext_shader_code_char, nullptr); glCompileShader(vertex_shader_id); CheckCompileErrors(vertex_shader_id, "VERTEX"); fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragment_shader_id, 1, &fragment_shader_code_char, nullptr); glCompileShader(fragment_shader_id); CheckCompileErrors(fragment_shader_id, "FRAGMENT"); geometry_shader_id = glCreateShader(GL_GEOMETRY_SHADER); glShaderSource(geometry_shader_id, 1, &geometry_shader_code_char, nullptr); glCompileShader(geometry_shader_id); CheckCompileErrors(geometry_shader_id, "GEOMETRY"); // 3 创建着色器程序 m_ShaderId = glCreateProgram(); glAttachShader(m_ShaderId, vertex_shader_id); glAttachShader(m_ShaderId, fragment_shader_id); glAttachShader(m_ShaderId, geometry_shader_id); glLinkProgram(m_ShaderId); CheckCompileErrors(m_ShaderId, "PROGRAM"); // 4 删除着色器 glDeleteShader(vertex_shader_id); glDeleteShader(fragment_shader_id); glDeleteShader(geometry_shader_id); } Shader::~Shader() { } void Shader::Use() { glUseProgram(m_ShaderId); } void Shader::UnUse() { glUseProgram(0); } unsigned int Shader::GetShaderIndex() { return m_ShaderId; } std::string Shader::GetShaderName() { return m_ShaderName; } void Shader::SetBool(const std::string& name, bool value) const { glUniform1i(glGetUniformLocation(m_ShaderId, name.c_str()), static_cast<int>(value)); } void Shader::SetInt(const std::string& name, int value) const { glUniform1i(glGetUniformLocation(m_ShaderId, name.c_str()), value); } void Shader::SetFloat(const std::string& name, float value) const { glUniform1f(glGetUniformLocation(m_ShaderId, name.c_str()), value); } void Shader::SetVec2(const std::string& name, const glm::vec2& value) const { glUniform2fv(glGetUniformLocation(m_ShaderId, name.c_str()), 1, &value[0]); } void Shader::SetVec2(const std::string& name, float x, float y) const { glUniform2f(glGetUniformLocation(m_ShaderId, name.c_str()), x, y); } void Shader::SetVec3(const std::string& name, const glm::vec3& value) const { glUniform3fv(glGetUniformLocation(m_ShaderId, name.c_str()), 1, &value[0]); } void Shader::SetVec3(const std::string& name, float x, float y, float z) const { glUniform3f(glGetUniformLocation(m_ShaderId, name.c_str()), x, y, z); } void Shader::SetVec4(const std::string& name, const glm::vec4& value) const { glUniform4fv(glGetUniformLocation(m_ShaderId, name.c_str()), 1, &value[0]); } void Shader::SetVec4(const std::string& name, float x, float y, float z, float w) { glUniform4f(glGetUniformLocation(m_ShaderId, name.c_str()), x, y, z, w); } void Shader::SetMat2(const std::string& name, const glm::mat2& mat) const { glUniformMatrix2fv(glGetUniformLocation(m_ShaderId, name.c_str()), 1, GL_FALSE, &mat[0][0]); } void Shader::SetMat3(const std::string& name, const glm::mat3& mat) const { glUniformMatrix3fv(glGetUniformLocation(m_ShaderId, name.c_str()), 1, GL_FALSE, &mat[0][0]); } void Shader::SetMat4(const std::string& name, const glm::mat4& mat) const { glUniformMatrix4fv(glGetUniformLocation(m_ShaderId, name.c_str()), 1, GL_FALSE, &mat[0][0]); } void Shader::SetMat4Vector(const std::string& name, const std::vector<glm::mat4>& mat4Vector) { glUniformMatrix4fv(glGetUniformLocation(m_ShaderId, name.c_str()), mat4Vector.size(), GL_FALSE, glm::value_ptr(mat4Vector[0])); } void Shader::SetFloatVector(const std::string& name, const std::vector<float>& floatArray, unsigned int length) const { glUniform4fv(glGetUniformLocation(m_ShaderId, name.c_str()), length, floatArray.data()); } std::string Shader::ReadShaderFromFile(const std::string& path) { std::string code; std::ifstream file; // 读取着色器文件 file.exceptions(std::ifstream::failbit | std::ifstream::badbit); try { file.open(path); if (!file.is_open()) { LOG(ERROR) << "Shader: Read shader file" << path << "is failed" << std::endl; return code; } std::stringstream stream; stream << file.rdbuf(); file.close(); code = stream.str(); } catch (std::ifstream::failure e) { LOG(ERROR) << "Shader: Read shader file" << path << "is failed" << std::endl; } return code; } void Shader::CheckCompileErrors(unsigned int shader, std::string type) { GLint success; GLchar infoLog[1024]; if (type != "PROGRAM") { glGetShaderiv(shader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(shader, 1024, NULL, infoLog); LOG(ERROR) << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl; } } else { glGetProgramiv(shader, GL_LINK_STATUS, &success); if (!success) { glGetProgramInfoLog(shader, 1024, NULL, infoLog); LOG(ERROR) << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl; } } } } </code>
本文作者:StubbornHuang
版权声明:本文为站长原创文章,如果转载请注明原文链接!
原文标题:Modern OpenGL – 与着色器Shader相关的API函数总结以及如何创建、编译与使用Shader
原文链接:https://www.stubbornhuang.com/2158/
发布于:2022年06月08日 20:25:38
修改于:2023年06月26日 20:06:40
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
评论
49