webgl基础笔记(四)

156 阅读1分钟

1. 3D场景构成要素

  • 视点:观察点
  • 目标点:要看的物体
  • 上方向:正方向

2. 观察平面

(1) 通过观察平面显示物体

  • 观察平面创建新的坐标系
  • 物体的所有坐标投影到坐标系上

(2) 创建坐标系的辅助函数

(3) 公共方法

// lib/index.js
function initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE) {
  // 创建着色器
  const vertexShader = gl.createShader(gl.VERTEX_SHADER)
  const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)

  // 指定顶点着色器源码
  gl.shaderSource(vertexShader, VERTEX_SHADER_SOURCE)
  // 指定片元着色器源码
  gl.shaderSource(fragmentShader, FRAGMENT_SHADER_SOURCE)

  // 编译着色器
  gl.compileShader(vertexShader)
  gl.compileShader(fragmentShader)

  // 创建一个程序对象
  const program = gl.createProgram()

  gl.attachShader(program, vertexShader)
  gl.attachShader(program, fragmentShader)

  gl.linkProgram(program)
  gl.useProgram(program)

  return program
}

// 平移矩阵
function getTranslateMatrix(x = 0, y = 0, z = 0) {
  return new Float32Array([
    1.0, 0.0, 0.0, 0.0,
    0.0, 1.0, 0.0, 0.0,
    0.0, 0.0, 1.0, 0.0,
      x,   y,   z,   1,
  ])
}

 // 缩放矩阵
 function getScaleMatrix(x = 1, y = 1, z = 1) {
  return new Float32Array([
      x, 0.0, 0.0, 0.0,
    0.0,   y, 0.0, 0.0,
    0.0, 0.0,   z, 0.0,
    0.0, 0.0, 0.0,   1,
  ])
}

// 绕z轴旋转的矩阵
function getRotateMatrix(deg) {
  return new Float32Array([
    Math.cos(deg),  Math.sin(deg), 0.0, 0.0,
    -Math.sin(deg), Math.cos(deg), 0.0, 0.0,
    0.0,            0.0,           1.0, 0.0,
    0.0,            0.0,           0.0,   1,
  ])
}

// 矩阵复合函数
function mixMatrix(A, B) {
  const result = new Float32Array(16)

  for (let i = 0; i < 4; i++) {
    result[i] = A[i] * B[0] + A[i + 4] * B[1] + A[i + 8] * B[2] + A[i + 12] * B[3]
    result[i + 4] = A[i] * B[4] + A[i + 4] * B[5] + A[i + 8] * B[6] + A[i + 12] * B[7]
    result[i + 8] = A[i] * B[8] + A[i + 4] * B[9] + A[i + 8] * B[10] + A[i + 12] * B[11]
    result[i + 12] = A[i] * B[12] + A[i + 4] * B[13] + A[i + 8] * B[14] + A[i + 12] * B[15]
  }

  return result
}

// 归一化函数
function normalized(arr) {
  let sum = 0;

  for (let i = 0; i < arr.length; i++) {
    sum += arr[i] * arr[i]
  }

  const middle = Math.sqrt(sum);

  for (let i = 0; i < arr.length; i++) {
    arr[i] = arr[i] / middle;
  }
}

// 叉积函数 获取法向量
function cross(a,b) {
  return new Float32Array([
    a[1] * b[2] - a[2] * b[1],
    a[2] * b[0] - a[0] * b[2],
    a[0] * b[1] - a[1] * b[0],
  ])
}

// 点积函数 获取投影长度
function dot(a, b) {
  return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
}

// 向量差
function minus(a, b) {
  return new Float32Array([
    a[0] - b[0],
    a[1] - b[1],
    a[2] - b[2],
  ])
}

// 视图矩阵获取
function getViewMatrix(eyex, eyey, eyez, lookAtx, lookAty, lookAtz, upx, upy, upz) {

  // 视点
  const eye = new Float32Array([eyex, eyey, eyez])
  // 目标点
  const lookAt = new Float32Array([lookAtx, lookAty, lookAtz])
  // 上方向
  const up = new Float32Array([upx, upy, upz])

  // 确定z轴
  const z = minus(eye, lookAt);

  normalized(z);
  normalized(up);

  // 确定x轴
  const x = cross(z, up);

  normalized(x);
  // 确定y轴
  const y = cross(x, z);

  return new Float32Array([
    x[0],       y[0],       z[0],       0,
    x[1],       y[1],       z[1],       0,
    x[2],       y[2],       z[2],       0,
    -dot(x,eye),-dot(y,eye),-dot(z,eye),1
  ])
}

// 获取正射投影矩阵
function getOrtho(l, r, t, b, n, f) {
  return new Float32Array([
    2 / (r - l), 0,           0,           0,
    0,           2/(t-b),     0,           0,
    0,           0,           -2/(f-n),    0,
    -(r+l)/(r-l),-(t+b)/(t-b),-(f+n)/(f-n),1
  ])
}

// 获取透视投影矩阵
function getPerspective(fov, aspect, far, near) {
  fov = fov * Math.PI / 180;
  return new Float32Array([
    1/(aspect*Math.tan(fov / 2)), 0, 0, 0,
    0, 1/(Math.tan(fov/2)),0,0,
    0,0,-(far+near)/(far-near),-(2*far*near)/(far-near),
    0,0,-1,0,
  ])
}

function distanceSelf(a, b) {
  const x = a[0] - b[0]
  const y = a[1] - b[1]
  const z = a[2] - b[2]

  const v = x * x + y * y + z * z;

  return Math.sqrt(v)
}

3. 透视投影

/** @type {HTMLCanvasElement} */
const canvas = document.querySelector('#canvas')

const gl = canvas.getContext('webgl')

// 创建着色器源码
const VERTEX_SHADER_SOURCE = `
      attribute vec4 aPosition;
      attribute vec4 aColor;
      varying vec4 vColor;

      uniform mat4 mat;
      void main() {
        gl_Position = mat * aPosition;
        vColor = aColor;
      }
    ` // 顶点着色器
const FRAGMENT_SHADER_SOURCE = `
      precision lowp float;
      varying vec4 vColor;
      void main() {
        gl_FragColor = vColor;
      }
    ` // 片元着色器

const program = initShader(
  gl,
  VERTEX_SHADER_SOURCE,
  FRAGMENT_SHADER_SOURCE
)

const aPosition = gl.getAttribLocation(program, 'aPosition')
const aColor = gl.getAttribLocation(program, 'aColor')
const mat = gl.getUniformLocation(program, 'mat')

// 创建三个点的数组
const points = new Float32Array([
  0.75,1.0,-0.6, 1.0,0.0,0.0,
  0.25,-1.0,-0.6, 1.0,0.0,0.0,
  1.0, -1.0,-0.6, 1.0,0.0,0.0,

  0.75,1.0,-0.5, 0.0,1.0,0.0,
  0.25,-1.0,-0.5, 0.0,1.0,0.0,
  1.0, -1.0,-0.5, 0.0,1.0,0.0,

  0.75,1.0,-0.4, 0.0,0.0,1.0,
  0.25,-1.0,-0.4, 0.0,0.0,1.0,
  1.0, -1.0,-0.4, 0.0,0.0,1.0,

  -0.75,1.0,-0.6, 1.0,0.0,0.0,
  -0.25,-1.0,-0.6, 1.0,0.0,0.0,
  -1.0, -1.0,-0.6, 1.0,0.0,0.0,

  -0.75,1.0,-0.5, 0.0,1.0,0.0,
  -0.25,-1.0,-0.5, 0.0,1.0,0.0,
  -1.0, -1.0,-0.5, 0.0,1.0,0.0,

  -0.75,1.0,-0.4, 0.0,0.0,1.0,
  -0.25,-1.0,-0.4, 0.0,0.0,1.0,
  -1.0, -1.0,-0.4, 0.0,0.0,1.0,
])

// 创建缓冲区对象
const buffer = gl.createBuffer()

// 绑定缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)

// 将数据写入缓冲区对象
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW)

// 获取数据字节数
const BYTES = points.BYTES_PER_ELEMENT

// 将数据写入缓冲区对象
gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, BYTES * 6, 0)

// 启用 attribute 变量
gl.enableVertexAttribArray(aPosition)

gl.vertexAttribPointer(aColor, 3, gl.FLOAT, false, BYTES * 6, BYTES * 3)

gl.enableVertexAttribArray(aColor)

let eyex = 0.0
let eyey = -0.1
let eyez = 0.2
function animation() {
  const vm = getViewMatrix(eyex, eyey, eyez, 0.0, 0.0, 0.0, 0.0, 0.6, 0.0)
  const perspective = getPerspective(150, canvas.width / canvas.height, 100, 1)
  // 解决后面图形覆盖前面图形的问题
  gl.enable(gl.DEPTH_TEST)
  /*
         * 修改矩阵变量 gl.uniformMatrix4fv(location, transpose, array)
         * @params: transpose 在webgl中恒为false
         * @params: array 矩阵数组 
         */   
  gl.uniformMatrix4fv(mat, false, mixMatrix(vm, perspective))
  gl.drawArrays(gl.TRIANGLES, 0, 3 * 6)

  // requestAnimationFrame(animation)
}

animation()

document.onkeydown = function (e) {
  switch (e.keyCode) {
    case 37:
      eyex += 0.01;
      break;
    case 38:
      eyex -= 0.01;
      break;
    case 39:
      eyey += 0.01;
      break;
    case 40:
      eyey -= 0.01;
      break;

    default:
      break;
  }
  animation()
}

4. 立方体绘制

(1) 顶点法

/** @type {HTMLCanvasElement} */
const canvas = document.querySelector('#canvas')

const gl = canvas.getContext('webgl')

// 创建着色器源码
const VERTEX_SHADER_SOURCE = `
      attribute vec4 aPosition;
      attribute vec4 aColor;
      varying vec4 vColor;

      uniform mat4 mat;
      void main() {
        gl_Position = mat * aPosition;
        vColor = aColor;
      }
    ` // 顶点着色器
const FRAGMENT_SHADER_SOURCE = `
      precision lowp float;
      varying vec4 vColor;

      void main() {
        gl_FragColor = vColor;
      }
    ` // 片元着色器

const program = initShader(
  gl,
  VERTEX_SHADER_SOURCE,
  FRAGMENT_SHADER_SOURCE
)

const aPosition = gl.getAttribLocation(program, 'aPosition')
const aColor = gl.getAttribLocation(program, 'aColor')
const mat = gl.getUniformLocation(program, 'mat')

// 顶点
const v0 = [1,1,1];
const v1 = [-1,1,1];
const v2 = [-1,-1,1];
const v3 = [1,-1,1];
const v4 = [1,-1,-1];
const v5 = [1,1,-1];
const v6 = [-1,1,-1];
const v7 = [-1,-1,-1];
const points = new Float32Array([
  ...v0,...v1,...v2, ...v0,...v2, ...v3, // 前
  ...v0,...v3,...v4, ...v0,...v4, ...v5, // 右
  ...v0,...v1,...v6, ...v0,...v6, ...v5, // 上面
  ...v1,...v2,...v7, ...v1,...v7, ...v6, // 左
  ...v2,...v3,...v4, ...v2,...v4, ...v7, // 底
  ...v4,...v7,...v6, ...v4,...v6, ...v5, // 后
])

// 创建缓冲区对象
const buffer = gl.createBuffer()

// 绑定缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)

// 将数据写入缓冲区对象
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW)

// 获取数据字节数
const BYTES = points.BYTES_PER_ELEMENT

// 将数据写入缓冲区对象
gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0)

// 启用 attribute 变量
gl.enableVertexAttribArray(aPosition)

const colorData = new Float32Array([
  1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,
  0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,
  0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,
])

const colorBuffer = gl.createBuffer()

gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer)     
gl.bufferData(gl.ARRAY_BUFFER, colorData, gl.STATIC_DRAW)
gl.vertexAttribPointer(aColor, 3, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(aColor)

let eyex = 3
let eyey = 3
let eyez = 5

let deg = 0
function animation() {
  deg += 0.01
  const rotate = getRotateMatrix(deg)
  const vm = getViewMatrix(eyex, eyey, eyez, 0.0, 0.0, 0.0, 0.0, 0.6, 0.0)
  const perspective = getPerspective(30, canvas.width / canvas.height, 100, 1)

  gl.enable(gl.DEPTH_TEST);
  gl.uniformMatrix4fv(mat, false, mixMatrix(mixMatrix(perspective, vm), rotate))
  gl.drawArrays(gl.TRIANGLES, 0, points.length / 3)

  requestAnimationFrame(animation)
}

animation()

(2) 索引法

/** @type {HTMLCanvasElement} */
const canvas = document.querySelector('#canvas')

const gl = canvas.getContext('webgl')

// 创建着色器源码
const VERTEX_SHADER_SOURCE = `
      attribute vec4 aPosition;
      attribute vec4 aColor;
      varying vec4 vColor;

      uniform mat4 mat;
      void main() {
        gl_Position = mat * aPosition;
        vColor = aColor;
      }
    ` // 顶点着色器
const FRAGMENT_SHADER_SOURCE = `
      precision lowp float;
      varying vec4 vColor;

      void main() {
        gl_FragColor = vColor;
      }
    ` // 片元着色器

const program = initShader(
  gl,
  VERTEX_SHADER_SOURCE,
  FRAGMENT_SHADER_SOURCE
)

const aPosition = gl.getAttribLocation(program, 'aPosition')
const aColor = gl.getAttribLocation(program, 'aColor')
const mat = gl.getUniformLocation(program, 'mat')

// 顶点
const vertices = new Float32Array([
  // 0123
  1, 1, 1,
  -1, 1, 1,
  -1,-1, 1,
  1,-1, 1,
  // 0345
  1, 1, 1,
  1,-1, 1,
  1,-1,-1,
  1, 1,-1,
  // 0156
  1, 1, 1,
  1, 1, -1,
  -1, 1,-1,
  -1, 1,1,
  // 1267
  -1, 1, 1,
  -1,1, -1,
  -1, -1,-1,
  -1,-1,1,
  // 2347
  -1,-1, 1,
  1,-1, 1,
  1,-1,-1,
  -1,-1,-1,
  // 4567
  1,-1,-1,
  1, 1,-1,
  -1, 1,-1,
  -1,-1,-1,
])

// 创建缓冲区对象
const buffer = gl.createBuffer()

// 绑定缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)

// 将数据写入缓冲区对象
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)

// 获取数据字节数
const BYTES = vertices.BYTES_PER_ELEMENT

// 将数据写入缓冲区对象
gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0)

// 启用 attribute 变量
gl.enableVertexAttribArray(aPosition)

const 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,
  0.4,1.0,0.4,0.4,1.0,0.4,0.4,1.0,0.4,0.4,1.0,0.4,
  1.0,0.4,0.4,1.0,0.4,0.4,1.0,0.4,0.4,1.0,0.4,0.4,
  1.0,1.0,0.4,1.0,1.0,0.4,1.0,1.0,0.4,1.0,1.0,0.4,
  1.0,0.0,1.0,1.0,0.0,1.0,1.0,0.0,1.0,1.0,0.0,1.0,
  0.0,1.0,1.0,0.0,1.0,1.0,0.0,1.0,1.0,0.0,1.0,1.0,
])

const colorBuffer = gl.createBuffer()

gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer)     
gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW)
gl.vertexAttribPointer(aColor, 3, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(aColor)

const indeces = new Uint8Array([
  0,1,2,0,2,3,
  4,5,6,4,6,7,
  8,9,10,8,10,11,
  12,13,14,12,14,15,
  16,17,18,16,18,19,
  20,21,22,20,22,23,
])
const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indeces, gl.STATIC_DRAW);

let eyex = 3
let eyey = 3
let eyez = 5

let deg = 0
function animation() {
  deg += 0.01
  const rotate = getRotateMatrix(deg)
  const vm = getViewMatrix(eyex, eyey, eyez, 0.0, 0.0, 0.0, 0.0, 0.6, 0.0)
  const perspective = getPerspective(30, canvas.width / canvas.height, 100, 1)

  gl.enable(gl.DEPTH_TEST);
  gl.uniformMatrix4fv(mat, false, mixMatrix(mixMatrix(perspective, vm), rotate))
  gl.drawElements(gl.TRIANGLES, indeces.length, gl.UNSIGNED_BYTE, 0);

  requestAnimationFrame(animation)
}

animation()

5. 光照

/** @type {HTMLCanvasElement} */
const canvas = document.querySelector('#canvas')

const gl = canvas.getContext('webgl')

// 创建着色器源码
const VERTEX_SHADER_SOURCE = `
      attribute vec4 aPosition;
      attribute vec4 aNormal;
      varying vec4 vColor;

      uniform mat4 mat;
      void main() {
        // 定义点光源的颜色
        vec3 uPointLightColor = vec3(1.0,1.0,0.0);

        // 点光源的位置
        vec3 uPointLightPosition = vec3(-5.0,6.0,10.0);

        // 环境光
        vec3 uAmbientLightColor = vec3(0.2,0.2,0.2);

        // 物体表面的颜色
        vec4 aColor = vec4(1.0,0.0,0.0,1.0);

        // 顶点的世界坐标
        vec4 vertexPosition = mat * aPosition;

        // 点光源的方向
        vec3 lightDirection = normalize(uPointLightPosition - vec3(vertexPosition));

        // 环境反射
        vec3 ambient = uAmbientLightColor * vec3(aColor);

        // 计算入射角 光线方向和法线方向的点积
        float dotDeg = dot(lightDirection, vec3(aNormal));

        // 漫反射光的颜色
        vec3 diffuseColor = uPointLightColor * vec3(aColor) * dotDeg;

        gl_Position = vertexPosition;
        vColor = vec4(ambient + diffuseColor, aColor.a);
      }
    ` // 顶点着色器
const FRAGMENT_SHADER_SOURCE = `
      precision lowp float;
      varying vec4 vColor;

      void main() {
        gl_FragColor = vColor;
      }
    ` // 片元着色器

const program = initShader(
  gl,
  VERTEX_SHADER_SOURCE,
  FRAGMENT_SHADER_SOURCE
)

const aPosition = gl.getAttribLocation(program, 'aPosition')
const aNormal = gl.getAttribLocation(program, 'aNormal')
const mat = gl.getUniformLocation(program, 'mat')

// 顶点
const vertices = new Float32Array([
  // 0123
  1, 1, 1,
  -1, 1, 1,
  -1,-1, 1,
  1,-1, 1,
  // 0345
  1, 1, 1,
  1,-1, 1,
  1,-1,-1,
  1, 1,-1,
  // 0156
  1, 1, 1,
  1, 1, -1,
  -1, 1,-1,
  -1, 1,1,
  // 1267
  -1, 1, 1,
  -1,1, -1,
  -1, -1,-1,
  -1,-1,1,
  // 2347
  -1,-1, 1,
  1,-1, 1,
  1,-1,-1,
  -1,-1,-1,
  // 4567
  1,-1,-1,
  1, 1,-1,
  -1, 1,-1,
  -1,-1,-1,
])

// 创建缓冲区对象
const buffer = gl.createBuffer()

// 绑定缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)

// 将数据写入缓冲区对象
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)

// 获取数据字节数
const BYTES = vertices.BYTES_PER_ELEMENT

// 将数据写入缓冲区对象
gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0)

// 启用 attribute 变量
gl.enableVertexAttribArray(aPosition)

// 法向量
const 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,
  0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,
  -1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,
  1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,
  0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,
  0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,
])

const normalsBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, normalsBuffer)
gl.bufferData(gl.ARRAY_BUFFER, normals, gl.STATIC_DRAW)
gl.vertexAttribPointer(aNormal, 3, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(aNormal)

const indeces = new Uint8Array([
  0,1,2,0,2,3,
  4,5,6,4,6,7,
  8,9,10,8,10,11,
  12,13,14,12,14,15,
  16,17,18,16,18,19,
  20,21,22,20,22,23,
  ])
  const indexBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indeces, gl.STATIC_DRAW);

  function animation() {
  const vm = getViewMatrix(3, 3, 5, 0.0, 0.0, 0.0, 0.0, 0.6, 0.0)
  const perspective = getPerspective(30, canvas.width / canvas.height, 100, 1)

  gl.enable(gl.DEPTH_TEST);
  gl.uniformMatrix4fv(mat, false, mixMatrix(perspective, vm))
  gl.drawElements(gl.TRIANGLES, indeces.length, gl.UNSIGNED_BYTE, 0);
}

animation()

6. 雾化

7. 半透明物体

// 基于光照代码
...

`vColor = vec4(ambient + diffuseColor, 0.5);
      }
    ` // 顶点着色器
    
...

gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

8. 绘制圆形的点

const canvas = document.querySelector('#canvas')

const gl = canvas.getContext("webgl")

// 创建着色器源码
const VERTEX_SHADER_SOURCE = `
      void main() {
        gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
        gl_PointSize = 60.0;
      }
    `; // 顶点着色器
const FRAGMENT_SHADER_SOURCE = `
      precision lowp float;
      void main() {
        // 计算当前点gl_PointCoord 和 点范围vec2(0.5, 0.5)的距离
        float dis = distance(gl_PointCoord, vec2(0.5, 0.5));
        if(dis > 0.5 || dis < 0.4) {
          discard;
        }
        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
      }
    `; // 片元着色器

initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)

// 执行绘制
// 要绘制的图形是什么?从哪个开始?使用几个顶点
gl.drawArrays(gl.POINTS, 0, 1)