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

这一节,我们研究一下以前一直使用的辅助函数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系统给出的错误信息都会包含代码出错行的行号。比如,如果你视图编译如下这样的一个着色器:

    //片元着色器
    var FSHADER_SOURCE = "" +
        "varying vec4 v_Color;\n" +
        "void main(){" +
        "   gl.FragColor = v_Color;\n" +
        "}\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具有了一个强大的特性,那就是在绘制前准备多个程序对象,然后在绘制的时候根据需要切换对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值