着色器和程序

  • 一、前言
  • 二、着色器和程序
    • 2.1 创建和编译一个着色器
      • 2.1.1 创建着色器
      • 2.1.2 删除着色器
      • 2.1.3 提供着色器源代码
      • 2.1.4 编译色器
      • 2.1.4 查询有关着色器对象的信息
        • 2.1.4.1 查询`GL_COMPILE_STATUS`
        • 2.1.4.2 查询`GL_SHADER_TYPE`
        • 2.1.4.3 查询`GL_DELETE_STATUS`
      • 2.1.5 获取信息日志
  • 三、加载着色器示例

一、前言

在博客 【我的OpenGL学习进阶之旅】你好,三角形:一个OpenGL ES 3.0示例。 分别使用C++ Native & Java 两种方式来实现 中,我们介绍了绘制一个三角形的简单程序。

在那个例子中,我们创建了两个着色器对象(一个顶点着色器,一个片段着色器)和一个程序对象,以渲染三角形。

着色器对象和程序对象是使用OpenGL ES 3.0着色器的基本概念。

接下来,我们要讲解的主题,如下所示:

  • 着色器和程序对象概述
  • 创建和编译着色器
  • 创建和链接程序
  • 获取和设置统一变量
  • 获取和设置属性
  • 着色器编译器和程序二进制代码

二、着色器和程序

需要创建两个基本对象才能用着色器进行渲染:着色器对象和程序对象。

理解着色器对象和程序对象的最佳方式就是将它们比作C语言的编译器和链接程序。 C编译器为一段源代码生成目标代码(例如,.obj或者.o文件)。在创建目标文件之后,C链接程序将对象文件链接为最后的程序。

OpenGL ES 在着色器的表现上使用类似的方式。

  1. 着色器对象是包含单个着色器的对象。
  2. 源代码提供给着色器对象,然后着色器对象被编译为一个目标形式(类似与.obj文件)。
  3. 编译之后,着色器对象可以连接到一个程序对象。程序对象可以连接多个着色器对象。
  4. 在OpenGL ES中,每个程序对象必须连接一个顶点着色器和一个片段着色器(不多也不少),这和桌面OpenGL不同。
  5. 程序对象被链接为用于渲染的最后“可执行程序”。

获得连接后的着色器对象的一般过程包括6个步骤:

  1. 创建一个顶点着色器对象和一个片段着色器对象
  2. 将源代码连接到每个着色器对象。
  3. 编译着色器对象。
  4. 创建一个程序对象。
  5. 将编译后的着色器对象连接到程序对象。
  6. 链接程序对象。

如果没有错误,就可以在任何时候通知GL使用这个程序绘图。

这篇博客我们先介绍着色器相关的知识。

2.1 创建和编译一个着色器

2.1.1 创建着色器

使用着色器对象的第一步是创建着色器,这可以用glCreateShader完成。

 GLuint glCreateShader (GLenum type);


参数 type : 创建的着色器类型可以是GL_VERTEX_SHADER或者GL_FRAGMENT_SHADER

调用glCreateShader 将根据传入的 type 参数创建一个新的顶点着色器或者片段着色器。 返回值是指向新着色器对象的句柄。

2.1.2 删除着色器

当完成着色器对象时,可以使用glDeleteShader 删除

void glDeleteShader (GLuint shader);

参数shader表示: 要删除的着色器对象的句柄。

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

2.1.3 提供着色器源代码

一旦创建着色器对象,下一件事通常是使用glShaderSource提供着色器源代码

void  glShaderSource (GLuint shader, GLsizei count, const GLchar *const*string,const GLint *length);

参数讲解:

  • shader
    指向着色器对象的句柄
  • count
    着色器源字符串的数量。着色器可以由多个源字符串组成,但是每个着色器只能有一个main函数。
  • string
    指向保存数量为count的着色器源字符串的数组指针。
  • length
    指向保存每个着色器字符串大小且元素数量为count的整数数组指针。如果lengthNULL,着色器字符串将被认定为空。如果length不为NULL,则它的每个元素保存对应string数组的着色器的字符数量。如果任何元素的length值均小于0,则该字符串被认定以null结束。

2.1.4 编译色器

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

void glCompileShader (GLuint shader);

参数 shader 表示: 需要编译的着色器对象句柄。

2.1.4 查询有关着色器对象的信息

调用glCompileShader将编译已经保存在着色器对象的着色器源代码。
和常规的语言编译器一样,编译之后你想知道的第一件事是不是没有错误。你可以使用glGetShaderiv查询这一信息和其他有关着色器对象的信息。

void glGetShaderiv (GLuint shader, GLenum pname, GLint *params);


参数讲解:

  • shader
    执向需要获取信息的着色器对象的句柄
  • pname
    获取信息的参数,可以是:

    • GL_COMPILE_STATUS
    • GL_DELETE_STATUS
    • GL_INFO_LOG_LENGTH
    • GL_SHADER_SOURCE_LENGTH
    • GL_SHADER_TYPE
  • params
    指向查询结果的整数存储位置的指针。

2.1.4.1 查询GL_COMPILE_STATUS

要检查着色器是否编译成功,可以用pname参数GL_COMPILE_STATUS在着色器对象上调用glGetShaderiv
如果着色器编译成功,结果将是GL_TRUE。如果编译失败,结果将是GL_FALSE,编译错误将写入信息日志

信息日志是由编译器写入并包含错误信息或者警告的日志。
即使编译操作成功,也会在信息日志中写入信息。要检索信息日志,可以使用GL_INFO_LOG_LENGTH查询它的长度。

日志本身可以用glGetShaderInfoLog检索。

2.1.4.2 查询GL_SHADER_TYPE

查询GL_SHADER_TYPE 将返回着色器类型:GL_VERTEX_SHADER或者GL_FRAGMENT_SHADER

2.1.4.3 查询GL_DELETE_STATUS

查询GL_DELETE_STATUS 返回着色器是否用glDeleteShader 标记为删除。

2.1.5 获取信息日志

编译着色器并检查信息日志长度之后,你可能希望检索信息日志(特别是在编译失败时查看原因)。为此,首先需要查询GL_INFO_LOG_LENGTH并分配一个足以存储信息日志的字符串。
然后,用glGetShaderInfoLog检索信息日志。

void glGetShaderInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);


参数说明:

  • shader
    需要获取信息日志的着色器对象句柄
  • bufSize
    保存信息日志的缓冲区大小
  • length
    写入的信息日志的长度(减去null终止符)。如果不需要知道长度,这个参数可以为NULL
  • infoLog
    指向保存信息日志的字符缓冲区的指针。

信息日志没有任何强制的格式或者必须的信息。但是,大部分OpenGL ES 3.0实现将返回错误信息,包括编译器发现的错误源代码行号。有些实现还在日志中提供警告或者附件信息。

比如我的博客 【我的OpenGL学习进阶之旅】解决着色器编译错误:#version directive must occur on the first line of the shader中描述的,产生的信息:

2021-11-15 15:09:07.406 26065-26107/opengles3.book.hello_Triangle E/ESShader: ERROR: 0:2: '' : #version directive must occur on the first line of the shader ERROR: 0:9: 'in' : storage qualifier supported in GLSL ES 3.00 only  

三、加载着色器示例

到现在为止,我们已经说明了创建着色器、编译、找出编译状态和查询信息日志所需的所有函数。

下面写一个示例,来加载一个着色器。

/*** Loads the given source code as a shader of the given type.** 负责 加载着色器源代码、编译并检查错误。他返回一个着色器对象*/
static GLuint loadShader(GLenum shaderType, const char** source) {// Create the shader objectGLuint shader;GLint compiled;// Create the shader object// shaderType 可以是  GL_VERTEX_SHADER  或者  GL_FRAGMENT_SHADERshader = glCreateShader(shaderType);if (shader == 0) {return 0;}// Load the shader sourceglShaderSource(shader, 1, source, nullptr);// Compile the shaderglCompileShader(shader);// Check the compile statusglGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);if (!compiled) {GLint infoLen = 0;glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);if (infoLen > 1) {char* infoLog = (char*)malloc(sizeof(char) * infoLen);// 检索信息日志glGetShaderInfoLog(shader, infoLen, nullptr, infoLog);LOGE("Error compiling shader:\n%s\n", infoLog);free(infoLog);}// 删除ShaderglDeleteShader(shader);return 0;}return shader;
}

【我的OpenGL学习进阶之旅】着色器和程序(上)------着色器相关推荐

  1. 【我的OpenGL学习进阶之旅】介绍一下 绘制图元

    目录 一.绘制图元 1.1 `glDrawArrays` 1.1.1 `glDrawArrays`API说明 1.1.2 `glDrawArrays`API示例 1.2 `glDrawElements ...

  2. 【我的OpenGL学习进阶之旅】C++如何加载TGA文件?

    一.TGA文件相关介绍 通过前面的博客 [我的OpenGL学习进阶之旅]什么是TGA文件以及如何打开TGA文件? 地址:https://ouyangpeng.blog.csdn.net/article ...

  3. 【我的OpenGL学习进阶之旅】【持续更新】关于学习OpenGL的一些资料

    目录 一.相关书籍 OpenGL 方面 C方面 NDK 线性代数 二.相关博客 2.0 一些比较官方的链接 2.1 OpenGL着色器语言相关 2.2 [[yfan]](https://segment ...

  4. 【我的OpenGL学习进阶之旅】OpenGL ES 3.0新功能

    目录 1.1 纹理 1.2 着色器 1.3 几何形状 1.4 缓冲区对象 1.5 帧缓冲区 OpenGL ES 2.0 开创了手持设备可编程着色器的时代,在驱动大量设备的游戏.应用程序和用户接口中获得 ...

  5. 【我的OpenGL学习进阶之旅】着色器编译器和程序二进制码

    目录 一.着色器编译器 二.程序二进制码 2.1 glGetProgramBinary 2.2 glProgramBinary 一.着色器编译器 当你要求OpenGL ES 编译和链接着色器的时候,光 ...

  6. 【我的OpenGL学习进阶之旅】解决着色器语法错误:The shader uses varying u_Color, but previous shader does not write to it

    一.错误描述 在加载完顶点着色器和片段着色器,然后link生成program的时候,出现了错误,如下所示: 2021-12-31 09:34:01.072 15937-16006/com.oyp.op ...

  7. 【我的OpenGL学习进阶之旅】解决着色器编译错误:#version directive must occur on the first line of the shader

    目录 一.问题描述 二.分析错误 三.解决问题 三.总结 一.问题描述 今天编写一个OpenGL ES的demo,发现没有任何图元输出. 查看日志,发现报了如下错误: 2021-11-15 15:09 ...

  8. 【我的OpenGL学习进阶之旅】着色器GLSL运行时报错: ERROR: 0:40: ‘gl_FragColor‘ : undeclared identifier

    一.错误描述 把从一段GLSL2.0的代码改造成GLSL3.0代码的时候,运行报错,如下所示: [GLUtils.cpp][loadShader][42]: GLUtils::loadShader e ...

  9. 【我的OpenGL学习进阶之旅】解决关于在OpenGL ES开发中GLSurfaceView调用了onPause和onResume方法,然后息屏亮屏之后GLSurfaceView黑屏的问题

    目录 一.问题描述 二.分析问题 2.1 排查onPause和onResume方法 2.2 注释掉onPause和onResume方法 2.3 GLSurfaceView 关于Activity生命周期 ...

最新文章

  1. Android Action Bar 详解篇
  2. 【控制】《多智能体系统的协同群集运动控制》陈杰老师-第2章-连通性保持条件下多智能体系统群集运动控制
  3. xpcom java_[Mozilla] JavaXPCOM 的jar 包概述
  4. BootstrapValidator验证
  5. P1344-[USACO4.4]追查坏牛奶Pollutant Control【网络流,最小割】
  6. Codeforces Round #671 (Div. 2)
  7. halcon 17 cuda cudnn 深度学习环境搭建
  8. timespan怎么比较大小_万能小哥丨厨房墙砖哪种好?厨房墙砖怎么挑选?
  9. 适用于 ESXi 6.x 中的 OpenSLP 安全漏洞 (CVE-2019-5544) 的权宜措施 (76372)
  10. oracle 安装ora 27102,Oracle ora-27102 错误
  11. 敏友的【敏捷个人】有感(6): 我的改变从执行力分享开始
  12. 如何在html中选择wrap,jQuery - .wrap() 使用HTML包裹选取的元素
  13. BestCoder Round #67 (div.2) N*M bulbs
  14. Crossin先生的微信打飞机游戏(4)
  15. 支付系统详解:清结算系统
  16. markdown语言练习
  17. paper reading: Rob-GAN: Generator, Discriminator, and Adversarial Attacker
  18. 使用广和通L610模块搭配RT-Thread操作系统连接onenet云
  19. Qt之移动无边框窗体
  20. 简述计算机程序执行过程,计算机程序的执行过程

热门文章

  1. 从音乐中削弱鼓点声音而保留其他乐器音,应选用什么滤波器
  2. 一分钟了解什么是docker镜像
  3. Ubuntu下搜狗拼音输入法在不同窗口切换后总弹出拼音状态框的问题
  4. 爬虫实战—爬取房天下全国所有的楼盘并入库(附源码)
  5. 2012“粤嵌杯”芯片应用电子设计比赛成功举办
  6. 如何将繁体转化为简体?
  7. 怎么样做好自己的服务器防御
  8. 度量学习(Metric Learning)【AMSoftmax、Arcface】
  9. Android 蓝牙 IOS ANCE协议介绍 - 史上最详细
  10. [Andriod] fastboot 和 recovery 模式的区别