1.着色器概念
着色器是运行在GPU上的小程序,这些小程序为图形渲染管线的某个特性部分而运行。着色器是一种把输入转化为输出的程序。着色器也是一种非常独立的程序,因为它们之间不能相互通信,它们之间唯一的沟通只有通过输入和输出
2.着色器的结构
着色器的开头总是要声明版本,接着是输入和输出的变量,uniform和main函数。每个着色器的入口点都是main函数,在main函数中,我们处理所有的输入变量,并且计算输出变量
主要的结构:
#version version_number
//输入变量
in type in_variable_name
in type in_variable_name
//输出变量
out type out_variable_name
uniform type uniform_name
int main()
{
//处理输入并进行一些图形操作
....
//输出处理过的结果到输出变量
out_variable_name = weird_stuff_we_processed
}
3.数据类型
GLSL中包含C等其他语言大部分的默认基础数据类型:int float double uint bool
GLSL包含两种容器类型 Vector 和 Matrix
4.向量(vector)
类型:
vecn:包含n个float分量
bvecn:包含n个bool分量
ivecn:包含n个int分量
uvecn:包含n个unsigned int 分量
dvecn:n个double分量
注:n代表分量的数量
注:①其中某个分量可以通过vec.x这种方式去获取,可以分别使用.x、.y、.z、.w来获取第1、2、3、4个分量,GLSL也可以用rgba,对纹理座标使用stpq访问相同的分量
注:②向量中的一些有趣的东西,叫重组
例如:
vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw;
vec4 otherVec = someVec.xxxx+ anotherVec.yxzy
vec2 vect = vec2(0.5, 0.7);
vec4 result = vec4(vect, 0.0, 0.0);
vec4 otherResult = vec4(result.xyz, 1.0)
5.in(输入)和out(输出)
GLSL定义了in和out设定输入和输出,只要一个输出变量与下一个着色阶段的输入匹配,就会传递下去
顶点着色器接收的是一种特殊形式的输入,顶点着色器从顶点数据中直接接收输入,使用location指定输入变量,顶点着色器需要为它的输入提供额外的layout标识,layout(location = 0)
片段着色器,需要一个vec4颜色输出变量,片段着色器需要生成一个最终输出的颜色,如果没有定义输出颜色,OpenGL会把物体渲染成黑色或者白色
如果从一个着色器向另一个着色器发送数据,必须在发送方着色器中声明一个输出,在接收方着色器中声明一个类似的输入。当类型和名字都一样的时候,OpenGL就会把两个变量链接到一起,它们之间就能发送数据了(这是在链接程序对象时完成的)
例如:
顶点着色器
#version 330 core
layout (location) in vec3 aPos //位置变量的属性位置为0
out vec4 vertexColor; //为片段着色器指定一个颜色输出
void main()
{
gl_Position = vec4(aPos, 1.0);
vertexColor = vec4(0.5f, 0.0, 0.0, 1.0)
}
片段着色器
#version 330 core
out vec4 FragColor;
in vec4 vertexColor;
void main()
{
FragColor = vertexColor
}
可以看到我们在顶点着色器中声明了一个vertexColor变量作为vec4输出,并在片段着色器中声明了一个类似的vertexColor。由于它们名字相同且类型相同,片段着色器中的vertexColor就和顶点着色器中的vertexColor链接了。由于我们在顶点着色器中将颜色设置为深红色,最终的片段也是深红色的。如下图所示:
详细代码见下
#include <GL/glew.h>
#include<gl\glut.h>
#include<iostream>
void MyInit();
void reshape(int w, int h);
void display();
GLuint vboId;
GLuint vaoId;
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(512, 512);
glutCreateWindow("SHADER_RECTANGLE");
glewInit();
MyInit();
glutReshapeFunc(&reshape);
glutDisplayFunc(&display);
glutMainLoop();
return 0;
}
void MyInit()
{
glClearColor(0.0,0.0, 0.0,0.0);
const GLfloat vertices[] = {
-0.5f,-0.5f,0.0f,1.0f,
0.5f,-0.5f,0.0f,1.0f,
0.5f,0.5f,0.0f,1.0f,
-0.5f,0.5f,0.0f,1.0f,
};
//创建VAO对象
glGenVertexArrays(1, &vaoId);
glBindVertexArray(vaoId);
//创建VBO对象
glGenBuffers(1, &vboId);
glBindBuffer(GL_ARRAY_BUFFER, vboId);
//传入VBO数据
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//解除VBO绑定
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void reshape(int w, int h)
{
glViewport(0, 0,(GLsizei)w, (GLsizei)h);
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
const char* vertex_shader =
"#version 330\n"
"in vec3 aPos;"
"out vec4 vertexColor;"
"void main() {"
" gl_Position = vec4(aPos, 1.0);"
"vertexColor =vec4 (0.5, 0.0, 0.0, 1.0);"
"}";
const char* fragment_shader =
"#version 330\n"
"out vec4 FragColor;"
"in vec4 vertexColor;" //跟顶点着色器中的输出变量类型,名字一样
"void main() {"
" FragColor = vertexColor;"
"}";
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertex_shader, NULL);
glCompileShader(vertexShader);
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragment_shader, NULL);
glCompileShader(fragmentShader);
GLuint shader_programme = glCreateProgram();
glAttachShader(shader_programme, fragmentShader);
glAttachShader(shader_programme, vertexShader);
glLinkProgram(shader_programme);
glUseProgram(shader_programme);
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader);
//绑定VBO
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glEnableVertexAttribArray(0);
//解释顶点数据方式
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
//绘制模型
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(0);
glutSwapBuffers();
}
6.Uniform
Uniform是一种从CPU中的应用向GPU中的着色器发送数据的方式,但uniform和顶点属性有些不同。
①uniform是全局的(Global)。全局意味着uniform变量必须在每个着色器程序对象中都是独一无二的,而且它可以被着色器程序的任意着色器在任意阶段访问;
②无论你把uniform值设置成什么,uniform会一直保存它们的数据,直到它们被重置或更新;
我们可以在着色器中添加uniform关键字
#version 330 core
out vec4 FragColor;
uniform vec4 myColor; //在OpenGL程序中设定这个变量
void main()
{
FragColor = myColor;
}
可以看到我们在片段着色器中声明了一个uniform vec4 的myColor,并且把着色器的输出颜色设置为uniform的内容,因为uniform变量是全局变量,我们可以在任何着色器中定义
注:如果声明了一个uniform,但是在GLSL中没用过,编译器会静默移除这个变量,导致编译出的版本并不会包含它。
现在uniform还是空的,我们没有添加数据,步骤:
(1)找到着色器中uniform属性的索引/位置值
(2)得到uniform的索引/位置值后,可以更新值
int vertexColorLocation = glGetUniformLocation(shaderProgram, "myColor");
glUseProgram(shaderProgram)
glUniform4f(vertexColorLocation, 0.5f, 0.5f, 0.0f, 1.0f);
用glGetUniformLocation查询uniform myColor的位置值,如果返回-1就代表没有找到这个位置
用glUniform4f函数设置uniform值
注意:查询uniform地址不要求你之前使用过着色器程序,但是更新一个uniform之前必须先使用程序(调用glUseProgram)
代码见下:
#include <GL/glew.h>
#include<gl\glut.h>
#include<iostream>
void MyInit();
void reshape(int w, int h);
void display();
GLuint vboId;
GLuint vaoId;
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(512, 512);
glutCreateWindow("SHADER_RECTANGLE");
glewInit();
MyInit();
glutReshapeFunc(&reshape);
glutDisplayFunc(&display);
glutMainLoop();
return 0;
}
void MyInit()
{
glClearColor(0.0,0.0, 0.0,0.0);
const GLfloat vertices[] = {
-0.5f,-0.5f,0.0f,1.0f,
0.5f,-0.5f,0.0f,1.0f,
0.5f,0.5f,0.0f,1.0f,
-0.5f,0.5f,0.0f,1.0f,
};
//创建VAO对象
glGenVertexArrays(1, &vaoId);
glBindVertexArray(vaoId);
//创建VBO对象
glGenBuffers(1, &vboId);
glBindBuffer(GL_ARRAY_BUFFER, vboId);
//传入VBO数据
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//解除VBO绑定
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void reshape(int w, int h)
{
glViewport(0, 0,(GLsizei)w, (GLsizei)h);
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
const char* vertex_shader =
"#version 330\n"
"in vec3 aPos;"
"void main() {"
" gl_Position = vec4(aPos, 1.0);"
"}";
const char* fragment_shader =
"#version 330\n"
"out vec4 FragColor;"
"uniform vec4 myColor;" //定义uniform变量
"void main() {"
" FragColor = myColor;"
"}";
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertex_shader, NULL);
glCompileShader(vertexShader);
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragment_shader, NULL);
glCompileShader(fragmentShader);
GLuint shader_programme = glCreateProgram();
glAttachShader(shader_programme, fragmentShader);
glAttachShader(shader_programme, vertexShader);
glLinkProgram(shader_programme);
glUseProgram(shader_programme);
//更新uniform颜色
int vertexColorLocation = glGetUniformLocation(shader_programme, "myColor");
glUniform4f(vertexColorLocation, 0.5f, 0.5f, 0.0f, 1.0f);
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader);
//绑定VBO
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glEnableVertexAttribArray(0);
//解释顶点数据方式
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
//绘制模型
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(0);
glutSwapBuffers();
}
扩展:
我们进行扩展,增加更多的属性,画一个三角形,三个角指定为不同的颜色
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; // 位置变量的属性位置值为 0
layout (location = 1) in vec3 aColor; // 颜色变量的属性位置值为 1
out vec3 ourColor; // 向片段着色器输出一个颜色
void main()
{
gl_Position = vec4(aPos, 1.0);
ourColor = aColor; // 将ourColor设置为我们从顶点数据那里得到的输入颜色
}
片段着色器
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
void main()
{
FragColor = vec4(ourColor, 1.0);
}
使用glVertexAttribPointer函数更新顶点格式
// 位置属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 颜色属性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3* sizeof(float)));
glEnableVertexAttribArray(1);
由于有两个顶点属性,必须要重新计算步长,获得数据队列的下一个属性值,即下一个顶点的属性,必须向右移动6个float,3个位置值,3个颜色值
同样的,我们必须指定一个偏移量,顶点位置属性在前,所以偏移量为0,颜色属性在后,所以偏移量是3个float
效果见下:
代码:
#include <GL/glew.h>
#include<gl\glut.h>
#include<iostream>
void MyInit();
void reshape(int w, int h);
void display();
GLuint vboId;
GLuint vaoId;
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(512, 512);
glutCreateWindow("SHADER_RECTANGLE");
glewInit();
MyInit();
glutReshapeFunc(&reshape);
glutDisplayFunc(&display);
glutMainLoop();
return 0;
}
void MyInit()
{
glClearColor(0.0,0.0, 0.0,0.0);
const GLfloat 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 // 顶部
};
//创建VAO对象
glGenVertexArrays(1, &vaoId);
glBindVertexArray(vaoId);
//创建VBO对象
glGenBuffers(1, &vboId);
glBindBuffer(GL_ARRAY_BUFFER, vboId);
//传入VBO数据
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//解除VBO绑定
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void reshape(int w, int h)
{
glViewport(0, 0,(GLsizei)w, (GLsizei)h);
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
const char* vertex_shader =
"#version 330\n"
"layout (location = 0) in vec3 aPos;"// 位置变量的属性位置值为 0
"layout (location = 1) in vec3 aColor; "// 颜色变量的属性位置值为 1
"out vec3 OurColor;"//向片段着色器输出一个颜色
"void main() {"
" gl_Position = vec4(aPos, 1.0);"
"OurColor = aColor;"
"}";
const char* fragment_shader =
"#version 330\n"
"out vec4 FragColor;"
"in vec3 OurColor;"
"void main() {"
" FragColor = vec4(OurColor, 1.0f);"
"}";
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertex_shader, NULL);
glCompileShader(vertexShader);
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragment_shader, NULL);
glCompileShader(fragmentShader);
GLuint shader_programme = glCreateProgram();
glAttachShader(shader_programme, fragmentShader);
glAttachShader(shader_programme, vertexShader);
glLinkProgram(shader_programme);
glUseProgram(shader_programme);
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader);
//绑定VBO
glBindBuffer(GL_ARRAY_BUFFER, vboId);
//解释顶点数据方式
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), 0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*) (3 * sizeof(float)));
glEnableVertexAttribArray(1);
//绘制模型
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glutSwapBuffers();
}