1GLSL
着色器是使用一种叫做GLSL的类c语言来编写的,GLSL是为图形计算量身定制的,包含一些针对向量和矩阵操作的有用特性
开头是声明版本,接着是输入和输出变量,uniform和main函数。每个着色器的入口点都是main函数,在这个函数中我们处理所有的输入变量,并将结果输出到输出变量中。
一个经典的着色器有着下面的结构
#version version_number
in type in_varible_name;
in type in_varible_name;
out type out_varible_name;
uniform type uniform_name;
int main()
{
//处理输入并进行一些图形操作
...
//输出处理的结果
out_varible_name=weird_stuff_we_processed;
}
顶点着色器中每个输入变量也称为顶点属性,我们能声明的顶点属性是由上线的,一般由硬件来决定。OpenGL中保证一般有16个包含4分量的顶点属性可用,当然可以用GL_MAX_VERTEX_ATTRIBS来获取具体的上限。
2Uniform
数据类型,GLSL语言有数据类型来指定变量的种类,GLSL中包含了c等其它语言的大部分默认基础数据类型,GLSL也有两种容器类型,分别是向量和矩阵。
而Uniform是一种由cpu向GPU发送数据的方式,但Uniform和顶点属性有些不同,首先,uniform是全局变量,代表他在每个着色器里都是独一无二的,而且可以被任何着色器在任何阶段访问,第二,无论你把uniform设置成什么值,他都会一直保持他的数据,知道它们被重置或者更新。
我们可以在着色器中添加Uniform关键字类型和变量名来声明一个GLSL的uniform。
#version 330 core
out vec4 fragColor ;
uniform vec4 ourColor;//在OpenGL代码中设置此变量
void main()
{
fragColor = ourColor;
}
在openGL代码中设置uniform变量
float timeValue = glfwGetTime();//获取当前时间的秒数
float greenValue =(sin(timeValue)/2.0f)+0.5f;
int vertexColorLocation =glGetUniformLocation(shaderProgram,"ourColor");
glUseProgram(shaderProgram);
glUniform4f(vertexColorLocation,0.0f,greenValue,0.0f,1.0f);
接下来的话颜色就可以跟着时间变化了
//渲染循环
while(!glfwWindowShouldClose(window))
{
processInput(window);
glClearColor(0.2f,0.3f,0.2f,1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//激活着色器
glUseProgram(shaderProgram);
float timeValue =glfwGetTime();
float greenValue =(sin(timeValue)/2.0f)+0.5f;
int vertexShaderLocation =glGetUniformLocation(ShaderProgram,"ourColor");
glUniform4f(vertexShaderLocation,0.0f,greenValue,0.0f,1.0f);
...//画你想要的图形
}
uniform对于设置一个在迭代中会改变的属性来说是一个强有力的工具,也是在程序和着色器数据之间交互的一个很好的工具。
3更多属性
如果我们打算把颜色加到顶点数据中去的话,我们把颜色数据加入到vertices数组中,这就要在属性配置上去调整一下。
//顶点数据
float vertices[] = {
// 位置 // 颜色
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 右下
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 左下
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // 顶部
};
由于现在有了更多的数据要发送到顶点着色器中去,我们要调整一下顶点着色器
#version 330 core
layout(location =0) in vec3 aPos;
layout(location =1) in vec3 aColor;
out vec3 ourColor;
void main()
{
gl_position =vec4(aPos,1.0f);
ourColor = aColor;
}
由于不用uniform来传递数据了,我们还得修改一下片段着色器
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
void main()
{
FragColor =vec4(ourColor,1.0f);
}
由于我们添加了另一个顶点属性,并且更新了VBO的内存,我们必须重新配置顶点属性指针。
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,6*sizeof(float),(void*)0);
glEnableVertexAttriArray(0);
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,6*sizeof(float),(void*)(3*sizeof(float)));
4着色器类
编写,管理和编译着色器是一件十分麻烦的事情,我们可以将这些方法封装成一个类,来使我们的变成变得简洁一点。
#ifndef SHADER_H
#define SHADER_H
#include<glad/glad.h>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
class Shader
{
public:
unsigned int ID;
Shader(const GLchar *vertexPath, const GLchar *fragment)
{
std::string vertexCode;
std::string fragmentCode;
std::ifstream vShaderFile;
std::ifstream fShaderFile;
//保证文件正确的抛出异常
vShaderFile.expection(std::ifstream::FailBit|std::ifstream::BadBit);
fShaderFile.expection(std::ifstream::FailBit|std::ifstream::BadBit);
try
{
vShaderFile.open(vertexPath);
fShaderFile.open(fragmentPath);
std::stringstream vShaderStream, fShaderStream;
vShaderStream<<vShaderFile.rdbuf();
fShaderStream<<fShaderFile.rdbuf();
//关闭文件处理器
vShaderFile.close();
fShaderFile.close();
//转换数据流到string
vertexCode =vShaderStream.str();
fragmentCode =fShaderStream.str();
}
catch(std::ifstream::failure e)
{
std::cout<<"ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ"<<std::endl;
}
const char *vShaderCode = vertexCode.c_str();
const char *fShaderCode = fragmentCode.c_str();
//编译着色器
unsigned int vertex,fragment;
vertex = glcreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex,1,&vShaderCode,NULL);
glcompileShader(vertex);
checkCompileError(vertex,"VERTEX");
fragment = glcreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment,1,&fShaderCode,NULL);
glcompileShader(fragment);
checkCompileError(fragment,"FRAGMENT");
ID =glCreateProgram();
glAttachShader(ID,vertex);
glAttachShader(ID,fragment);
glLinkProgram(ID);
checkCompileError(ID,"Program");
//删除着色器对象
glDeleteShader(vertex);
glDeleteShader(fragment);
}
void use()
{
glUseProgram(ID);
}
void setBool(const std::string &name, bool value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
}
void setInt(const std::string &name, int value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
}
void setFloat(const std::string &name, float value) const
{
glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
}
private:
void checkCompileErrors(unsigned int shader, std::string type)
{
int success;
char infoLog[1024];
if (type != "PROGRAM")
{
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(shader, 1024, NULL, infoLog);
std::cout << "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);
std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
}
}