2018-2019 年度第二学期 00106501 计算机图形学 童伟华管理科研楼 1205 室 E-mail: tongwh@ustc.edu.cn 中国科学技术大学数学科学学院 http://math.ustc.edu.cn/
第二节 GLSL(I) 2
顶点着色器应用 顶点的移动 变形 (morphing) 波动 分形 光照 更真实的模型 卡通着色器 3
片段着色器应用 逐片段进行光照计算 逐顶点光照计算 逐片段光照计算 4
片段着色器应用 典型应用 : 纹理映射 光滑明暗处理环境映射凹凸映射 5
着色器的编程语言 OpenGL Shading Language 的缩写 OpenGL 4.6 的一部分 高级类 C 语言 引进新的数据类型 矩阵 向量 采样器 (Samplers) OpenGL 的状态通过内置变量传递 6
可编程流水线 利用 GLSL 语言编写着色器 (Shader), 用于替代固定流水线中顶点处理子流水线及片元处理子流水线 另外, 最新版本的 OpenGL 还提供了细分着色器, 几何着色器, 计算着色器等, 来提供更丰富的绘制功能 7
一个简单的顶点着色器 //filename: vgl_simple.vert #version 330 core layout(location = 0) in vec4 aposition; uniform mat4 ModelViewMatrix; uniform mat4 ProjectionMatrix; void main(void) { gl_position = ProjectionMatrix * ModelViewMatrix * aposition; } 8
一个简单的片元着色器 //filename: fgl_simple.frag #version 330 core layout(location = 0) out vec4 fcolor; void main (void) { fcolor = vec4(0.0f, 0.0f, 1.0f, 1.0f); } 9
GLSL 的基本语法 与 C 语言类似, 着色器从 main( ) 函数开始运行 ; 可自定义函数 ; 注释方式相同 不同之处 : #version 330 core: 指定所用的 OpenGL 语言版本, 什么模式 uniform: 变量修饰符, 指变量为用户应用程序传递给着色器的数据, 它对于给定的图元而言是一个常量 in: 变量修饰符, 指这个变量为着色器的输入变量 out: 变量修饰符, 指这个变量为着色器的输出变量 layout: 布局限定符, 指定变量的布局规范 10
GLSL 的代码编写与命名 GLSL 的代码有两种编写方式 利用 C/C++ 语言的字符串数组 ( 优点 : 不需要进行文件读写 ) 例如 : Const char *vshader = { }; #version 330 core layout(location = 0) in vec4 aposition; 利用文本编辑器编写, 然后利用文件读写载入程序中 ( 优点 : 代码修改方便 ) 若采用文本编辑器,GLSL 代码的文件后缀建议使用.vert: 顶点着色器.frag: 片元着色器.tesc,.tese: 细分控制 / 求值着色器.geom: 几何着色器.comp: 计算着色器 11
顶点着色器的数据输入与输出 输入逐顶点变量的基本步骤 : 初始化顶点数组对象 :glgenvertexarrays, glbindvertexarray 等 分配顶点缓存对象 :glgenbuffers,glbindbuffer 等 将数据载入缓存对象 :glbufferdata( 或 glbuffersubdata) 等 将顶点数据与顶点着色器的 in 变量关联 : glgetattriblocation, glvertexattribpointer,glenablevertexattribarray 等 输入 uniform 变量的基本步骤 : 查询 uniform 变量的索引 :glgetuniformlocation 等 设置 uinform 变量的值 :gluniform, gluniformmatria 等 ( 根据类型选择合适的函数 ) 利用 out 修饰变量, 在片元着色器可接收经过插值的相应变量 利用 transform feedback 机制, 可将数据回传至缓存中 12
片元着色器的输入输入与输出 利用 in 修饰变量, 可从顶点着色器获取经过插值的数据 利用 out 修饰变量, 将数据输出至帧缓存中 ; 位置可由 layout 指定 ; 输出到哪个颜色缓存可通过 gldrawbuffer 或 gldrawbuffers 指定 13
连接着色器与 OpenGL 14
创建着色器 创建着色器对象 加载着色器源代码 编译着色器对象 验证是否成功编译 15
创建着色器对象 GLuint glcreateshader(glenum type); 创建一个空的着色器对象, 着色器对象维护定义着色器的源代码字符串 type 可取值为 GL_VERTEX_SHADER: 顶点着色器 GL_FRAGMENT_SHADER: 片段着色器 返回非零整数, 出错时返回零 16
加载着色器源代码 void glshadersource(gluint shader, GLsizei count, const CLchar **string, const GLint *length); 把存储在字符串数组 string 中的源代码加载到着色器对象 shader, 之前存储在着色器对象里的源代码将被完全替换 count 字符串的个数 length NULL( 每个字符串都是以 null 结束 ) 或字符串长度的数组 ( 若有长度为负值, 表示该字符串以 null 结束 ) 17
编译着色器对象 void glcompileshader(gluint shader); 编译着色器对象 shader 的源代码 可以调用 glgetshaderiv() 函数以 GL_COMPILE_STATUS 为参数查询编译结果 可以调用 glgetshaderinfolog() 函数查看编译信息日志 18
创建程序 创建程序对象 把着色器对象连接到程序对象 链接程序 验证链接是否成功 使用着色器 19
创建程序对象 GLuint glcreateprogram(void); 创建一个空的程序对象 返回非零整数, 出错时返回零 OpenGL 用程序对象来封装管理可执行着色器 可创建多个程序对象, 并在渲染时切换以使用不同的着色器 20
连接着色器对象 void glattachshader(gluint program, GLuint shader); 把着色器对象 shader 连接 (attach) 到程序对象 program 可在加载源代码或编译前, 把着色器对象连接到程序对象 可连接顶点 / 片段着色器对, 或只连接一类 可连接多个同一类的着色器对象到程序对象, 但只能有一个着色器对象有 main 函数 一个着色器对象可连接到多个程序对象 21
链接程序 void gllinkprogram(gluint program); 链接程序对象 program, 生成可执行程序 链接前, 连接到程序对象的着色器对象必须已成功编译 可以调用 glgetprogramiv() 函数以 GL_LINK_STATUS 为参数查询链接结果 可以调用 glgetprograminfolog() 函数查看链接信息日志 22
使用程序 void gluseprogram(gluint program); 安装可执行程序 program 作为 OpenGL 渲染状态机的一部分进行顶点和 / 或片段处理 program 为 0 时, 使用固定功能流水线 程序对象在使用中时, 应用程序可以随意修改甚至删除程序对象, 而不会影响作为当前状态机一部分的可执行代码 gllinkprogram() 成功重链接使用中的程序对象后, 将安装生成的可执行代码作为当前渲染状态机的一部分 23
清理函数 void gldeleteshader(gluint shader); 删除着色器对象 shader 如果着色器对象没有连接到任何程序对象, 立即删除 ; 否则, 标记为删除 void gldetachshader(gluint program, GLuint shader); 把着色器对象 shader 从程序对象 program 分离 如果着色器标记为删除, 且分离后没有连接到其他着色器, 则被删除 24
清理函数 void gldeleteprogram(gluint program); 删除程序对象 program 如果程序对象不是正在使用的渲染状态机的一部分, 立即删除 ; 否则, 标记为删除 当程序对象被删除时, 所有连接的着色器对象将被分离出来 25
查询函数 void glgetshaderiv(gluint shader, GLuint index, GLint *params); 返回着色器对象 shader 参数为 pname 的属性值 pname GL_SHADER_TYPE GL_DELETE_STATUS GL_COMPILE_STATUS GL_INFO_LOG_LENGTH GL_SHADER_SOURCE_LENGT H params 着色器类型 :GL_VERTEX_SHADER 或 GL_FRAGMENT_SHADER 删除状态 :GL_TRUE 当前标记为删除 编译状态 :GL_TRUE 之前的编译成功 信息日志的长度 ( 含 null),0 表示无日志 串联的源代码字符串的长度 ( 含 null 字符 ),0 表示没加载源代码 26
查询函数 void glgetprogramiv(gluint program, GLenum pname, GLint *params); 返回程序对象 program 参数为 pname 的属性值 pname GL_DELETE_STATUS GL_LINK_STATUS GL_VALIDATE_STATUS GL_INFO_LOG_LENGTH GL_ATTACHED_SHADERS GL_ACTIVE_ATTRIBUTES GL_ACTIVE_UNIFORMS params 删除状态 :GL_TRUE 当前标记为删除链接状态 :GL_TRUE 之前的链接成功验证状态 :GL_TURE 之前的验证成功信息日志的长度 ( 含 null),0 表示无日志连接的着色对象的个数活动属性变量的个数活动一致变量的个数 27
查询函数 void glgetshaderinfolog(gluint shader, GLsizei bufsize, GLsizei *length, GLchar *infolog); void glgetprograminfolog(gluint program, GLsizei bufsize, GLsizei *length, GLchar *infolog); infolog 返回着色器对象 shader 最后一次编译或程序对象 program 最后一次链接的信息日志 缓冲区 infolog 的大小为 bufsize, 实际返回的日志字符数存放在 length 中 信息日志的字符数可由 glgetshaderiv() 或 glgetprogramiv() 查询得到 28
查询函数 void glgetshadersource(gluint shader, GLsizei bufsize, GLsizei *length, GLchar *source); source 返回着色器对象 shader 的源代码字符串 缓冲区 source 的大小为 bufsize, 实际的源代码字符数存放在 length 中 源代码字符串的长度可用 glgetshaderiv() 查询得到 29
查询函数 GLboolean glisshader(gluint shader); GLboolean glisprogram(gluint program); 检查 shader 或 program 是否是着色器对象或程序对象的名称 void glvalidateprogram(gluint program); 验证程序对象 program 是否可以在当前 OpenGL 环境中运行 如果验证通过, 程序对象 program 的 GL_VALIDATE_STATUS 值置为 GL_TURE 30
Thanks for your attention! 31