OpenGL3.3着色器

着色器介绍

什么是着色器

着色器是运行在GPU上的小程序
着色器使用一种叫GLSL的类C语言编程
着色器只是一种把输入转为输出的程序
着色器之间的唯一沟通就是输入输出
每个着色器的入口点都是main函数

什么是GPU(Graphic Process Unit图像处理单元)

首先说明GPU不是显卡!!! 显卡由GPU、显存、电路板,还有BIOS固件组成
GPU是专门为更快更高效得处理计算机图形图像操作而设计的处理单元或电路
在硬件中 他和CPU一样 是半导体芯片(处理器)

GPU的作用

GPU主要用于计算大量特定的数学运算
由于当今3d游戏的兴起和流行 在图形图像方面更加注重多边形变换和光照的影响了 其中的数学运算大大影响了CPU的运行 所以GPU诞生了

CPU和GPU的对比

在这里插入图片描述
GPU的特点是有很多的ALU(算术运算单元)和很少的cache(高速缓冲存储器)

GPU拥有众核每个核拥有的缓存大小相对小,数字逻辑运算单元也少而简单

也就是说CPU适合比较少量而复杂的计算 而GPU适合大量而简单的计算
GPU采用了数量众多的计算单元和超长的流水线,但只有非常简单的控制逻辑并省去了Cache。而CPU不仅被Cache占据了大量空间,而且还有有复杂的控制逻辑和诸多优化电路,相比之下计算能力只是CPU很小的一部分

GPU的运算速度取决于雇了多少小学生,CPU的运算速度取决于请了多么厉害的教授

顶点着色器

顶点着色器是逐顶点执行 主要用于也只用于处理顶点 将输入的顶点进行矩阵变换 实现裁剪 视口变换 透视等操作
一般在顶点着色器实现:
1.视口变换(将世界坐标中的像素映射在2d屏幕上)
2.

每个顶点着色器的每个输入变量也叫顶点属性

片元着色器

片元着色器则是逐片元执行 主要是光栅化之后的各个像素片元进行颜色处理

几何着色器

逐图元为单位执行 一般在顶点着色器之后 用于

GLSL是OpenGL着色语言(OpenGL Shading Language)是用来在OpenGL中着色编程的语言,也即开发人员写的短小的自定义程序(目前是写在记事本里 再用文件输在程序中调用 此举是为了方便修改着色器)

着色器内置变量

关键字in和out

专门实现着色器的输入输出 只要一个输出变量与下一个着色器阶段的输入匹配 他就会传递下去
顶点着色器的输入:应该接受一种特殊形式的输入,否则就会效率低下 顶点着色器的输入是从顶点数据中直接接收输入

使用location指定输入变量

比如:
layout(locationg=0)in vec3 apos apos指变量名称,它所保存的是顶点的位置信息 in表示设置这个变量是输入变量,即apos为着色器阶段的输入变量,指定了数据进入着色器的流向 layout(location=0),这一字段非常重要,它将apos的位置属性location设置为0
这个位置属性 location=0作用主要是在使用这个函数glVertexAttribPointer()如果第一个参数的值和这个位置值相同则相关数据就会传输过去 其实也就是给这个值传值需要知道他的位置值 这个函数也行glEnableVertexAttribArray(0);
(一个小理解 如果我们打算从一个着色器向另一个着色器发送数据,我们必须在发送方着色器中声明一个输出,在接受方着色器中声明一个类似的输入。当类型和名字都一样的时候 OpenGL就会把两个变量链接到一起 他们之间就可以发送数据了)

重组:类似于vec4 a =vec4(vec2 b,1,2);就是用更小的向量分量去定义更大的

uniform

uniform是一种从cpu中的应用向GPU中的着色器发送数据的方式
uniform是全局的 也就意味着他可以被任意着色器在任意阶段访问

vec4

默认初始化为(0.0,0.0,0.0,1.0)

mat3*4

第一个值表示列数 第二个值表示行数
m = mat3(4.0) = 4.0 0.0 0.0
0.0 4.0 0.0
0.0 0.0 4.0
位置相关分量 (x,y,z,w)
颜色相关分量(r,g,b,a)
纹理相关分量(s,t,p,q)

mat3*4 m;//3列4行矩阵
vec4 v = m[1];//v记录了矩阵m第二列的数据值

OpenGL的一些专用词

顶点数组对象:Vertex Array Object,VAO
顶点缓冲对象:Vertex Buffer Object,VBO
索引缓冲对象:Element Buffer Object,EBO或Index Buffer Object,IBO
帧缓冲对象:fbo
渲染缓冲对象:rbo
关于绑定缓冲对象问题
绑定对象的过程就像设置铁路的道岔开关,每一个缓冲类型中的各个对象就像不同的轨道一样,我们将开关设置为其中一个状态,那么之后的列车都会驶入这条轨道。
OpenGL允许我们同时绑定多个缓冲类型,只要这些缓冲类型是不同的,换句话说,同一时间,不能绑定两个相同类型的缓冲对象。
绑定缓冲类型后,所有该缓冲类型的函数调用都要用来配置该目标缓冲类型
比如顶点缓冲类型GL_ARRAY_BUFFER,glBufferData是通过指定目标缓冲类型来进行数据传输的,而每一个目标缓冲类型再使用前要提前绑定一个缓冲对象,从而赋予这个缓冲对象一个类型的意义,如果绑定了两个相同类型的目标缓冲,数据的配置肯定就会出错。(可以这样想一下,我要把数据存入顶点缓冲区,但是顶点缓冲区可以有很多缓冲对象,我需要传入哪个呢,于是我就要提前绑定一个,之后,我只要向顶点缓冲区内传入数据,这个数据就会自动进入被绑定的那个对象里面)
之后调用glBufferData()传输所需数据,其中第一个参数就是要制定缓冲类型,根据这个类型锁定当前唯一的目标缓冲

glCreateShader函数(创建着色器)

unsight int vertexShader;
vertexShader=glCreateShader(GL_VERTEX_SHADER); glCreateShader创建一个着色器 因为创建的是顶点着色器 所以提供的参数是GL_VERTEX_SHADER

glShaderSource函数 (把文档中的着色器代码传给着色器)

glShaderSource(vertexShader,1,&vertexShaderSource,NULL); glShaderSource函数第一个参数是要编译的着色器对象,第二个参数是指定传递的源代码字符串数量 第三个参数是顶点着色器的真正源码 第四个参数目前不知道 这个函数主要就是给着色器提供源码
glCompileShader函数 (和上面那个函数配套使用 编写着色器的意思)
glCompileShader(vertexShader);glCompileShader函数应该是配套使用的 在指定完源码之后用这个 意思是编写着色器 compile是编写的意思

glGetShaderiv函数 (查着色器错误 如果出错就会在小黑框里面显示)

glGetShaderiv(vertexShader,GL_COMPILE_STATUS,&success); glGetShaderiv可检测是否编译成功 第一个参数是要检测的着色器对象 第二个参数是指定着色器对象的参数。 返回的值存储在第三个参数里面
第二个参数可为:
GL_SHADER_TYPE:用来判断并返回着色器类型,若是顶点着色器返回GL_VERTEX_SHADER,若为片元着色器返回GL_FRAGMENT_SHADER.
GL_DELETE_STATUS:判断着色器是否被删除,是返回GL_TRUE,否则返回GL_FALSE,
GL_COMPILE_STATUS:用于检测编译是否成功,成功为GL_TRUE,否则为GL_FALSE.
GL_INFO_LOG_LENGTH:用于返回着色器的信息日志的长度,包括空终止字符(即存储信息日志所需的字符缓冲区的大小)。 如果着色器没有信息日志,则返回值0。
GL_SHADER_SOURCE_LENGTH:返回着色器源码长度,不存在则返回0;
与这个检测着色器是否编译成功相同的是 glGetProgramiv(shaderProgrm,GL_LINK_STATUS,&success);

glGetShaderInfolog函数 (这个是和上面那个配套用 也是查错)

glGetShaderInfolog(vertexShader,512,NULL,infoLog);glGetShaderInfolog是获取信息日志 第一个参数是指定获取着色器对象,第二个参数是保存信息日志的缓冲区大小,第三个参数是要写入的信息日志长度,减去null终止符,第四个参数是指向保存信息日志的缓冲区的指针 本函数中infoLog是一个char [512]的数组
与这个函数类似的是 glGetProgramInfoLog(shaderProgram,512,NULL,infoLog);

片段着色器改成GL_FRAGMENT_SHADER就行

glCreateProgram()函数 (创建着色器程序)

unsigned int shaderProgram;
shaderProgram= glCreateProgram(); glCreateProgram();创建一个着色器项目

glAttachShader函数 (将着色器对象(也就是类似顶点着色器之类的)和着色器程序连接)

glAttachShader(shaderProgram,vertexShader);glAttachShader函数是将着色器对象和着色器程序连接 第一个参数是着色器程序,第二个参数是着色器对象 glDetachShader是断开连接
glAttachShader(shaderProgram,fragmentShader);这个函数将着色器连接到指定的程序。着色器可以在任何时候连接,着色器连接到程序之前不一定需要编译,甚至可以没有源代码。唯一的要求是:每一个程序对象必须有且只有一个顶点着色器和一个片段着色器与之相连

glLinkProgram函数 (链接形成可执行的着色器程序 也就是组装着色器程序)

glLinkProgram(shaderProgram);glLinkProgram链接操作负责生成最终的可执行程序,生成在硬件上运行的最终硬件指令。链接操作将检查各种对象的数量,确保成功的链接。
链接程序将确保:
确保 Vertex Shader 写入 Fragment Shader 的输出变量,和Fragment Shader使用的输入变量,有相同的声明。
确保在Vertex Shader 和 Fragment Shader中声明的统一变量和统一变量缓冲区的类型相同
确保最终的程序符合具体实现的限制,例如,属性、统一变量或者输入输出着色器变量的数量

在检测完编译是否成功之后 调用glUseProgram(shaderProgram);激活这个程序对象,在激活后每个着色器调用和渲染调用都会使用这个程序对象

在把着色器链接给程序对象之后要记得删除着色器,不再需要他们了

glDrawArrays函数 (用索引来绘制)

glDrawArrays(GL_TRIANGLES,0,3);glDrawArrays第一个参数是绘制的OpenGL图元类型,第二个参数是指定数组的起始索引,第三个参数是我们打算绘制多少个顶点
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);//这里是索引的绑定
indices是索引的数组

glGetUniformLocation函数 (获取着色器程序中某变量的位置值)

uniform vec4 b;//定义了一个uniform类型的vec4变量b
int a = glGetUniformLocation(shaderProgram,“b”); 这样我们就获取到了b的位置值 第一个参数是一个着色器程序 第二个参数是这个变量的名称

glUniformMatrix4fv函数 (用来给glUniform传值用的)

glUniformMatrix4fv(transformLoc, 1, GL_FALSE, value_ptr(trans));第一个参数是Uniform的位置值 第二个参数是要传几个矩阵进来 第三个参数是 询问我们我们是否希望对我们的矩阵进行置换,也就是说交换我们矩阵的行和列 最后一个参数是真正的矩阵数据 但是GLM并不是把它们的矩阵储存为OpenGL所希望接受的那种,因此我们要先用GLM的自带的函数value_ptr来变换这些数据
glUniform4f(vertexColorLocation, 0.0f, 1.0f, 0.0f, 1.0f); 这个是给uniform 的vec4赋值

反转向量 color = color.abgr

子程序

这一部分是在着色器代码中写
subroutine 用来定义子程序的类型
subroutine vec4 Color(vec4);//创建一个子程序类型 Color 只需要定义参数类型就行
subroutine (Color) vec4 blue (vec4 n)//定义子程序集合的内容
{
return 0
}
subroutine (Color) vec4 red (vec4 n)
{
return 0
}
subroutine uniform Color ColorShader; //声明一个Color类型的子程序的uniform变量

在这里插入图片描述
上面例子中
func_1可以使用Func_1,Func_2因为这两个子程序都指定了Type_1
func_2只能Func_1
func_3只能Func_2

下面的函数都是在c++中写

glGetSubroutineUniformLocation(GLuint program,GLunum shadertype,const char*name)

例如:glGetSubroutineUniformLocation(vertexshader,GL_VERTEX_SHADER,ColorShader);
返回名为name的子程序uniform的位置
program就是着色器对象
shadertype指定着色器 值必须为:
GL_VERTEX_SHADER
GL_TESS_CONTROL_SHADER
GL_TESS_EVALUATION_SHADER
GL_GEOMETRY_SHADER
GL_FRAGMENT_SHADER

glGerSubroutineIndex(GLuint program,GLunum shadertype,const char*name)

例子:GLuint ambientIndex;
ambientIndex = glGetSubroutineIndex(vertexshader,GL_VERTEX_SHADER,ColorShader)
从着色器程序program中返回名为name所对应的着色器函数的索引
shadertype指定着色器 值必须为:
GL_VERTEX_SHADER
GL_TESS_CONTROL_SHADER
GL_TESS_EVALUATION_SHADER
GL_GEOMETRY_SHADER
GL_FRAGMENT_SHADER

glGetIntegerv(GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS,&n)

这个函数不知道具体时干嘛的 但是这样写应该是获取子程序的最大数量给n

glUniformSubroutinesuiv(GLenum shadertype,GLsizei count,const GLuint*indices)

获取到子程序的索引河uniform位置之后 用来指定在着色器中执行哪一个子程序的函数
某个着色阶段中,所有的子程序uniform都必须现先经过初始化的过程
设置所有count个着色器子程序uniform使用indices数组中的值
第i个子程序对应于indices[i]的值
shadertype指定着色器 值必须为:
GL_VERTEX_SHADER
GL_TESS_CONTROL_SHADER
GL_TESS_EVALUATION_SHADER
GL_GEOMETRY_SHADER
GL_FRAGMENT_SHADER
注意:调用glUseProgram()时会重新设置所有子程序uniform的值

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值