webgl 学习笔记


webgl 介绍

区别
  • webgl 是在传统的html文件的系统上,添加了 GLSLes(主要是编写着色器的配置)
  • webgl通过 js赋值给着色器数据,着色器主要是编写绘制图形需要的配置项

webgl 代码的主要部分和一些需要知道的概念介绍


学习新语言,需要了解语法和一些概念,下面是我总结的一些东西

着色器

  • 着色器是webgl 中的绘制图形的主要部分,每个着色器都是由顶点着色器 片元着色器组成
  • 顶点着色器 主要负责设置元素的位置信息,尺寸大小(还可以在着色器中添加一些逻辑判断,本质上着色器语法为 opengl 语法)
  • 片元着色器 用于设置物体范围内的颜色插值(以设置片元的颜色为标准,设置像素的颜色值)(还可以在着色器中添加一些逻辑判断,本质上着色器语法为 opengl 语法)
// 最简单的代码片段参考
// gl 中 定义变量的方法 attribute 和 uniform
// attribute 定义的是和 顶点 着色器 相关的变量
// uniform 定义的是 对于所有顶点 都相同的数据
// attribute(存储限定符) vec4(类型) a_Position(变量)
var VSHADER_SOURCE = 
  'attribute vec4 a_Position;\n' + // attribute 定义一个 数据类型为 vec4 变量 名称为 a_Position
  'void main() {\n' +
  '  gl_Position = a_Position;\n' + // 设置 位置坐标
  '  gl_PointSize = 10.0;\n' + // 设置尺寸大小
  '}\n'; 

// Fragment shader program
var FSHADER_SOURCE = 
  'void main() {\n' +
  '  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + // 设置片元着色器 填充颜色
  '}\n';

缓冲区对象

缓冲区作用

个人认为缓冲区对象的主要作用: 一次性提取数据,提高数据的处理速度

  • 创建缓冲区对象的具体步骤
1 创建缓冲区对象
2 将缓冲区分配给对应需要的缓冲区类型(有多个缓冲区类型)
3 给缓冲区对象中写入需要的数据
4 把缓冲区和着色器中的变量绑定(可以理解为赋值)
5 开启缓冲区的访问权限
  • 代码逻辑
// 将多个顶点的数据  保存在缓存区对象中
// 缓冲区传给 着色器
function initVertexBuffers(gl) {
  var vertices = new Float32Array([
    0.0, 0.5,   -0.5, -0.5,   0.5, -0.5
  ]); // 定义数据
  var n = 3; // 点的个数

  // 1 创建缓冲区对象
  var vertexBuffer = gl.createBuffer();
  if (!vertexBuffer) {
    console.log('Failed to create the buffer object');
    return -1;
  }

  // 2 将缓冲区对象绑定到目标
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);

  // 3 向缓冲区对象写入数据
  // 参数3 的作用是 表示程序将如何使用存储在缓冲区对象的数据 (做优化操作)
  gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

  // 获取变量
  var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
  if (a_Position < 0) {
    console.log('Failed to get the storage location of a_Position');
    return -1;
  }

  /**
   * 4 把缓冲区和着色器中的变量绑定(可以理解为赋值)
   * 将缓冲区对象 分配给(赋值给) a_Position 变量
   * 参数含义
   * 参数1 a_Position 赋值的变量
   * 参数2 指定缓冲区中每个顶点的分量个数
   * 参数3 指定数据类型 gl.FLOAT 是其中一种类型
   * 参数4 表明是否将非浮点型数据 归一化成 [0,1] 或 [-1,1]
   * 参数5 指定相邻的两个顶点间的字节数
   * 参数6 指定从第几个数据位置开始(偏移数)
  */
  gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);

  // 5 开启访问权限 
  //开启a_Position,为了使着色器能够访问 a_Position 变量
  // 连接 a_Position 变量 与分配给他的缓冲区对象
  gl.enableVertexAttribArray(a_Position);

  return n;
}

纹理映射

纹理贴图.png

纹理贴图的原理就是把把图片中对应的像素值(gaba) 映射到三维物体的表面

  • 创建纹理对象的具体步骤
1 创建纹理对象
2 开启纹理对象
3 对纹理单元绑定 纹理对象
4 设置纹理参数
5 配置纹理图像 --> 将纹理图像分配给纹理对象
6 将纹理单元赋值给着色器中的变量
7 着色器变量 调用 texture2D 取出片元对应的颜色值
  • 代码具体实现
// 1 创建一个纹理对象
var texture = gl.createTexture();   
// 2 开启 0号纹理单元
gl.activeTexture(gl.TEXTURE0);
// 3 向 EXTURE_2D 绑定纹理对象
gl.bindTexture(gl.TEXTURE_2D, texture);
// 4配置纹理参数 (设置样式)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
// 5 配置纹理图像 --> 将纹理图像分配给纹理对象
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
// 6 将 0 号纹理传递给着色器
 gl.uniform1i(u_Sampler, 0);

//  创建片元着色器
var FSHADER_SOURCE =
  // '#ifdef GL_ES\n' +
  'precision mediump float;\n' +
  // '#endif\n' +
  'uniform sampler2D u_Sampler;\n' + // 6 定义取样器 用来接收 纹理图
  'varying vec2 v_TexCoord;\n' +
  'void main() {\n' +
  '  gl_FragColor = texture2D(u_Sampler, v_TexCoord);\n' + // 6 提取纹理颜色值栅格化片元着色器
  '}\n';

矩阵对象

可以看做一系列变化关系的集合的统一表达方式(用于设置图形变换旋转、缩放,设置视图顶点坐标)

平移矩阵.png

  • 用于表示 顶点位置、颜色数据、物体变化数据、视图投影矩阵等
  • 多个矩阵可以合并

光线

用于模拟现实场景下的光照因素

  • 光线分类
1 平行光源(太阳)
2 点光源(灯泡)
3 环境光(物体周围均匀分布的光线)
  • 反射类型(光照下的物体表面颜色)


    漫反射.png

    环境光.png

    归一化.png
1 漫反射计算: 漫反射 = 入射光线颜色*物体表面颜色*cos(a) 
1 环境反射: 环境反射 = 入射光线颜色*物体表面颜色
  • 代码参考
// 顶点着色器
var VSHADER_SOURCE =
  'attribute vec4 a_Color;\n' + // 定义顶点的 颜色 变量
  'attribute vec4 a_Normal;\n' + // 定义法向量的变量、
  'uniform vec3 u_LightColor;\n' +     // 定义漫反射的光照颜色 变量
  'uniform vec3 u_LightDirection;\n' + // 定义漫反射的光照方向 变量
  'uniform vec3 u_AmbientLight;\n' +   // 定义环境光的变量
  'varying vec4 v_Color;\n' + // 要传给片元着色器的变量
  'void main() {\n' +
     // 计算 cos 值 = 漫反射的方向向量 * 法向量
  '  float nDotL = max(dot(u_LightDirection, a_Normal), 0.0);\n' +
     // 计算漫反射后的物体颜色
  '  vec3 diffuse = u_LightColor * a_Color.rgb * nDotL;\n' +
     // 计算环境光的物体颜色
  '  vec3 ambient = u_AmbientLight * a_Color.rgb;\n' +
     // 计算最终的物体颜色
  '  v_Color = vec4(diffuse + ambient, a_Color.a);\n' + 
  '}\n';

// 片元着色器
var FSHADER_SOURCE =
  '#ifdef GL_ES\n' +
  'precision mediump float;\n' + // 设置数据精度
  '#endif\n' +
  'varying vec4 v_Color;\n' +
  'void main() {\n' +
  '  gl_FragColor = v_Color;\n' +
  '}\n';

视图矩阵

由 观察者位置, 目标位置, 观察者上方向位置组成的 视图矩阵

视图矩阵原理

定义视图矩阵.png

  • 代码
// 包括 观察者(x y z)坐标,目标点(x y z)坐标,观察者上方向(x y z)坐标
viewMatrix.setLookAt(0.20, 0.25, 0.25, 0, 0, 0, 0, 1, 0); // 设置视图矩阵的api

可视空间矩阵

观察者可以看到区域范围,下面两个选择设置一个即可

  • 盒状可视空间


    盒状.png
// 参数依次为 近裁剪面左边界、近裁剪面右边界、近裁剪面上边界、近裁剪面下边界、近裁剪面位置、远裁剪面位置
projMatrix.setOrtho(-1.0, 1.0, -1.0, 1.0, g_near, g_far); // 设置盒装可视空间
  • 四棱锥/金字塔 可视空间(又叫透视投影矩阵)


    透视投影矩阵.png

    透视投影矩阵1.png
// 参数依次是  可视空间的夹角(观察者顶面和底面的夹角),近裁剪面的宽高比,近裁剪面位置,远裁剪面的位置
viewProjectionMatrix.setPerspective(30.0, canvas.width / canvas.height, 1.0, 100.0);// 设置透视投影矩阵(更真实,近处大 远处小)

隐藏面消除功能

gl.enable(gl.DEPTH_TEST);//开启隐藏面消除功能
gl.clear( gl.DEPTH_BUFFER_BIT); // 清除深度缓冲区

立体图形绘制

立方体绘制原理.png

webgl 没有提供绘制三维图形的方法,只能通过二维绘制的api来实现

  • 方法一 通过绘制多个三角形来拼成3d图形
    立方体6个面,每个面需要两个三角形来组成,所以需要的 定点数 =6(6个面) * 3(每个三角形需要3的顶点) * 2(每个面需要两个三角形)
    通过拼接三角形需要36个顶点
  • 方法二 通过 gl.drawElements() 方式索引顶点坐标来绘制立方体
    索引的方式来绘制立方体.png

    调用 gl.drawElements() 方法前需要做以下准备
    1创建 顶点坐标 并存放到 缓冲区中
    2 创建 顶点坐标对应的 索引坐标(通过索引坐标来获取顶点坐标来绘制3d图形)
    3 创建缓冲区 并绑定到 gl.ELEMENT_ARRAY_BUFFER 缓冲区类型上
    4 将 索引坐标 绑定到 gl.ELEMENT_ARRAY_BUFFER 缓冲区 上
    5 调用 gl.drawElements() 绘制3d图形
    下面是简单的代码逻辑

  // 1创建 顶点坐标 并存放到 缓冲区中
  // 将缓冲区对象 绑定到 gl.ARRAY_BUFFER
// Create a cube
  //    v6----- v5
  //   /|      /|
  //  v1------v0|
  //  | |     | |
  //  | |v7---|-|v4
  //  |/      |/
  //  v2------v3
  var verticesColors = new Float32Array([
    // 8个顶点位置 和 颜色值
     1.0,  1.0,  1.0,     1.0,  1.0,  1.0,  // v0 White
    -1.0,  1.0,  1.0,     1.0,  0.0,  1.0,  // v1 Magenta
    -1.0, -1.0,  1.0,     1.0,  0.0,  0.0,  // v2 Red
     1.0, -1.0,  1.0,     1.0,  1.0,  0.0,  // v3 Yellow
     1.0, -1.0, -1.0,     0.0,  1.0,  0.0,  // v4 Green
     1.0,  1.0, -1.0,     0.0,  1.0,  1.0,  // v5 Cyan
    -1.0,  1.0, -1.0,     0.0,  0.0,  1.0,  // v6 Blue
    -1.0, -1.0, -1.0,     0.0,  0.0,  0.0   // v7 Black
  ]);
  var vertexColorBuffer = gl.createBuffer();
  var indexBuffer = gl.createBuffer();
  if (!vertexColorBuffer || !indexBuffer) {
    return -1;
  }
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);
  var FSIZE = verticesColors.BYTES_PER_ELEMENT;
  var a_Position = gl.getAttribLocation(gl.program, 'a_Position'); // 获取 变量
  if(a_Position < 0) {
    console.log('Failed to get the storage location of a_Position');
    return -1;
  }
  gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE * 6, 0);
  gl.enableVertexAttribArray(a_Position);
  var a_Color = gl.getAttribLocation(gl.program, 'a_Color'); // 获取 变量
  if(a_Color < 0) {
    console.log('Failed to get the storage location of a_Color');
    return -1;
  }
  gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3);
  gl.enableVertexAttribArray(a_Color);

  // 2 创建 顶点坐标对应的 索引坐标(通过索引坐标来获取顶点坐标来绘制3d图形)
  // 这个就是索引坐标
  var indices = new Uint8Array([
    0, 1, 2,   0, 2, 3,    // front
    0, 3, 4,   0, 4, 5,    // right
    0, 5, 6,   0, 6, 1,    // up
    1, 6, 7,   1, 7, 2,    // left
    7, 4, 3,   7, 3, 2,    // down
    4, 7, 6,   4, 6, 5     // back
 ]);

  // 3 创建缓冲区 并绑定到 `gl.ELEMENT_ARRAY_BUFFER` 缓冲区类型上
  // 将缓冲区对象 绑定到 gl.ELEMENT_ARRAY_BUFFER(gl.ELEMENT_ARRAY_BUFFER 管理具有索引结构的三维数据模型)
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  // 4 将 索引坐标 绑定到 `gl.ELEMENT_ARRAY_BUFFER` 缓冲区 上
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
  // 5 绘制 立方体
  gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_BYTE, 0);

通过 gl.drawElements 方法绘制 需要的是 8个顶点,相比第一种减少了数据的创建

雾化

雾化公式.png

雾化因子.png

基本开发流程

  • 创建着色器对象
  • 创建canvas元素,并获取canvas 的 dom 元素
  • 通过canvas 获取 webgl 的绘图上下文对象
  • 初始化着色器,包括设置着色器的 定点位置,颜色值,视图投影矩阵,向量矩阵,纹理对象,光线对象值等的设置(是webgl中的最重要的开发部分)
  • 设置canvas背景色
  • 清除canvas 中的颜色缓冲区和深度缓冲区
  • webgl 绘图(包含许多绘图api)


代码案例


  • 案例1 通过js控制绘制元素的颜色,代码大致实现原理

主要注意的是 可以通过 js 来动态赋值着色器中的变量

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Hello Point (2)</title>
  </head>

  <body onload="main()">
    <canvas id="webgl" width="400" height="400">
    Please use a browser that supports "canvas"
    </canvas>

    <script src="../../lib/webgl-utils.js"></script>
    <script src="../../lib/webgl-debug.js"></script>
    <script src="../../lib/cuon-utils.js"></script>
    <script>
      // 顶点着色器
      // gl 中 定义变量的方法 attribute 和 uniform
      // attribute 定义的是和 顶点 着色器 相关的变量
      // uniform 定义的是 对于所有顶点 都相同的数据
      // attribute(存储限定符) vec4(类型) a_Position(变量)
      var VSHADER_SOURCE = 
        'attribute vec4 a_Position;\n' + // attribute 定义一个 数据类型为 vec4 变量 名称为 a_Position
        'void main() {\n' +
        '  gl_Position = a_Position;\n' + // 设置 位置坐标
        '  gl_PointSize = 10.0;\n' + // 设置尺寸大小
        '}\n'; 

      // 片元着色器
      var FSHADER_SOURCE = 
        'void main() {\n' +
        '  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + // 设置片元着色器 填充颜色
        '}\n';

      function main() {
        // 获取dom元素
        var canvas = document.getElementById('webgl');

        // 获取gl上下文对象
        var gl = getWebGLContext(canvas);
        if (!gl) {
          console.log('Failed to get the rendering context for WebGL');
          return;
        }

        // 初始化着色器 返回程序对象
        if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
          console.log('Failed to intialize shaders.');
          return;
        }

        // 拿到 gl 中定义的坐标 a_Position
        // 获取 attribute 存储变量的位置
        var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
        if (a_Position < 0) {
          console.log('Failed to get the storage location of a_Position');
          return;
        }

        // a_Position 赋值
        // 将 位置数据 传给 attribute 变量
        gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0);

        // 设置背景色
        gl.clearColor(0.0, 0.0, 0.0, 1.0);

        // 清空颜色缓冲区
        gl.clear(gl.COLOR_BUFFER_BIT);
          
        // 画图
        gl.drawArrays(gl.POINTS, 0, 1);
      }
    </script>
  </body>
</html>

  • 案例2 通过缓冲区对象来设置顶点位置 颜色信息
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Draw Multiple Points</title>
  </head>

  <body onload="main()">
    <canvas id="webgl" width="400" height="400">
    Please use a browser that supports "canvas"
    </canvas>

    <script src="../../lib/webgl-utils.js"></script>
    <script src="../../lib/webgl-debug.js"></script>
    <script src="../../lib/cuon-utils.js"></script>
    <script>
      // Vertex shader program
      //  缓冲区对象 一次性的向着色器传多个顶点数据 
      /**
       * 创建缓存区对象 步骤
       * 1 创建缓存区对象 gl.createBuffer()
       * 2 绑定缓冲区对象 gl.bindBuffer()
       * 3 将数据写入缓冲区对象 gl.bufferData()
       * 4 将缓冲区对象分配给一个attribute变量 gl.vertexAttribPointer()
       * 5 开启attribute变量 gl.enableVertexAttribArray()
       * */ 


      var VSHADER_SOURCE =
        'attribute vec4 a_Position;\n' +
        'void main() {\n' +
        '  gl_Position = a_Position;\n' +
        '  gl_PointSize = 10.0;\n' +
        '}\n';

      // Fragment shader program
      var FSHADER_SOURCE =
        'void main() {\n' +
        '  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' +
        '}\n';

      function main() {
        // Retrieve <canvas> element
        var canvas = document.getElementById('webgl');

        // Get the rendering context for WebGL
        var gl = getWebGLContext(canvas);
        if (!gl) {
          console.log('Failed to get the rendering context for WebGL');
          return;
        }

        // 初始化着色器
        if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
          console.log('Failed to intialize shaders.');
          return;
        }

        // 设置 顶点位置
        var n = initVertexBuffers(gl);
        if (n < 0) {
          console.log('Failed to set the positions of the vertices');
          return;
        }

        // 设置背景色
        gl.clearColor(0, 0, 0, 1);

        // 清空 颜色缓冲区
        gl.clear(gl.COLOR_BUFFER_BIT);

        // 画个点
        // 参数1: gl.POINTS 代表绘制点 还有其他的绘制方法(后面说)
        // 参数2: 0 表示从起始位置开始
        // 参数3: 1 绘制几个
        gl.drawArrays(gl.POINTS, 0, n);
      }

      // 创建顶点 缓冲区对象
      // 将多个顶点的数据  保存在缓存区对象中
      // 缓冲区传给 着色器
      function initVertexBuffers(gl) {
        var vertices = new Float32Array([
          0.0, 0.5,   -0.5, -0.5,   0.5, -0.5
        ]);
        var n = 3; // 点的个数

        // 创建缓冲区对象
        var vertexBuffer = gl.createBuffer();
        if (!vertexBuffer) {
          console.log('Failed to create the buffer object');
          return -1;
        }

        // 将缓冲区对象绑定到目标
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
        // 向缓冲区对象写入数据
        // 参数3 的作用是 表示程序将如何使用存储在缓冲区对象的数据 (做优化操作)
        gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

        // 获取变量
        var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
        if (a_Position < 0) {
          console.log('Failed to get the storage location of a_Position');
          return -1;
        }
        /**
         * 将缓冲区对象 分配给(赋值给) a_Position 变量
         * 参数含义
         * 参数1 a_Position 赋值的变量
         * 参数2 指定缓冲区中每个顶点的分量个数
         * 参数3 指定数据类型 gl.FLOAT 是其中一种类型
         * 参数4 表明是否将非浮点型数据 归一化成 [0,1] 或 [-1,1]
         * 参数5 指定相邻的两个顶点间的字节数
         * 参数6 指定从第几个数据位置开始(偏移数)
        */
        gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);

        // 开启a_Position,为了使着色器能够访问 a_Position 变量
        // 连接 a_Position 变量 与分配给他的缓冲区对象
        gl.enableVertexAttribArray(a_Position);

        return n;
      }

    </script>
  </body>
</html>

  • 案例3 模型矩阵 视图矩阵 投影矩阵开启深度缓冲区
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Perspective Projection</title>
  </head>

  <body onload="main()">
    <canvas id="webgl" width="400" height="400">
    Please use a browser that supports "canvas"
    </canvas>

    <script src="../../lib/webgl-utils.js"></script>
    <script src="../../lib/webgl-debug.js"></script>
    <script src="../../lib/cuon-utils.js"></script>
    <script src="../../lib/cuon-matrix.js"></script>
    <script>
      // 顶点着色器
      var VSHADER_SOURCE =
        'attribute vec4 a_Position;\n' +
        'attribute vec4 a_Color;\n' +
        'uniform mat4 u_ViewMatrix;\n' +
        'uniform mat4 u_ProjMatrix;\n' +
        'varying vec4 v_Color;\n' +
        'void main() {\n' +
        '  gl_Position = u_ProjMatrix * u_ViewMatrix * a_Position;\n' +
        '  v_Color = a_Color;\n' +
        '}\n';

      // 片元着色器
      var FSHADER_SOURCE =
        '#ifdef GL_ES\n' +
        'precision mediump float;\n' +
        '#endif\n' +
        'varying vec4 v_Color;\n' +
        'void main() {\n' +
        '  gl_FragColor = v_Color;\n' +
        '}\n';

      function main() {
        // 获取dom 元素
        var canvas = document.getElementById('webgl');

        // 获取 gl 上线文
        var gl = getWebGLContext(canvas);
        if (!gl) {
          console.log('Failed to get the rendering context for WebGL');
          return;
        }

        // 初始化着色器
        if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
          console.log('Failed to intialize shaders.');
          return;
        }

        // 初始化 着色器 函数调用
        var n = initVertexBuffers(gl);
        if (n < 0) {
          console.log('Failed to set the vertex information');
          return;
        }

        // 设置背景颜色
        gl.clearColor(0, 0, 0, 1);

        // 获取矩阵变量
        var u_ViewMatrix = gl.getUniformLocation(gl.program, 'u_ViewMatrix');
        var u_ProjMatrix = gl.getUniformLocation(gl.program, 'u_ProjMatrix');
        if (!u_ViewMatrix || !u_ProjMatrix) { 
          console.log('Failed to get the storage location of u_ViewMatrix and/or u_ProjMatrix');
          return;
        }

        var viewMatrix = new Matrix4(); // 创建 矩阵对象
        var projMatrix = new Matrix4();  // 创建 矩阵对象

        // 计算视图矩阵
        viewMatrix.setLookAt(0, 0, 5, 0, 0, -100, 0, 1, 0);
        projMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100); // 设置 投影矩阵 数据
        // 赋值
        gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
        gl.uniformMatrix4fv(u_ProjMatrix, false, projMatrix.elements);

        // 清空颜色缓冲区
        gl.clear(gl.COLOR_BUFFER_BIT);

        // 画图
        gl.drawArrays(gl.TRIANGLES, 0, n);
      }

      function initVertexBuffers(gl) {
        var verticesColors = new Float32Array([
          // 位置 颜色数据
          0.75,  1.0,  -4.0,  0.4,  1.0,  0.4, // 后面的
          0.25, -1.0,  -4.0,  0.4,  1.0,  0.4,
          1.25, -1.0,  -4.0,  1.0,  0.4,  0.4, 

          0.75,  1.0,  -2.0,  1.0,  1.0,  0.4, // 中间的
          0.25, -1.0,  -2.0,  1.0,  1.0,  0.4,
          1.25, -1.0,  -2.0,  1.0,  0.4,  0.4, 

          0.75,  1.0,   0.0,  0.4,  0.4,  1.0,  // 前面的
          0.25, -1.0,   0.0,  0.4,  0.4,  1.0,
          1.25, -1.0,   0.0,  1.0,  0.4,  0.4, 

          // Three triangles on the left side
        -0.75,  1.0,  -4.0,  0.4,  1.0,  0.4, // 后面的
        -1.25, -1.0,  -4.0,  0.4,  1.0,  0.4,
        -0.25, -1.0,  -4.0,  1.0,  0.4,  0.4, 

        -0.75,  1.0,  -2.0,  1.0,  1.0,  0.4, // 中间的
        -1.25, -1.0,  -2.0,  1.0,  1.0,  0.4,
        -0.25, -1.0,  -2.0,  1.0,  0.4,  0.4, 

        -0.75,  1.0,   0.0,  0.4,  0.4,  1.0,  // 前面的 
        -1.25, -1.0,   0.0,  0.4,  0.4,  1.0,
        -0.25, -1.0,   0.0,  1.0,  0.4,  0.4, 
        ]);
        var n = 18; // Three vertices per triangle * 6

        // 创建 缓冲区对象
        var vertexColorbuffer = gl.createBuffer();  
        if (!vertexColorbuffer) {
          console.log('Failed to create the buffer object');
          return -1;
        }

        // 将缓冲区对象 绑定到 gl.ARRAY_BUFFER
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorbuffer);
        gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);

        var FSIZE = verticesColors.BYTES_PER_ELEMENT;

        // 获取 变量
        var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
        if(a_Position < 0) {
          console.log('Failed to get the storage location of a_Position');
          return -1;
        }
        gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE * 6, 0);
        gl.enableVertexAttribArray(a_Position);

        // 获取 变量
        var a_Color = gl.getAttribLocation(gl.program, 'a_Color');
        if(a_Color < 0) {
          console.log('Failed to get the storage location of a_Color');
          return -1;
        }

        gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3);
        gl.enableVertexAttribArray(a_Color);

        return n;
      }

    </script>
  </body>
</html>

  • 案例4 添加纹理贴图
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Draw quad with texture</title>
  </head>

  <body onload="main()">
    <canvas id="webgl" width="400" height="400">
    Please use a browser that supports "canvas"
    </canvas>

    <script src="../../lib/webgl-utils.js"></script>
    <script src="../../lib/webgl-debug.js"></script>
    <script src="../../lib/cuon-utils.js"></script>
    <script src="../../lib/cuon-matrix.js"></script>
    <script>
      // TexturedQuad.js (c) 2012 matsuda and kanda
      // 创建顶点着色器
      var VSHADER_SOURCE =
        'attribute vec4 a_Position;\n' +
        'attribute vec2 a_TexCoord;\n' +
        'varying vec2 v_TexCoord;\n' +
        'void main() {\n' +
        '  gl_Position = a_Position;\n' +
        '  v_TexCoord = a_TexCoord;\n' +
        '}\n';

      //  创建片元着色器
      var FSHADER_SOURCE =
        // '#ifdef GL_ES\n' +
        'precision mediump float;\n' +
        // '#endif\n' +
        'uniform sampler2D u_Sampler;\n' + // 定义取样器 用来接收 纹理图
        'varying vec2 v_TexCoord;\n' +
        'void main() {\n' +
        '  gl_FragColor = texture2D(u_Sampler, v_TexCoord);\n' +
        '}\n';

      function main() {
        // 获取dom 元素
        var canvas = document.getElementById('webgl');

        // 获取 gl 上下文对象
        var gl = getWebGLContext(canvas);
        if (!gl) {
          console.log('Failed to get the rendering context for WebGL');
          return;
        }

        // 初始化对象
        if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
          console.log('Failed to intialize shaders.');
          return;
        }

        // 函数调用
        var n = initVertexBuffers(gl);
        if (n < 0) {
          console.log('Failed to set the vertex information');
          return;
        }

        // 设置颜色
        gl.clearColor(0.0, 0.0, 0.0, 1.0);

        // 设置纹理
        if (!initTextures(gl, n)) {
          console.log('Failed to intialize the texture.');
          return;
        }
      }

      function initVertexBuffers(gl) { // 将纹理坐标和顶点坐标对应上
        var verticesTexCoords = new Float32Array([
          // 顶点坐标, 纹理坐标
          -0.5,  0.5,   0.0, 1.0,
          -0.5, -0.5,   0.0, 0.0,
          0.5,  0.5,   1.0, 1.0,
          0.5, -0.5,   1.0, 0.0,
        ]);
        var n = 4; // The number of vertices

        // 创建 缓冲区对象
        var vertexTexCoordBuffer = gl.createBuffer();
        if (!vertexTexCoordBuffer) {
          console.log('Failed to create the buffer object');
          return -1;
        }

        // 将缓冲区对象 分配给 gl.ARRAY_BUFFER
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexCoordBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW);

        var FSIZE = verticesTexCoords.BYTES_PER_ELEMENT;// 每个数据所占的字节
        //获取着色器 对象 变量
        var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
        if (a_Position < 0) {
          console.log('Failed to get the storage location of a_Position');
          return -1;
        }
        gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 4, 0);
        gl.enableVertexAttribArray(a_Position);  // 开启缓冲区对象访问权限 使 着色器能访问到变量的值

        // 获取着色器 对象 变量
        var a_TexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord');
        if (a_TexCoord < 0) {
          console.log('Failed to get the storage location of a_TexCoord');
          return -1;
        }
        // 赋值
        gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, FSIZE * 4, FSIZE * 2);
        gl.enableVertexAttribArray(a_TexCoord);  // 开启缓冲区对象访问权限 使 着色器能访问到变量的值

        return n;
      }

      function initTextures(gl, n) { // 配置和加载纹理
        var texture = gl.createTexture();   // 创建一个纹理对象
        if (!texture) {
          console.log('Failed to create the texture object');
          return false;
        }

        // 获取着色器 对象 变量(取样器)
        var u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler');
        if (!u_Sampler) {
          console.log('Failed to get the storage location of u_Sampler');
          return false;
        }
        var image = new Image();  // 创建 图片对象
        if (!image) {
          console.log('Failed to create the image object');
          return false;
        }
        // 注册图像加载事件响应函数
        image.onload = function(){ loadTexture(gl, n, texture, u_Sampler, image); };
        // 图片路径(赋值后 浏览器开始加载)
        image.src = '../../resources/sky.JPG';

        return true;
      }

      function loadTexture(gl, n, texture, u_Sampler, image) { // 对纹理开始处理
        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); // 对纹理图像进行y轴反转
        // 开启 0号纹理单元
        gl.activeTexture(gl.TEXTURE0);
        // 向 EXTURE_2D 绑定纹理对象
        gl.bindTexture(gl.TEXTURE_2D, texture);

        // 配置纹理参数 (设置样式)
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        // 配置纹理图像 --> 将纹理图像分配给纹理对象
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
        
        // 将 0 号纹理传递给着色器
        gl.uniform1i(u_Sampler, 0);
        
        gl.clear(gl.COLOR_BUFFER_BIT);   // 清空颜色缓冲区

        gl.drawArrays(gl.TRIANGLE_STRIP, 0, n); // 绘制矩形
      }

    </script>
  </body>
</html>

  • 案例5 立方体绘制
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Hello cube</title>
  </head>

  <body onload="main()">
    <canvas id="webgl" width="400" height="400">
    Please use a browser that supports "canvas"
    </canvas>

    <script src="../../lib/webgl-utils.js"></script>
    <script src="../../lib/webgl-debug.js"></script>
    <script src="../../lib/cuon-utils.js"></script>
    <script src="../../lib/cuon-matrix.js"></script>
    <script>
      // 顶点着色器
      var VSHADER_SOURCE =
        'attribute vec4 a_Position;\n' +
        'attribute vec4 a_Color;\n' +
        'uniform mat4 u_MvpMatrix;\n' +
        'varying vec4 v_Color;\n' +
        'void main() {\n' +
        '  gl_Position = u_MvpMatrix * a_Position;\n' +
        '  v_Color = a_Color;\n' +
        '}\n';

      // 片元着色器
      var FSHADER_SOURCE =
        '#ifdef GL_ES\n' +
        'precision mediump float;\n' +
        '#endif\n' +
        'varying vec4 v_Color;\n' +
        'void main() {\n' +
        '  gl_FragColor = v_Color;\n' +
        '}\n';

      function main() {
        // 获取dom 元素
        var canvas = document.getElementById('webgl');

        // 获取 gl 上线文
        var gl = getWebGLContext(canvas);
        if (!gl) {
          console.log('Failed to get the rendering context for WebGL');
          return;
        }

        // 初始化着色器
        if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
          console.log('Failed to intialize shaders.');
          return;
        }

        // 初始化 着色器 函数调用
        var n = initVertexBuffers(gl);
        if (n < 0) {
          console.log('Failed to set the vertex information');
          return;
        }

        // 设置背景颜色
        gl.clearColor(0.0, 0.0, 0.0, 1.0);
        gl.enable(gl.DEPTH_TEST); // 开启隐藏面消除功能

        // 获取着色器 变量
        var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
        if (!u_MvpMatrix) { 
          console.log('Failed to get the storage location of u_MvpMatrix');
          return;
        }

        // 创建矩阵对象
        var mvpMatrix = new Matrix4();
        mvpMatrix.setPerspective(30, 1, 1, 100); // 设置 投影矩阵
        mvpMatrix.lookAt(3, 3, 7, 0, 0, 0, 0, 1, 0);// 视图模型矩阵

        // 赋值
        gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);

        // 清空颜色缓冲区 和 深度缓冲区
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

        // 绘制 立方体
        gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);
      }

      function initVertexBuffers(gl) {
        // Create a cube
        //    v6----- v5
        //   /|      /|
        //  v1------v0|
        //  | |     | |
        //  | |v7---|-|v4
        //  |/      |/
        //  v2------v3
        var verticesColors = new Float32Array([
          // 8个顶点位置 和 颜色值
          1.0,  1.0,  1.0,     1.0,  1.0,  1.0,  // v0 White
          -1.0,  1.0,  1.0,     1.0,  0.0,  1.0,  // v1 Magenta
          -1.0, -1.0,  1.0,     1.0,  0.0,  0.0,  // v2 Red
          1.0, -1.0,  1.0,     1.0,  1.0,  0.0,  // v3 Yellow
          1.0, -1.0, -1.0,     0.0,  1.0,  0.0,  // v4 Green
          1.0,  1.0, -1.0,     0.0,  1.0,  1.0,  // v5 Cyan
          -1.0,  1.0, -1.0,     0.0,  0.0,  1.0,  // v6 Blue
          -1.0, -1.0, -1.0,     0.0,  0.0,  0.0   // v7 Black
        ]);

        // 6个面
        var indices = new Uint8Array([
          0, 1, 2,   0, 2, 3,    // front
          0, 3, 4,   0, 4, 5,    // right
          0, 5, 6,   0, 6, 1,    // up
          1, 6, 7,   1, 7, 2,    // left
          7, 4, 3,   7, 3, 2,    // down
          4, 7, 6,   4, 6, 5     // back
      ]);

        // 创建缓冲区对象
        var vertexColorBuffer = gl.createBuffer();
        var indexBuffer = gl.createBuffer();
        if (!vertexColorBuffer || !indexBuffer) {
          return -1;
        }

        // 将缓冲区对象 绑定到 gl.ARRAY_BUFFER
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);

        var FSIZE = verticesColors.BYTES_PER_ELEMENT;
        // 获取 变量
        var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
        if(a_Position < 0) {
          console.log('Failed to get the storage location of a_Position');
          return -1;
        }
        gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE * 6, 0);
        gl.enableVertexAttribArray(a_Position);
        // 获取 变量
        var a_Color = gl.getAttribLocation(gl.program, 'a_Color');
        if(a_Color < 0) {
          console.log('Failed to get the storage location of a_Color');
          return -1;
        }
        gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3);
        gl.enableVertexAttribArray(a_Color);

        // 将缓冲区对象 绑定到 gl.ELEMENT_ARRAY_BUFFER(gl.ELEMENT_ARRAY_BUFFER 管理具有索引结构的三维数据模型)
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);

        return indices.length;
      }

    </script>
  </body>
</html>

  • 案例6 光照
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Point lighted cube (with animation)</title>
  </head>

  <body onload="main()">
    <canvas id="webgl" width="400" height="400">
    Please use a browser that supports "canvas"
    </canvas>

    <script src="../../lib/webgl-utils.js"></script>
    <script src="../../lib/webgl-debug.js"></script>
    <script src="../../lib/cuon-utils.js"></script>
    <script src="../../lib/cuon-matrix.js"></script>
    <script>
      // 顶点着色器
      var VSHADER_SOURCE =
        'attribute vec4 a_Position;\n' + // 顶点位置
        'attribute vec4 a_Color;\n' + // 顶点颜色
        'attribute vec4 a_Normal;\n' + // 法向量
        'uniform mat4 u_MvpMatrix;\n' + // 视图 投影矩阵
        'uniform mat4 u_ModelMatrix;\n' +    // 模型矩阵
        'uniform mat4 u_NormalMatrix;\n' +   // 用来变换 法向量的矩阵
        'uniform vec3 u_LightColor;\n' +     // 光线颜色
        'uniform vec3 u_LightPosition;\n' +  // 光线方向
        'uniform vec3 u_AmbientLight;\n' +   // 环境光
        'varying vec4 v_Color;\n' + // 传给片元着色器的
        'void main() {\n' +
        '  gl_Position = u_MvpMatrix * a_Position;\n' +
          // 计算位置变换后的 方向量 并 归一化
        '  vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));\n' +
          // 计算顶点坐标
        '  vec4 vertexPosition = u_ModelMatrix * a_Position;\n' +
          // 计算光线方向并且归一化 (点光源的光线方向 = 点光线坐标 - 顶点坐标)
        '  vec3 lightDirection = normalize(u_LightPosition - vec3(vertexPosition));\n' +
          // 计算 cos 值 = 漫反射的方向向量 * 法向量
        '  float nDotL = max(dot(normal, lightDirection), 0.0);\n' +
          //  计算漫反射后的物体颜色
        '  vec3 diffuse = u_LightColor * a_Color.rgb * nDotL;\n' +
          // 计算环境光的物体颜色
        '  vec3 ambient = u_AmbientLight * a_Color.rgb;\n' +
          // 计算最终的物体颜色
        '  v_Color = vec4(diffuse + ambient, a_Color.a);\n' + 
        '}\n';

      // Fragment shader program
      var FSHADER_SOURCE =
        '#ifdef GL_ES\n' +
        'precision mediump float;\n' +
        '#endif\n' +
        'varying vec4 v_Color;\n' +
        'void main() {\n' +
        '  gl_FragColor = v_Color;\n' +
        '}\n';

      function main() {
        // 获取dom元素
        var canvas = document.getElementById('webgl');

        // 获取gl 上下文
        var gl = getWebGLContext(canvas);
        if (!gl) {
          console.log('Failed to get the rendering context for WebGL');
          return;
        }

        // 初始化着色器
        if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
          console.log('Failed to intialize shaders.');
          return;
        }

        // 着色器赋值
        var n = initVertexBuffers(gl);
        if (n < 0) {
          console.log('Failed to set the vertex information');
          return;
        }

        // 设置背景颜色
        gl.clearColor(0.0, 0.0, 0.0, 1.0);
        gl.enable(gl.DEPTH_TEST); // 开启隐藏面消除功能

        // 获取着色器 变量
        var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
        var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
        var u_NormalMatrix = gl.getUniformLocation(gl.program, 'u_NormalMatrix');
        var u_LightColor = gl.getUniformLocation(gl.program, 'u_LightColor');
        var u_LightPosition = gl.getUniformLocation(gl.program, 'u_LightPosition');
        var u_AmbientLight = gl.getUniformLocation(gl.program, 'u_AmbientLight');
        if (!u_MvpMatrix || !u_NormalMatrix || !u_LightColor || !u_LightPosition || !u_AmbientLight) { 
          console.log('Failed to get the storage location');
          return;
        }

        var vpMatrix = new Matrix4();   // 视图 投影矩阵
        vpMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);// 设置设置透视投影矩阵
        vpMatrix.lookAt(6, 6, 14, 0, 0, 0, 0, 1, 0);// 设置视图模型矩阵

        // 设置光颜色
        gl.uniform3f(u_LightColor, 1.0, 1.0, 1.0);
        // 设置光方向
        gl.uniform3f(u_LightPosition, 2.3, 4.0, 3.5);
        // 设置环境光
        gl.uniform3f(u_AmbientLight, 0.2, 0.2, 0.2);

        var currentAngle = 0.0;  // 当前角度
        var modelMatrix = new Matrix4();  // 模型变换矩阵
        var mvpMatrix = new Matrix4();    // 视图 投影矩阵
        var normalMatrix = new Matrix4(); // 计算法向量的矩阵

        var tick = function() {
          currentAngle = animate(currentAngle);  // 更新旋转角度

          // 设置旋转角度
          modelMatrix.setRotate(currentAngle, 0, 1, 0); // Rotate around the y-axis
          // 更新到 着色器 上
          gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);

          // 视图 投影矩阵 更新 并 赋值到 着色器上
          mvpMatrix.set(vpMatrix).multiply(modelMatrix);
          gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);

          // 根据模型矩阵 计算出用来转化法向量的矩阵
        // 将 normalMatrix 设置为 modelMatrix 的 逆转置矩阵,(用于计算 物体 旋转或平移变换后的 法向量)
          normalMatrix.setInverseOf(modelMatrix);
          normalMatrix.transpose();// 对自身进行转置操作,并将自身设为转置后的结果(用于计算 物体 旋转或平移变换后的 法向量)
          gl.uniformMatrix4fv(u_NormalMatrix, false, normalMatrix.elements);

          // 清空颜色缓冲区 和 深度缓冲区
          gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

          // 画图
          gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);

          requestAnimationFrame(tick, canvas); // 循环调用
        };
        tick();
      }

      function initVertexBuffers(gl) {
        // Create a cube
        //    v6----- v5
        //   /|      /|
        //  v1------v0|
        //  | |     | |
        //  | |v7---|-|v4
        //  |/      |/
        //  v2------v3
        // 顶点
        var vertices = new Float32Array([
          2.0, 2.0, 2.0,  -2.0, 2.0, 2.0,  -2.0,-2.0, 2.0,   2.0,-2.0, 2.0, // v0-v1-v2-v3 front
          2.0, 2.0, 2.0,   2.0,-2.0, 2.0,   2.0,-2.0,-2.0,   2.0, 2.0,-2.0, // v0-v3-v4-v5 right
          2.0, 2.0, 2.0,   2.0, 2.0,-2.0,  -2.0, 2.0,-2.0,  -2.0, 2.0, 2.0, // v0-v5-v6-v1 up
          -2.0, 2.0, 2.0,  -2.0, 2.0,-2.0,  -2.0,-2.0,-2.0,  -2.0,-2.0, 2.0, // v1-v6-v7-v2 left
          -2.0,-2.0,-2.0,   2.0,-2.0,-2.0,   2.0,-2.0, 2.0,  -2.0,-2.0, 2.0, // v7-v4-v3-v2 down
          2.0,-2.0,-2.0,  -2.0,-2.0,-2.0,  -2.0, 2.0,-2.0,   2.0, 2.0,-2.0  // v4-v7-v6-v5 back
        ]);

        // 颜色
        var colors = new Float32Array([
          1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v1-v2-v3 front
          1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v3-v4-v5 right
          1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v5-v6-v1 up
          1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v1-v6-v7-v2 left
          1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v7-v4-v3-v2 down
          1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0     // v4-v7-v6-v5 back
      ]);

        // 法向量
        var normals = new Float32Array([
          0.0, 0.0, 1.0,   0.0, 0.0, 1.0,   0.0, 0.0, 1.0,   0.0, 0.0, 1.0,  // v0-v1-v2-v3 front
          1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,  // v0-v3-v4-v5 right
          0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,  // v0-v5-v6-v1 up
        -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  // v1-v6-v7-v2 left
          0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,  // v7-v4-v3-v2 down
          0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0   // v4-v7-v6-v5 back
        ]);

        // 索引值
        var indices = new Uint8Array([
          0, 1, 2,   0, 2, 3,    // front
          4, 5, 6,   4, 6, 7,    // right
          8, 9,10,   8,10,11,    // up
          12,13,14,  12,14,15,    // left
          16,17,18,  16,18,19,    // down
          20,21,22,  20,22,23     // back
      ]);

        // 给着色器变量 赋值
        if (!initArrayBuffer(gl, 'a_Position', vertices, 3, gl.FLOAT)) return -1;
        if (!initArrayBuffer(gl, 'a_Color', colors, 3, gl.FLOAT)) return -1;
        if (!initArrayBuffer(gl, 'a_Normal', normals, 3, gl.FLOAT)) return -1;

        // 缓冲区对象置空
        gl.bindBuffer(gl.ARRAY_BUFFER, null);

        // 创建缓冲区对象
        var indexBuffer = gl.createBuffer();
        if (!indexBuffer) {
          console.log('Failed to create the buffer object');
          return false;
        }
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);// 将缓冲区对象 绑定到 gl.ELEMENT_ARRAY_BUFFER(gl.ELEMENT_ARRAY_BUFFER 管理具有索引结构的三维数据模型)
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);

        return indices.length;
      }

      function initArrayBuffer(gl, attribute, data, num, type) {
        // 创建一个 缓冲区对象
        var buffer = gl.createBuffer();
        if (!buffer) {
          console.log('Failed to create the buffer object');
          return false;
        }
        // 将缓冲区对象分配给 gl.ARRAY_BUFFER
        gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
        gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
        // 获取变量
        var a_attribute = gl.getAttribLocation(gl.program, attribute);
        if (a_attribute < 0) {
          console.log('Failed to get the storage location of ' + attribute);
          return false;
        }
        gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);
        // 开启访问权限
        gl.enableVertexAttribArray(a_attribute);

        return true;
      }

      // 每次旋转的角度值
      var ANGLE_STEP = 30.0;
      // 上次时间值
      var g_last = Date.now();
      function animate(angle) {
        // 获取当前时间
        var now = Date.now();
        var elapsed = now - g_last;
        g_last = now;
        // 计算应该旋转的角度
        var newAngle = angle + (ANGLE_STEP * elapsed) / 1000.0;
        return newAngle %= 360;
      }

    </script>
  </body>
</html>

  • 雾化效果
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Fog</title>
  </head>

  <body onload="main()">
    <canvas id="webgl" width="400" height="400">
    Please use a browser that supports "canvas"
    </canvas>
    <p>&uarr;&darr;: Increase/decrease the fog distance</p></p>

    <script src="../../lib/webgl-utils.js"></script>
    <script src="../../lib/webgl-debug.js"></script>
    <script src="../../lib/cuon-utils.js"></script>
    <script src="../../lib/cuon-matrix.js"></script>
    <script>
      // 顶点着色器
      var VSHADER_SOURCE =
        'attribute vec4 a_Position;\n' + // 设置顶点
        'attribute vec4 a_Color;\n' + // 颜色变量
        'uniform mat4 u_MvpMatrix;\n' + // 视图投影矩阵
        'uniform mat4 u_ModelMatrix;\n' + // 
        'uniform vec4 u_Eye;\n' +     // 观察者位置 视点
        'varying vec4 v_Color;\n' + // 颜色
        'varying float v_Dist;\n' + // 顶点与 视点的距离
        'void main() {\n' +
        '  gl_Position = u_MvpMatrix * a_Position;\n' + // 计算物体顶点位置
        '  v_Color = a_Color;\n' +
          // 计算顶点与 视点的距离
        '  v_Dist = distance(u_ModelMatrix * a_Position, u_Eye);\n' +
        '}\n';

      // 片元着色器
      var FSHADER_SOURCE =
        '#ifdef GL_ES\n' +
        'precision mediump float;\n' + // 设置精度
        '#endif\n' +
        'uniform vec3 u_FogColor;\n' + // 雾的颜色
        'uniform vec2 u_FogDist;\n' +  // 雾化的起点和终点
        'varying vec4 v_Color;\n' + // 传过来的变量
        'varying float v_Dist;\n' + // 传过来的变量
        'void main() {\n' +
          // 计算雾化因子 u_FogDist.y:雾化终点  v_Dist:顶点与 视点的距离  u_FogDist.x:雾化起点   clamp api:把第一个参数的值 限制在第二三的区间中(0,1)
        '  float fogFactor = clamp((u_FogDist.y - v_Dist) / (u_FogDist.y - u_FogDist.x), 0.0, 1.0);\n' +
          // api 吧 混合 雾的颜色  物体颜色 雾化因子
        '  vec3 color = mix(u_FogColor, vec3(v_Color), fogFactor);\n' +
        '  gl_FragColor = vec4(color, v_Color.a);\n' +
        '}\n';

      function main() {
        // 获取dom对象
        var canvas = document.getElementById('webgl');

        // 获取gl 上下文对象
        var gl = getWebGLContext(canvas);
        if (!gl) {
          console.log('Failed to get the rendering context for WebGL');
          return;
        }

        // 初始化着色器  返回程序对象
        if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
          console.log('Failed to intialize shaders.');
          return;
        }

        // 着色器变量赋值
        var n = initVertexBuffers(gl);
        if (n < 1) {
          console.log('Failed to set the vertex information');
          return;
        }

        // 雾化颜色
        var fogColor = new Float32Array([0.137, 0.231, 0.423]); // 设置雾的颜色
        // 雾化的起点和终点
        var fogDist = new Float32Array([55, 80]);
        // 设置视点坐标
        var eye = new Float32Array([25, 65, 35, 1.0]);

        // 获取着色器的变量
        var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
        var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
        var u_Eye = gl.getUniformLocation(gl.program, 'u_Eye');
        var u_FogColor = gl.getUniformLocation(gl.program, 'u_FogColor');
        var u_FogDist = gl.getUniformLocation(gl.program, 'u_FogDist');
        if (!u_MvpMatrix || !u_ModelMatrix || !u_Eye || !u_FogColor || !u_FogDist) {
          console.log('Failed to get the storage location');
          return;
        }
        
        // 给 着色器变量赋值
        gl.uniform3fv(u_FogColor, fogColor); // Colors
        gl.uniform2fv(u_FogDist, fogDist);   // Starting point and end point
        gl.uniform4fv(u_Eye, eye);           // Eye point

        // 设置背景色
        gl.clearColor(fogColor[0], fogColor[1], fogColor[2], 1.0); // Color of Fog
        gl.enable(gl.DEPTH_TEST); // 开启隐藏面消除功能

        // 创建矩阵
        var modelMatrix = new Matrix4();
        modelMatrix.setScale(10, 10, 10); // 设置 收缩比例
        gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements); // 赋值

        // 创建矩阵
        var mvpMatrix = new Matrix4();
        mvpMatrix.setPerspective(30, canvas.width/canvas.height, 1, 1000); // // 定义透视 投影矩阵
        mvpMatrix.lookAt(eye[0], eye[1], eye[2], 0, 2, 0, 0, 1, 0);// 创建视图矩阵
        mvpMatrix.multiply(modelMatrix);
        gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);
        document.onkeydown = function(ev){ keydown(ev, gl, n, u_FogDist, fogDist); }; // 注册事件

        // 清空颜色缓冲器 和 深度缓冲区
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        // 画图
        gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);

        var modelViewMatrix = new Matrix4(); // ??? 干什么的 没看懂
        modelViewMatrix.setLookAt(eye[0], eye[1], eye[2], 0, 2, 0, 0, 1, 0);
        modelViewMatrix.multiply(modelMatrix);
        modelViewMatrix.multiplyVector4(new Vector4([1, 1, 1, 1]));
        mvpMatrix.multiplyVector4(new Vector4([1, 1, 1, 1]));
        modelViewMatrix.multiplyVector4(new Vector4([-1, 1, 1, 1]));
        mvpMatrix.multiplyVector4(new Vector4([-1, 1, 1, 1]));
      }

      function keydown(ev, gl, n, u_FogDist, fogDist) {
        switch (ev.keyCode) {
          case 38: // 方向键 上键 改变  fogDist的值
            fogDist[1]  += 1;
            break;
          case 40: // 方向键 下键 改变  fogDist的值
            if (fogDist[1] > fogDist[0]) fogDist[1] -= 1;
            break;
          default: return;
        }
        gl.uniform2fv(u_FogDist, fogDist);   // 赋值
        // 清空颜色缓冲器 和 深度缓冲区
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        // 画图
        gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);
      }

      function initVertexBuffers(gl) {
        // Create a cube
        //    v6----- v5
        //   /|      /|
        //  v1------v0|
        //  | |     | |
        //  | |v7---|-|v4
        //  |/      |/
        //  v2------v3

        var vertices = new Float32Array([   // 顶点坐标
          1, 1, 1,  -1, 1, 1,  -1,-1, 1,   1,-1, 1,    // v0-v1-v2-v3 front
          1, 1, 1,   1,-1, 1,   1,-1,-1,   1, 1,-1,    // v0-v3-v4-v5 right
          1, 1, 1,   1, 1,-1,  -1, 1,-1,  -1, 1, 1,    // v0-v5-v6-v1 up
          -1, 1, 1,  -1, 1,-1,  -1,-1,-1,  -1,-1, 1,    // v1-v6-v7-v2 left
          -1,-1,-1,   1,-1,-1,   1,-1, 1,  -1,-1, 1,    // v7-v4-v3-v2 down
          1,-1,-1,  -1,-1,-1,  -1, 1,-1,   1, 1,-1     // v4-v7-v6-v5 back
        ]);

        var colors = new Float32Array([     // 颜色坐标
          0.4, 0.4, 1.0,  0.4, 0.4, 1.0,  0.4, 0.4, 1.0,  0.4, 0.4, 1.0,  // v0-v1-v2-v3 front
          0.4, 1.0, 0.4,  0.4, 1.0, 0.4,  0.4, 1.0, 0.4,  0.4, 1.0, 0.4,  // v0-v3-v4-v5 right
          1.0, 0.4, 0.4,  1.0, 0.4, 0.4,  1.0, 0.4, 0.4,  1.0, 0.4, 0.4,  // v0-v5-v6-v1 up
          1.0, 1.0, 0.4,  1.0, 1.0, 0.4,  1.0, 1.0, 0.4,  1.0, 1.0, 0.4,  // v1-v6-v7-v2 left
          1.0, 1.0, 1.0,  1.0, 1.0, 1.0,  1.0, 1.0, 1.0,  1.0, 1.0, 1.0,  // v7-v4-v3-v2 down
          0.4, 1.0, 1.0,  0.4, 1.0, 1.0,  0.4, 1.0, 1.0,  0.4, 1.0, 1.0   // v4-v7-v6-v5 back
        ]);

        var indices = new Uint8Array([       // 3d 物体 索引值坐标
          0, 1, 2,   0, 2, 3,    // front
          4, 5, 6,   4, 6, 7,    // right
          8, 9,10,   8,10,11,    // up
          12,13,14,  12,14,15,    // left
          16,17,18,  16,18,19,    // down
          20,21,22,  20,22,23     // back
        ]);

        // 创建 缓冲区对象
        var indexBuffer = gl.createBuffer();
        if (!indexBuffer) 
          return -1;

        // 着色器 变量赋值
        if (!initArrayBuffer(gl, vertices, 3, gl.FLOAT, 'a_Position')) return -1;
        if (!initArrayBuffer(gl, colors, 3, gl.FLOAT, 'a_Color')) return -1;

        // 将缓冲区对象 绑定到 gl.ELEMENT_ARRAY_BUFFER(gl.ELEMENT_ARRAY_BUFFER 管理具有索引结构的三维数据模型)
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);

        return indices.length;
      }

      function initArrayBuffer (gl, data, num, type, attribute) {
        // 创建 缓冲区对象
        var buffer = gl.createBuffer();
        if (!buffer) {
          console.log('Failed to create the buffer object');
          return false;
        }
        // 将缓冲区对象分配给 ARRAY_BUFFER
        gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
        gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW); // 赋值
        // 获取着色器 变量
        var a_attribute = gl.getAttribLocation(gl.program, attribute);
        if (a_attribute < 0) {
          console.log('Failed to get the storage location of ' + attribute);
          return false;
        }
        gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);
        // 开启访问权限
        gl.enableVertexAttribArray(a_attribute);
        // 缓冲区对象置空
        gl.bindBuffer(gl.ARRAY_BUFFER, null);

        return true;
      }

    </script>
  </body>
</html>

webgl代码地址


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容

  • 构成三维模型的基本图形是三角形,所以接下来就从如何绘制一个三角形开始,之后涉及到图形的变换和动画。 1. 图形绘制...
    Patrick浩阅读 749评论 1 1
  • 三角形在现实世界有着重要的作用,如上图中的球体建筑是由一个个三角形构建起来的,同样地三角形在图形学中也有着重要的地...
    辣椒爸阅读 615评论 0 0
  • 参考书籍:《WebGL编程指南》 随着学习的逐步深入,对顶点着色器和片元着色器有了更进一步的理解,之前的文章Web...
    辣椒爸阅读 1,192评论 1 1
  • 在利用缓冲区并在学会利用 mode绘制图形动画以后,继续研究二维图形的颜色渲染以及纹理操作 1. 颜色渲染 1.1...
    Patrick浩阅读 1,178评论 2 0
  • WebGL从2012年开始接触,后面因为开始专注前端其他方面的事情,慢慢地就把它给遗忘。最近前端开始又流行起绘画制...
    我不是传哥阅读 4,076评论 1 22