我的OpenGL ES学习之路(五):着色器

在上篇文章渲染流程 已经说到了着色器,在使用着色器进行渲染的时候,需要两个对象:着色器对象程序对象。着色器对象和程序对象就好比编译器和链接对象,着色器对象是包含单个着色器的对象,着色器对象被编译为一个目标形式 (类似.o .obj文件),编译之后着色器对戏那个可以链接到程序对象。

程序对象可以链接多个着色器对象。在OpenGL ES中,每个程序对象必须连接一个顶点着色器和一个片段着色器。程序对象被链接为用于渲染的最后“可执行程序”。

获得链接后的着色器对象的步骤:

  • 创建一个顶色器对象和一个片段着色器对象
  • 着色器源代码连接到每个着色器对象
  • 编译着色器对象
  • 创建一个程序对象
  • 编译后的着色器连接到程序对象
  • 链接程序对象

下面结合着OpenGL ES的API挨个说

创建和编译一个着色器

  • 创建着色器

使用着色器的第一步是创建着色器

// type: 创建的着色器类型可以是:GL_VERTEX_SHADER 或者GL_FRAGMENT_SHADER
// 返回值:指向着色器的句柄
GLuint glCreateShader (GLenum type);
  • 删除着色器

当使用完着色器对象的时候,可以 删除着色器

// shader: 要删除的着色器对象的句柄
void glDeleteShader (GLuint shader);

注意:如果一个着色器连接到一个程序对象,调用glDeleteShader不会立刻删除着色器,而是将着色器标记为删除,在着色器不再连接任何程序对象时,内存将被释放。

  • 给着色器对象提供源代码

创建了着色器对象之后,就是给着色器对象提供源代码,着色器源代码以字符串的形式提供:

// shader: 指向着色器对象的句柄
// count: 着色器源代码字符串的数量。着色器可以由多个源字符串组成,但是每个着色器只能有一个main函数
// string: 指向着色器源代码的字符串指针
// length: 指向保存着多个(如果有多个)源代码字符串大小的整型数组指针
void glShaderSource (GLuint shader, 
                           GLsizei count, 
                           const GLchar *const string,
                           const GLint *length );
  • 编译着色器

指定完着色器源代码之后,下一步就是用glCompileShader编译着色器:

void glCompileShader (GLuint shader);

可以用glGetShaderivGL_COMPILE_STATUS参数查询编译的信息

  • 查询着色器状态信息

// shader: 着色器句柄
/* pname: 要查询的信息的参数:
                  GL_COMPILE_STATUS: 编译状态
                  GL_DELETE_STATUS: 着色器是否用glDeleteShader标记为删除
                  GL_INFO_LOG_LENGTH: 编译信息日志长度
                  GL_SHADER_SOURCE_LENGTH: 着色器源代码长度         
                  GL_SHADER_TYPE: 着色器类型
*/
// params: 指向 “查询结果” 的整数存储位置的指针
void glGetShaderiv (GLuint shader, GLenum pname, GLint *params);

检查着色器是否编译成功,用GL_COMPILE_STATUS参数,成功编译,params结果是GL_TRUE,编译失败GL_FALSE,编译错误信息会写入信息日志。即使编译成功,也会在信息日志中写入信息。
可以用GL_INFO_LOG_LENGTH查询日志的长度。日志可以用glGetShaderInfoLog检索。

  • 获取信息日志

// maxLength: 保存信息日志的缓冲区大小
// length: 要写入的信息日志的长度,减去null终止符
// infoLog: 指向保存信息日志的字符缓冲区的指针
void glGetShaderInfoLog (GLuint shader, 
                                GLsizei maxLength, 
                                GLsizei *length, 
                                GLchar *infoLog);
  • 创建程序对象

上面的都是如果创建着色器对象,下面是创建一个程序对象。程序对象是一个容器对象,可以将着色器对象与之相连接,并链接到一个最终的可执行程序。操作程序对象的API与着色器的API比较相似。

// glCreateProgram没有参数,返回一个指向新程序对象的句柄
GLuint glCreateProgram();
  • 删除程序对象

// program: 要删除的程序对象的句柄
void glDeleteProgram(GLuint program);
  • 连接 程序对象 和 着色器对象

// program: 程序对象的句柄
// shader: 着色器对象的句柄
void glAttachShader(GLuint program, GLuint shader);

注意:这个函数将着色器连接到指定的程序。着色器可以在任何时候连接,着色器连接到程序之前不一定需要编译,甚至可以没有源代码。唯一的要求是:每一个程序对象必须有且只有一个顶点着色器和一个片段着色器与之相连

可以用glDetachShader断开连接。

  • 断开 连接

void glDetachShader (GLuint program, GLuint shader);
  • 链接生成最终的可执行程序

前提:程序连接了着色器,并且着色器编译成功

void glLinkProgram (GLuint program);

链接操作负责生成最终的可执行程序,生成在硬件上运行的最终硬件指令。链接操作将检查各种对象的数量,确保成功的链接。
链接程序将确保:

  • 确保 Vertex Shader 写入 Fragment Shader输出变量,和Fragment Shader使用的输入变量,有相同的声明。
  • 确保在Vertex ShaderFragment Shader中声明的统一变量统一变量缓冲区的类型相同

  • 确保最终的程序符合具体实现的限制,例如,属性、统一变量或者输入输出着色器变量的数量

  • 查询链接状态

上面说过 查询shader的编译状态glGetShaderiv,下面是查询程序的链接链接状态glGetProgramiv

// pname: 要获取的参数信息
void glGetProgramiv (GLuint program, GLenum pname, GLint *params);

要检查链接是否成功,可以查询 GL_LINK_STATUS,和着色器一样,程序对象存储在一个信息日志,日志的长度可以用GL_INFO_LOG_LENGTH来查询

  • 获取程序的日志信息

// 参数跟着色器部分一样
void glGetProgramInfoLog (GLuint program, 
                          GLsizei maxLength,
                          GLsizei *length,
                          GLchar *infoLog);

程序成功链接后,就已经差不多为使用它渲染做好了准备。但是在这之前,我们需要检查程序是否有效,即:链接成功并不能保证执行成功,例如,应用程序没有把有效的纹理绑定到采样器,这种错误在链接的时候我们无法知道,但是在绘图的时候会出现,可以调用glValidateProgram来校验

  • 校验应用程序有效性

void glValidateProgram (GLuint program);

校验的结果可以用glGetProgramiv来查询,pname参数使用GL_VALIDATE_STATUS来检查,同时信息日志也会更新。
注意:glValidateProgram是一个比较耗时的操作,一般只是用作调试。

在渲染之前,还有一件事要做:把应用程序设置成活动程序

  • 应用程序设置成 活动程序

void glUseProgram (GLuint program);

设置成活动程序之后,就可以开始渲染了。

未完待续 。。。。。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容