HTML WebGL着色器的编译和连接

关于HTML和canvas的处理,在之前的文章中也已经讲过了。基本上也不会有什么变化,在这里再说一下。

<html>
  <head>
    <title>WebGL TEST</title>
    <script src="script.js" type="text/javascript"></script>
    <script src="minMatrix.js" type="text/javascript"></script>

    <script id="vs" type="x-shader/x-vertex">
      attribute vec3 position;
      uniform   mat4 mvpMatrix;

      void main(void){
          gl_Position = mvpMatrix * vec4(position, 1.0);
      }
    </script>

    <script id="fs" type="x-shader/x-fragment">
      void main(void){
          gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
      }
    </script>
  </head>
  <body>
    <canvas id="canvas"></canvas>
  </body>
</html>

HTML代码就是上面这样,head标签中引用了两个javascript文件,一个是WebGL的处理文件script.js,另一个是本网站自己写的矩阵计算的库minMatrix.js。

顶点着色器的处理

接着,该出现顶点着色器和片段着色器的代码了。首先,先从type是x-shader/x-vertex的顶点着色器开始。下面是顶点着色器部分的代码。

attribute vec3 position;
uniform   mat4 mvpMatrix;
 
void main(void){
    gl_Position = mvpMatrix * vec4(position, 1.0);
}

这里用到了一个attribute变量和一个uniform变量。

变量position的类型是vec3,是一个3维向量。里面是顶点的位置,向量的三个元素分别是X,Y,Z坐标。

另一个uniform声明的变量mvpMatrix,类型是mat4,所以是一个4x4的方阵。是模型,视图,投影的各个变换矩阵结合后的坐标变换矩阵。

这次的顶点着色器,只是利用坐标变换矩阵来变换顶点的坐标位置,使用乘法运算。这时候,为了让position和矩阵相乘,使用vec4函数,先将其变成一个4维的向量,然后相乘,最后将计算结果代入到gl_Position,顶点着色器的处理结束。

片段着色器的处理

接着说片段着色器。

这次,绘制的模型是一个简单的三角形,先不进行着色,只是使用白色来填充。所以,片段着色器中的处理,就只是将白色信息传给gl_FragColor中。下面是片段着色器的代码。

void main(void){
    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}

编译代码,生成着色器

接着来看顶点着色器和片段着色器的编译。

编译也不需要什么特别的编译器,只需要调用WebGL内部的函数就可以进行编译了。准备一个函数,从着色器的编译,到实际着色器的生成这一连串的流程,都在这一个函数中来完成。下面是这个函数的代码。

function create_shader(id){
    // 用来保存着色器的变量
    var shader;
    // 根据id从HTML中获取指定的script标签
    var scriptElement = document.getElementById(id);
    // 如果指定的script标签不存在,则返回
    if(!scriptElement){return;}
    // 判断script标签的type属性
    switch(scriptElement.type){
        // 顶点着色器的时候
        case 'x-shader/x-vertex':
            shader = gl.createShader(gl.VERTEX_SHADER);
            break;
        // 片段着色器的时候
        case 'x-shader/x-fragment':
            shader = gl.createShader(gl.FRAGMENT_SHADER);
            break;
        default :
            return;
    }
    // 将标签中的代码分配给生成的着色器
    gl.shaderSource(shader, scriptElement.text);
    // 编译着色器
    gl.compileShader(shader);
    // 判断一下着色器是否编译成功
    if(gl.getShaderParameter(shader, gl.COMPILE_STATUS)){
        // 编译成功,则返回着色器
        return shader;
    }else{
        // 编译失败,弹出错误消息
        alert(gl.getShaderInfoLog(shader));
    }
}

使用这个函数的时候,需要传入script标签的id作为参数,函数中根据这个id来获取指定的标签。

生成着色器的时候,使用WebGL中的函数createShader。这个函数在生成着色器的时候,根据顶点着色器和片段着色器的不同来传入不同的参数。参数指定为gl.VERTEX_SHADER的时候会生成顶点着色器,参数指定为gl.FRAGMENT_SHADER的时候会生成片段着色器。

首先,将代码分配给生成的着色器的时候,使用的是shaderSource函数,参数有两个,第一个参数是着色器对象,第二个参数是着色器的代码。这时候,只是把着色器的代码分配给了着色器,还没有进行编译。编译的时候,使用的是compileShader函数,将着色器对象作为参数传给这个函数,就可以将着色器进行编译。

着色器是否编译成功,通过着色器中的参数可以判断,获取这个参数的时候,使用getShaderParameter函数,并使用WebGL中存在的常量COMPILE_STATUS作为参数。如果这时候的返回值是false,则表示因为什么原因导致编译失败了,要想查看原因的话,将着色器传入getShaderInfoLog函数中,就可以确认log了。

这个自定义函数,无论是顶点着色器还是片段着色器,都可以进行编译。实际上,顶点着色器和片段着色器的处理不同的地方就是createShader函数,其他地方是完全一样的。

程序对象的生成和连接

着色器生成之后,接着来生成程序对象。这里突然出现的程序对象,到底是个什么对象呢?

以前的文章中也稍微接触了一些,使用varying修饰符定义的变量,可以从顶点着色器向片段着色器中传递数据。其实,实现从一个着色器向另一个着色器传递数据的,不是别的,就是程序对象。程序对象是管理顶点着色器和片段着色器,或者WebGL程序和各个着色器之间进行数据的互相通信的重要的对象。

那么,生成程序对象,并把着色器传给程序对象,然后连接着色器,将这些处理函数化。

展开阅读全文

本文系作者在时代Java发表,未经许可,不得转载。

如有侵权,请联系nowjava@qq.com删除。

编辑于

关注时代Java

关注时代Java