• WebGL 初始化着色器


    目录

    前言

    初始化着色器的7个步骤

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

    gl.createShader()规范

    gl.deleteShader()规范

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

    gl.shaderSource()规范

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

    gl.compileShader()规范​编辑

    gl.getShaderParameter()规范

    gl.getShaderInfoLog()规范

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

    gl.createProgram()规范

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

    gl.attachShader()规范

    gl.detachShader()规范

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

    gl.getProgramPara-meters()规范

    gl.getProgramInfoLog()规范

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

    gl.usePorgram()规范

    initShaders()函数的内部流程

    initShaders()函数

    createProgram()函数

    loadShader()函数


    前言

    本章来研究一下以前一直使用的辅助函数initShaders()。以前的所有程序都使用了这个函数,它隐藏了建立和初始化着色器的细节。但如果你确实很想知道WebGL原生API是如何将字符串形式的GLSL ES代码编译为显卡中运行的着色器程序,那么这一节的内容将大大满足你的好奇心。

    初始化着色器的7个步骤

    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中,一个程序对象必须包含一个顶点着色器和一个片元着色器。 

    着色器对象和程序对象的关系图

     

    下面就来逐个讨论上述7个步骤。 

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

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

    gl.createShader()规范

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

    gl.deleteShader()规范

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

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

    通过gl.shaderSource()函数向着色器指定GLSL ES源代码。在JavaScript程序中,源代码以字符串的形式存储

    gl.shaderSource()规范

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

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

    gl.compileShader()规范

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

    gl.getShaderParameter()规范

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

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

    gl.getShaderInfoLog()规范

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

     代码的第2行出错了(应该是gl_而不是gl.),Chrome浏览器给出的编译错误信息如图9.11所示(cuon-utils.js将错误信息字符串打印在控制台上):

    可见,错误信息告诉我们:第2行的变量gl未被定义。

    cuon-utils.js:88表示这条错误是在JavaScript文件cuon-utils.js的第88行,即定义initShaders()函数之处检测到的,该函数调用了gl.getShaderInfolog()函数。 

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

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

    gl.createProgram()规范

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

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

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

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

    gl.attachShader()规范

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

    gl.detachShader()规范

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

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

    程序对象进行着色器连接操作,目的是保证:(1)顶点着色器和片元着色器的varying变量同名同类型,且一一对应;(2)顶点着色器对每个varying变量赋了值;(3)顶点着色器和片元着色器中的同名uniform变量也是同类型的(无需一一对应,即某些uniform变量可以出现在一个着色器中而不出现在另一个中);(4)着色器中的attribute变量、uniform变量和varying变量的个数没有超过着色器的上限,等等。 

    在着色器连接之后,应当检查是否连接成功。通过调用gl.getProgramPara-meters()函数来实现。

    gl.getProgramPara-meters()规范

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

    gl.getProgramInfoLog()规范

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

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

    gl.usePorgram()规范

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

    这样,建立和初始化着色器的任务就算完成了。如你所见,initShaders()函数隐藏了大量的细节,我们可以放心地使用该函数来创建和初始化着色器,而不必考虑这些细节。本质上,在该函数顺利执行后,顶点着色器和片元着色器就已经就位了,只需要调用gl.drawArrays()或gl.drawElements()来使整个WebGL系统运行起来。

    现在,你对上述诸多WebGL原生API函数已经有了不错的理解,下面来看一下cuon-utils.js中initShaders()函数的内部流程。

    initShaders()函数的内部流程

    initShaders()函数将调用createProgram()函数,后者负责创建一个连接好的程序对象;createProgram()函数则又会调用loadShader()函数,后者负责创建一个编译好的着色器对象;这3个函数被依次定义在cuon-utils.js文件中。initShaders()函数定义在该文件的顶部,如下所示

    initShaders()函数

    1. function initShaders(gl, vshader, fshader) {
    2. // 创建程序对象
    3. var program = createProgram(gl, vshader, fshader);
    4. if (!program) {
    5. console.log('Failed to create program');
    6. return false;
    7. }
    8. gl.useProgram(program);
    9. gl.program = program;
    10. return true;
    11. }

    initShaders()函数本身很简单,首先调用createProgram()函数创建一个连接好的程序对象,然后告诉WebGL系统来使用这个程序对象,最后将程序对象设为gl对象的program属性。

    createProgram()函数

     createProgram()函数通过调用loadShader()函数,创建顶点着色器和片元着色器的着色器对象(第31和32行)。loadShader()函数返回的着色器对象已经指定过源码并已经成功编译了。

    createProgram()函数自己负责创建程序对象(第38行),然后将前面创建的顶点着色器和片元着色器分配给程序对象(第44和45行)。

    接着,该函数连接程序对象(第48行),并检查是否连接成功(第51行)。如果连接成功,就返回程序对象(第60行)。

    最后看一下loadShader()函数,如下所示。该函数被createProgram()函数调用。

    loadShader()函数

    loadShader()函数首先创建了一个着色器对象(第72行),然后为该着色器对象指定源代码(第79行),并进行编译(第82行),接着检查编译是否成功(第85行),如果成功编译,没有出错,就返回着色器对象。

  • 相关阅读:
    MySQL基础
    全国护眼工程大会|2023山东哺光仪展|近视眼镜展|眼视光展
    Verilog语言实现设计交通灯控制器
    03--nginx架构实战
    测试进阶必备,这5款http接口自动化测试工具不要太香~
    记一次生产大对象及GC时长优化经验
    MyCat分片水平拆分
    Flask模板_循环结构
    基本分段存储管理方式(分段,段表,地址转换以及与分页管理对比)
    【Ant Design Pro】使用ant design pro做为你的开发模板(完结篇)上线部署项目
  • 原文地址:https://blog.csdn.net/dabaooooq/article/details/133145653