39 WebGL着色器和着色器程序对象:initShader()函数的作用

2018-03-04 03:17:26 人阅读

案例查看有问题?
全屏试试!

案例解析

 这一节,我们研究一下以前一直使用的辅助函数initShader()。

之前教程当中故意把这个函数中留到最后讲解,是为了确保学习initShaders()函数中的复杂细节时,对WebGL已经有了比较深入的了解。掌握这一部分内容不是必须的,直接使用initShaders()函数也能够编写出相当不错的WebGL程序。

initShaders()函数的作用是,编译GLSL ES代码,创建和初始化着色器供WebGL使用。具体分为以下7个步骤:

(1)创建着色器对象(gl.createShader())

(2)向着色器对象中填充着色器程序的源代码(gl.shaderSource())

(3)编译着色器(gl.compileShader())

(4)创建程序对象(gl.createProgram())

(5)为程序对象分配着色器(gl.attachShader())

(6)链接程序对象(gl.linkProgram())

(7)使用程序对象(gl.useProgram())

在这里出现了两种对象:着色器对象(shader object)和程序对象(program object)。

着色器对象:着色器对象管理一个顶点着色器或一个片元着色器。每一个着色器都有一个着色器对象。

程序对象:程序对象是管理着色器对象的容器。WebGL中,一个程序对象必须包含一个顶点着色器和一个片元着色器。

 

创建着色器对象(gl.createShader())

所有的着色器对象都必须通过调用gl.createShader()来创建

gl.createShader()函数根据传入的参数创建一个顶点着色器或者片元着色器。如果不需要这个着色器,可以使用gl.deleteShader()函数来删除着色器。

注意,如果着色器对象还在使用(也就是说已经使用gl.attachShader()函数使之附加在了程序对象上),那么gl.deleteShader()并不会立刻删除着色器,二十要等到程序对象不再使用该着色器后,才将其删除。

 

指定着色器对象的代码(gl.shaderSource())

通过gl.shaderSource()函数向着色器指定GLSL ES源代码。

 

编译着色器(gl.compileShader())

向着色器对象传入源代码之后,还需要对其进行编译才能够使用。GLSL ES语言和JavaScript不同而更接近C或C++,在使用之前需要编译成二进制的可执行格式,WebGL系统真正使用的是这种可执行格式。使用gl.compileShader()函数进行编译。注意,如果你通过调用gl.shaderSource(),用新的代码替换掉了着色器中旧的代码,WebGL系统中的用旧的代码编译出的可执行部分不会被自动替换,你需要手动地重新进行编译。

当调用gl.compileShader()函数时,如果着色器源代码中存在错误,那么就会出现编译错误。可以调用gl.getShaderParameter()函数来检查着色器的状态。

调用gl.getShaderParameter()并将参数pname指定为gl.COMPILE_STATUS,就可以检查着色器编译是否成功。

如果编译失败,gl.getShaderParameter()会返回false,WebGL系统会把编译错误的具体内容写入着色器的信息日志(information log),我们可以通过gl.getShaderInfoLog()来获取。

虽然日志信息的具体格式依赖于浏览器对WebGL的实现,但大多数WebGL系统给出的错误信息都会包含代码出错行的行号。比如,如果你视图编译如下这样的一个着色器:

 

[cpp] view plain copy
 
  1. //片元着色器  
  2. var FSHADER_SOURCE = "" +  
  3.     "varying vec4 v_Color;\n" +  
  4.     "void main(){" +  
  5.     "   gl.FragColor = v_Color;\n" +  
  6.     "}\n";  

代码的第二行出错了,在控制台显示的错误信息是:

 

 

创建程序对象(gl.createProgram())

程序对象包含了顶点着色器和片元着色器,可以调用gl.createProgram()来创建程序对象。事实上,之前使用程序对象,gl.getAttribLocation()和gl.getUniformLocation()函数的第一个函数,就是这个程序对象。

可以使用gl.deleteProgram()函数来删除程序对象。

一旦程序对象被创建之后,需要向程序附上两个着色器。

 

为程序对象分配着色器对象(gl.attachShader())

WebGL系统要运行起来,必须要有两个着色器:一个顶点着色器和一个片元着色器。可以使用gl.attachShader()函数为程序对象分配这两个着色器。

.

着色器在附给程序对象前,并不一定要为其制定代码或进行编译(也就是说,把空的着色器附给程序对象也是可以的)。类似的,可以使用gl.detachShader()函数来解除分配给程序对象的着色器。

 

连接程序对象(gl.linkProgram())

在为程序对象分配了两个着色器对象后,还需要将顶点着色器和片元着色器连接起来。使用gl.linkProgram()函数来进行这一步操作。

程序对象进行着色器连接操作,目的是保证:

(1)顶点着色器和片元着色器的varying变量同名同类型,且一一对应

(2)顶点着色器对每个varying变量赋了值

(3)顶点着色器和片元着色器中的同名uniform变量也是同类型的(无需一一对应,即某些uniform变量可以出现在一个着色器中而不出现在另一个中)

(4)着色器中的attribute变量、uniform变量和varying变量的个数没有超过着色器的上线,等等。

 

在着色器连接之后,应当检查是否连接成功。通过调用gl.getProgramPara-meters()函数来实现。(程序对象即使连接成功了,也有可能运行失败,比如没有为取样器分配纹理单元。这写错误是在运行阶段而不是连接阶段产生的。在运行阶段进行错误检查的性能开销很大,所以通常只在调试程序时这样做。)

如果程序已经成功连接,我们就得到了一个二进制的可执行模块供WebGL系统使用。如果连接失败了,也可以通过调用gl.getProgramInfoLog()从信息日志中获取连接出错信息。

 

告知WebGL系统所使用的程序对象(gl.useProgram())

最后,通过调用gl.useProgramUI告知WebGL系统绘制时使用哪个程序对象。

这个函数的存在使得WebGL具有了一个强大的特性,那就是在绘制前准备多个程序对象,然后在绘制的时候根据需要切换对象。

案例源代码


                                            
展开内容

联系我们

一个人的力量不如两个人的,两个人的力量不如一群人的。欢迎加入大家庭一起共同学习,共同进步。

查看更多