[OpenGL]OpenGL着色器基础知识及应用

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();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章