《OpenGL ES应用开发实践指南:Android卷》—— 3.2 编译着色器

3.2 编译着色器

现在我们已经把着色器源代码从文件中读出来了,下一步就是编译每个着色器了。我们要创建一个新的辅助类,它可以创建新的OpenGL着色器对象、编译着色器代码并且返回代表那段着色器代码的着色器对象。一旦写出样板代码,在未来的项目中就可以重用了。
作为开始,创建一个名为ShaderHelper的新类,并在类中添加如下代码:

这些代码会作为着色器辅助类的基础。与以前一样,不要忘了把导入加进代码中;如果你在使用静态导入时碰到什么问题,请参考1.5节;在本书的剩余部分,我们会一直遵循这个样式。
在下一节中,我们会逐步构建compileShader()方法。

3.2.1 创建一个新的着色器对象

我们应该做的第一件事就是创建一个新的着色器对象,并且检查这个创建是否成功。在compileShader()中加入如下代码:

这里,用glCreateShader()调用创建了一个新的着色器对象,并把这个对象的ID存入变量shaderObjectId。这个type可以是代表顶点着色器的GL_VERTEX_SHADER,或者是代表片段着色器的GL_FRAGMENT_SHADER。剩下的代码也用同样的方式。
记住我们是如何创建对象并检查它是否有效的;这个模式将在OpenGL里广泛使用:
1.首先使用一个如glCreateShader()一样的调用创建一个对象,这个调用会返回一个整型值(integer)。
2.这个整型值就是OpenGL对象的引用。无论后面什么时候想要引用这个对象,就要把这个整型值传回OpenGL。
3.返回值0表示这个对象创建失败,它类似于Java代码中返回null值。
如果对象创建失败,就给调用代码返回0。为什么返回0而不是抛出一个异常呢?这是因为OpenGL内部实际不会抛出任何异常;相反,我们会得到返回值0,并且OpenGL通过glGetError()告诉我们这个错误,这个方法可以让我们询问OpenGL是不是某个API调用导致了错误。我们会一直遵从这个惯例。
附录B介绍了更多关于glGetError()的知识以及其他调试OpenGL代码的方法。

3.2.2 上传和编译着色器源代码

让我们加入如下代码把着色器源代码上传到着色器对象里:

一旦有了有效的着色器对象,就可以调用glShaderSource(shaderObjectId, shaderCode)上传源代码了。这个调用告诉OpenGL读入字符串shaderCode定义的源代码,并把它与shaderObjectId所引用的着色器对象关联起来。然后,可以调用glCompileShader(shaderObjectId)编译这个着色器:

这个调用告诉OpenGL编译先前上传到shaderObjectId的源代码。

3.2.3 取出编译状态

让我们加入如下代码检查OpenGL是否能成功地编译这个着色器:

为了检查编译是失败还是成功,首先要创建一个新的长度为1的int数组,称为compileStatus;然后调用glGetShaderiv(shaderObjectId, GLES20.GL_COMPILE_STATUS, compileStatus, 0)。这就告诉OpenGL读取与shaderObjectId关联的编译状态,并把它写入compileStatus的第0个元素。
这是Android平台上的OpenGL的另外一个通用模式。为了取出一个值,我们通常会使用一个长度为1的数组,并把这个数组传进一个OpenGL调用。在同一个调用中,我们告诉OpenGL把结果存进数组的第一个元素中。

3.2.4 取出着色器信息日志

当我们获得编译状态的时候,OpenGL只给出一个简单的是或否的回答。难道没兴趣知道发生了什么错误以及哪里出问题了吗?事实证明,我们可以通过调用glGetShaderInfoLog(shaderObjectId)获得一个可读的消息。如果OpenGL有什么关于着色器的有用内容,它就会把消息存到着色器的信息日志里。
让我们加入如下代码获取着色器信息日志:

我们把日志输出到Android的日志输出中,并把一切都封装在那个检查LoggerConfg.ON值的if语句里。通过把这个常量赋值为“false”,我们可以很容易地关闭这些日志。

3.2.5 验证编译状态并返回着色器对象ID

既然我们已经记录了着色器信息日志,就可以查看一下编译是否成功了:

我们所需要做的就是检查在3.2.3节那步的返回值,它是不是0。如果它是0,编译就失败了,这种情况下,我们就不再需要着色器对象了,因此告诉OpenGL把它删除并返回0给调用代码;如果编译成功,着色器对象就是有效的,我们就可以在代码中使用它了。
这就是为了编译一个着色器所需要的全部内容,让我们返回那个新的着色器对象ID:

3.2.6 在Renderer类中编译着色器

现在是时候充分利用我们刚刚写过的代码了。切换到AirHockeyRender.java,并在onSurfaceCreated()的结尾处加入如下代码:

让我们回顾一下本节所完成的工作。首先,我们创建了一个新类ShaderHelper,并加入了一个用来创建、编译新着色器对象的方法;我们也创建了LoggerConfig,一个用来在单一代码行打开或者关闭日志的类。
如果你再看一下ShaderHelper,就会发现我们实际上定义了三个方法。
compileShader():这个compileShader(type, shaderCode)方法使用了着色器源代码和类型;type可以是代表顶点着色器的GL_VERTEX_SHADER,或者是代表片段着色器的GL_FRAGMENT_SHADER。如果OpenGL能成功编译这个着色器,这个方法就会给调用代码返回着色器对象的ID,否则,它就会返回0。
compileVertexShader():这个compileVertexShader(shaderCode)方法是调用compileShader()的辅助方法,使用GL_VERTEX_SHADER作为着色器类型。
compileFragmentShader():这个compileVertexShder(shaderCode)方法也是调用compileShader()的辅助方法,它使用GL_FRAGMENT_SHADER作为着色器类型。
如你所见,这段代码的实质内容都在compileshader()中;其他两个方法就是使用GL_VERTEX_SHADER或GL_FRAGMENT_SHADER调用它。

时间: 2024-09-07 15:35:14

《OpenGL ES应用开发实践指南:Android卷》—— 3.2 编译着色器的相关文章

《OpenGL ES应用开发实践指南:Android卷》—— 导读

前 言 Android刚刚经历了一个难以置信的增长周期,全世界的消费者手里有超过7.5亿台设备,并且每天都有一百多万台设备被激活.和Apple一样,在每一台Android手机和平板上也有个集中的市场,称为Google Play.随着这个市场被安装到每台Android设备上,对任何一个梦想着发布自己的游戏或者壁纸的人来说,这都是个前所未有的机会. 在Android上,与Apple的iOS及许多其他移动平台一样,开发者可以使用一个跨平台应用编程接口创建二维或者三维图形,这个接口称为OpenGL:Op

《OpenGL ES应用开发实践指南:Android卷》—— 3.4 做最后的拼接

3.4 做最后的拼接 我们在前两章中用了很大篇幅为这个应用打下了很好的基础:我们学习了如何使用属性数组定义一个物体的结构,也学习了如何创建着色器.加载并编译它们,以及把它们链接起来形成一个OpenGL的程序. 现在是时候在这个基础上开始构建并把它们拼接起来了.在下面的几个步骤里,我们就要把这些部分拼在一起,并准备好把第一个版本的空气曲棍球桌子画到屏幕上. 3.4.1 验证OpenGL程序的对象 在开始使用OpenGL的程序之前,我们首先应该验证一下它,看看这个程序对于当前的OpenGL状态是不是

《OpenGL ES应用开发实践指南:Android卷》——第3章 编译着色器及在屏幕上绘图

第3章 编译着色器及在屏幕上绘图 本章会继续上一章开始的工作.作为本章的开发计划,我们首先加载并编译前面定义的着色器,然后把它们链接在一起放在OpenGL的一个程序里.我们接下来就可以用着色器程序在屏幕上绘制空气曲棍球桌子了.打开上一章启动的AirHockey1项目,并从那里开始.

《OpenGL ES应用开发实践指南:Android卷》—— 3.3 把着色器一起链接进OpenGL的程序

3.3 把着色器一起链接进OpenGL的程序 既然我们已经加载并编译了一个顶点着色器和一个片段着色器,下一步就是把它们绑定在一起放入一个单个的程序(program)里. 3.3.1 理解OpenGL的程序 简单来说,一个OpenGL程序就是把一个顶点着色器和一个片段着色器链接在一起变成单个对象.顶点着色器和片段着色器总是一起工作的.没有片段着色器,OpenGL就不知道怎么绘制那些组成每个点.直线和三角形的片段:如果没有顶点着色器,OpenGL就不知道在哪里绘制这些片段.我们知道顶点着色器计算屏幕

《OpenGL ES应用开发实践指南:Android卷》—— 2.7 小结

2.7 小结 本章用大量篇幅只介绍了如何定义数据,以及沿着OpenGL管道移动数据的着色器.让我们花点儿时间复习一下本章学到的那些关键概念:首先,我们学习了如何定义一个顶点属性数组,并把这个数组复制到本地内存里,以便OpenGL存取它.然后,我们写了一个顶点着色器和一个片段着色器:并了解到一个着色器只是可以运行在GPU上的一个特殊类型的程序.在下一章里,我们会在本章的基础上继续开发:在下一章的结尾处,我们就能看到空气曲棍球桌子了,也将为进一步的练习做好准备.我们将以学习如何读入和编译定义过的着色

《OpenGL ES应用开发实践指南:Android卷》—— 3.6 小结

3.6 小结 在最后把第一个版本的空气曲棍球桌子显示之前,我们不得不撰写了很多样例代码,好消息是,我们可以在后面的项目中重用这些代码.让我们花点儿时间回顾一下本章学过的内容:如何创建和编译着色器.顶点着色器和片段着色器总是一起工作,我们也学到了如何把它们链接到一起形成一个OpenGL的程序对象.如何把一个顶点着色器内部的属性变量与顶点属性数组关联起来.最后,我们把所有的内容放在一起,在屏幕上显示一些东西.既然我们已经把所有的知识点串连起来了,现在可能是时候回顾一下本章当时可能感觉不太清楚的部分了

《OpenGL ES应用开发实践指南:Android卷》—— 1.1 安装工具

1.1 安装工具 下面是开发Android OpenGL所需要的基本工具列表: 一台运行Windows.OS X或者Linux的计算机 Java开发包(JDK) Android软件开发包(SDK) 集成开发环境(IDE) 一个手机.平板电脑或者支持OpenGL ES 2.0的模拟器 你需要的第一个工具是一台适合开发的个人计算机:任何较新的计算机都可以,它需要能运行Windows.OS X或者Linux.在这台计算机上,你需要安装Java开发包,可以从Oracle的网站下载:当前,Google指定

《OpenGL ES应用开发实践指南:Android卷》—— 1.4 创建Renderer类

1.4 创建Renderer类 现在要定义一个渲染器,以便我们开始清空屏幕.让我们快速浏览一下渲染器接口定义的方法: onSurfaceCreated(GL10 glUnused, EGLConfig config) 当Surface被创建的时候,GLSurfaceView会调用这个方法:这发生在应用程序第一次运行的时候,并且,当设备被唤醒或者用户从其他activity切换回来时,这个方法也可能会被调用.在实践中,这意味着,当应用程序运行时,本方法可能会被调用多次. onSurfaceChang

《OpenGL ES应用开发实践指南:Android卷》—— 1.3 初始化OpenGL

1.3 初始化OpenGL 下一步就是使用一个特殊的类GLSurfaceView初始化OpenGL.GLSurfaceView会处理OpenGL初始化过程中比较基本的操作,如配置显示设备(display)以及在后台线程中渲染:渲染是在显示设备中一个称为"surface"的特定区域完成的,有时也称为视口(viewport).GLSurfaceView类也使得处理标准Android Activity生命周期变得容易了:在Android里,任何Activity都可以创建(create)和销毁