什么是Shader?
shader是运行在GPU上的程序。我们前面已经在GPU上创建了buffer并且传输了数据,那么shader就是利用这些数据来展示出图形效果
Vertex Shader和Fragment Shader
简化一下pipeline的话就是
传数据->Vertex Shader -> Fragment Shader ->输出
简单来说,Vertex Shader确定点的位置,Fragment Shader填充像素点。
以三角形为例,Vertex Shader调用3次,而Fragment Shader调用次数和三角形内像素的个数有关,多少个多少次
OpenGL中创建Shader
- 创建Shader并分配ID,glCreateShader
- 设置Shader内容, glShaderSource
- 编译Shader, glCompileShader
- 创建Program,glCreateProgram
- 附加Shader,glAttachShader
- 链接Shader, glLinkProgram
- 删除Shader,glDeleteShader
- 使用Program, glUseProgram
- 删除Program,glDeleteProgram
static GLuint CompileShader(unsigned int type, const std::string& source)
{
GLuint id = glCreateShader(type);
const char* src = source.c_str();
glShaderSource(id, 1, &src, nullptr);
glCompileShader(id);
int result;
glGetShaderiv(id, GL_COMPILE_STATUS, &result);
if (result == GL_FALSE) {
int length;
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
char* message = (char*)_alloca(length * sizeof(char));
glGetShaderInfoLog(id, length, &length, message);
std::cout << "failed" << std::endl;
std::cout << message << std::endl;
glDeleteShader(id);
return 0;
}
return id;
}
static GLuint CreateShader(const std::string& vertexShader, const std::string& fragmentShader)
{
GLuint program = glCreateProgram();
GLuint vs = CompileShader(GL_VERTEX_SHADER, vertexShader);
GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fragmentShader);
glAttachShader(program, vs);
glAttachShader(program, fs);
glLinkProgram(program);
glValidateProgram(program);
glDeleteShader(vs);
glDeleteShader(fs);
return program;
}
GLuint shader = CreateShader(vertexShader, fragmentShader);
glUseProgram(shader);
Shader代码
#shader vertex
#version 330 core
layout(location = 0)in vec4 position;
void main()
{
gl_Position = position;
};
#shader fragment
#version 330 core
layout(location = 0) out vec4 color;
void main()
{
color = vec4(1.0, 0.0, 0.0, 1.0);
};
从文件获取shader代码
struct ShaderProgramSource
{
std::string vertexSource;
std::string fragmentSource;
};
static ShaderProgramSource ParseShader(const std::string& filepath)
{
std::ifstream stream(filepath);
enum class ShaderType
{
NONE = -1, VERTEX = 0, FRAGMENT = 1
};
std::string line;
std::stringstream ss[2];
ShaderType type = ShaderType::NONE;
while (getline(stream, line))
{
if (line.find("#shader") != std::string::npos) {
if (line.find("vertex") != std::string::npos) {
type = ShaderType::VERTEX;
}
else if (line.find("fragment") != std::string::npos) {
type = ShaderType::FRAGMENT;
}
}
else
{
ss[(int)type] << line << '\n';
}
}
return { ss[0].str(), ss[1].str() };
}
ShaderProgramSource source = ParseShader("./res/shaders/Basic.shader");
GLuint shader = CreateShader(source.vertexSource, source.fragmentSource);
glUseProgram(shader);