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
函数作用
创建和分配一个着色器对象。
函数形式
GLuint glCreateShader(GLenum type)
函数参数
- 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
函数作用
将着色器源代码关联到一个着色器对象上。
函数形式
void glShaderSource(GLuint shader,GLsizei count,const GLchar** string,const GLint* length)
函数参数
- 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对象的源代码
函数形式
void glCompileShader(GLuint shader)
函数参数
- shader:所需编译的着色器ID
函数返回值
无
1.2.4 glCreateProgram
函数作用
创建一个空的着色器程序
函数形式
GLuint glCreateProgram(void)
函数参数
无
函数返回值
如果成功返回一个非零的整数值,这个整数值就是所创建的着色器程序的ID;如果失败返回0。
1.2.5 glAttachShader
函数作用
将Shader对象关联到着色器程序program上
函数形式
void glAttachShader(Gluint program,GLuint shader)
函数参数
- program:着色器程序ID
- shader:shader对象ID
函数返回值
无
1.2.6 glDetachShader
函数作用
移除Shader对象与着色器程序program的关联
函数形式
void glDetachShader(Gluint program,GLuint shader)
函数参数
- program:着色器程序ID
- shader:shader对象ID
函数返回值
无
1.2.7 glLinkProgram
函数作用
在所有需要的Shader关联到着色器程序program之后,链接所有的Shader对象生成一个完整的着色器程序
函数形式
void glLinkProgram(GLuint program)
函数参数
- program:着色器程序ID
函数返回值
无
1.2.8 glUseProgram
函数作用
使用链接好的着色器程序program
函数形式
void glUseProgram(GLuint program)
函数参数
- program:着色器程序ID,如果program为零,则当前所有的使用的着色器程序都会被清除。如果没有绑定任何着色器,那么OpenGL的操作结果是未定义的,虽然操作结果是未定义的,但是不会产生错误。
如果已经启用了一个着色器程序,而它需要关联新的着色器对象,或者解除之前关联的对象,那么我们需要重新对它进行链接。如果链接过程成功,那么新的着色器程序会直接替代之前启用的着色器程序。如果链接失败,那么当前绑定的着色器程序依然是可用的,不会被替代,直到我们重新链接或者使用glUseProgram
指定了新的着色器程序为止。
函数返回值
无
1.2.9 glDeleteShader
函数作用
删除着色器Shader对象
函数形式
void glDeleteShader(GLuint shader)
函数参数
- shader:需要删除的shader对象的ID
函数返回值
无
1.2.10 glDeleteProgram
函数作用
立即删除一个当前没有在任何环境中使用的着色器程序
函数形式
void glDeleteProgram(GLuint program)
函数参数
- program:需要删除的着色器程序的ID
函数返回值
无
1.2.11 glIsShader
函数作用
判断某个Shader对象是否存在
函数形式
GLboolean glIsShader(GLuint shader)
函数参数
- shader:需要判断的shader对象的ID
函数返回值
如果shader
是一个通过glCreateShader()
生成的Shader对象,并且没有被删除,则返回GL_TRUE
;如果shader
为零或者不是Shader对象ID,则返回GL_FALSE
。
1.2.12 glIsProgram
函数作用
判断某个着色器程序是否存在
函数形式
GLboolean glIsProgram(GLuint program)
函数参数
- program:需要判断的着色器对象的ID
函数返回值
如果program
是一个通过glCreateProgram()
生成的着色器对象,并且没有被删除,则返回GL_TRUE
;如果program
为零或者不是着色器对象ID,则返回GL_FALSE
。
1.3 封装的Shader类
在上一节中总结了opengl中几乎所有与shader操作相关的api函数,为了更好的使用上述的api,我们将上述api使用面向对象的思想封装为Shader类。
Shader.h
#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
Shader.cpp
#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;
}
}
}
}
本文作者:StubbornHuang
版权声明:本文为站长原创文章,如果转载请注明原文链接!
原文标题:Modern OpenGL – 与着色器Shader相关的API函数总结以及如何创建、编译与使用Shader
原文链接:https://www.stubbornhuang.com/2158/
发布于:2022年06月08日 20:25:38
修改于:2023年06月26日 20:06:40
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
评论
50