WebGL基本解析 | 青训营笔记

180 阅读4分钟

这是我参与「第四届青训营」笔记创作活动的的第15天

WebGL 是什么

WebGL-一种绘图协议 我们先来逐字分析

  • Web 自然不用多说
  • G Graphics 涉及显卡
  • L Library 库/馆
    现在我们对WebGL已经初步认识 WebGL可以为H5提供硬件加速

WebGL 基本概念

根据"G" 我们已知WebGL 运行在电脑的 GPU 中 自然 我们需要使用能在 GPU 上运行的代码
另外 我们在代码中 有种东西叫着色器-用来替代固定渲染管线的可编辑程序 顾名思义 渲染用的。 着色器替代了传统的固定渲染管线,可以实现3D图形学计算中的相关计算,由于其可编辑性,可以实现各种各样的图像效果 我们在WebGL开发中 用到两种着色器-顶点AND片元

  • 顶点着色器 都顶点了 自然与顶点有关 提供顶点坐标
  • 片元着色器 就是像素着色(大概) 将空间像素上色

数据获取方式

好耶!有了着色的玩意 可是没有对象(无端联想)
1.属性和缓冲 缓冲是发送到 GPU 的一些二进制数据序列 坐标 顶点 属性等等
2.全局变量 全局变量在着色程序运行前赋值
3.纹理 自然 纹理 颜色之类
4.可变量 两种着色器传递 从顶点到片元

实践

1.创建画布-canvas元素

function webglInit () {
  const canvasEl = document.createElement('canvas'); // canvas 元素创建
  canvasEl.width = document.body.clientWidth; // 设置 canvas 画布的宽度
  canvasEl.height = document.body.clientHeight; // 设置 canvas 画布的高度
  document.body.append(canvasEl); // 将创建好的 canvas 画布添加至页面中的 body 元素下
  // 接下来我们需要判断浏览器对于 WebGL 的兼容性,如果浏览器不支持 WebGL 那么我们就不需要再进行下去了
  if(!canvasEl.getContext("webgl") && !canvasEl.getContext("experimental-webgl ")) {
    alert("Your Browser Doesn't Support WebGL");
    return;
  }
  // 如果浏览器支持 WebGL,那么我们就获取 WebGL 的上下文对象并复制给变量 gl
  const context = (canvasEl.getContext("webgl"))
  ? canvasEl.getContext("webgl") 
  : getContext("experimental-webgl");
  /* 
    设置视口 context.viewport(x, y, width, height);
    x: 用来设定视口的左下角水平坐标。默认值:0
    y: 用来设定视口的左下角垂直坐标。默认值:0
    width: 用来设定视口的宽度。默认值:canvas 的宽度
    height: 用来设定视口的高度。默认值:canvas 的高度
    当你第一次创建 WebGL 上下文的时候,视口的大小和 canvas 的大小是匹配的。然而,如果你重新改变了canvas的大小,你需要告诉 WebGL 上下文设定新的视口,因此这里作为初次创建这行代码可以省略
  */
  context.viewport(0, 0, context.canvas.width, context.canvas.height);
  return context;
}

创建canvas完成 绘画开始

const gl =  webglInit();
// 创建顶点着色器 语法 gl.createShader(type) 此处 type 为枚举型值为 gl.VERTEX_SHADER 或 gl.FRAGMENT_SHADER 两者中的一个
const vShader = gl.createShader(gl.VERTEX_SHADER) 
// 编写顶点着色器的 GLSL 代码 语法 gl.shaderSource(shader, source); shader - 用于设置程序代码的 webglShader(着色器对象) source - 包含 GLSL 程序代码的字符串
gl.shaderSource(vShader, `
  attribute vec4 v_position;

  void main() {
    gl_Position = v_position; // 设置顶点位置
  }
`)
gl.compileShader(vShader) // 编译着色器代码

const fShader = gl.createShader(gl.FRAGMENT_SHADER) 
gl.shaderSource(fShader, `
  precision mediump float;
  uniform vec4 f_color;
  void main() {
    gl_FragColor = f_color; // 设置片元颜色
  }
`) // 编写片元着色器代码 
gl.compileShader(fShader) // 编译着色器代码

连接两个着色器

// 创建一个程序用于连接顶点着色器和片元着色器
const program = gl.createProgram() 
gl.attachShader(program, vShader) // 添加顶点着色器
gl.attachShader(program, fShader) // 添加片元着色器
gl.linkProgram(program) // 连接 program 中的着色器

gl.useProgram(program) // 告诉 WebGL 用这个 program 进行渲染

const color = gl.getUniformLocation(program, 'f_color') 
// 获取 f_color 变量位置
gl.uniform4f(color, 0.93, 0, 0.56, 1) // 设置它的值

const position = gl.getAttribLocation(program, 'v_position') 
// 获取 v_position 位置
const pBuffer = gl.createBuffer() 
// 创建一个顶点缓冲对象,返回其 id,用来放三角形顶点数据,
gl.bindBuffer(gl.ARRAY_BUFFER, pBuffer) 
// 将这个顶点缓冲对象绑定到 gl.ARRAY_BUFFER
// 后续对 gl.ARRAY_BUFFER 的操作都会映射到这个缓存
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
    0, 0.5,
    0.5, 0,
    -0.5, -0.5
]),  // 三角形的三个顶点
     // 因为会将数据发送到 GPU,为了省去数据解析,这里使用 Float32Array 直接传送数据
gl.STATIC_DRAW // 表示缓冲区的内容不会经常更改
)
// 将顶点数据加入的刚刚创建的缓存对象

gl.vertexAttribPointer( // 告诉 OpenGL 如何从 Buffer 中获取数据
    position, // 顶点属性的索引
    2, // 组成数量,必须是 1,2,3 或 4。我们只提供了 x 和 y
    gl.FLOAT, // 每个元素的数据类型
    false, // 是否归一化到特定的范围,对 FLOAT 类型数据设置无效
    0, // stride 步长 数组中一行长度,0 表示数据是紧密的没有空隙,让 OpenGL 决定具体步长
    0 // offset 字节偏移量,必须是类型的字节长度的倍数。
)
gl.enableVertexAttribArray(position);
// 开启 attribute 变量额,使顶点着色器能够访问缓冲区数据

gl.clearColor(0, 1, 1, 1) // 设置清空颜色缓冲时的颜色值
gl.clear(gl.COLOR_BUFFER_BIT) // 清空颜色缓冲区,也就是清空画布
// 语法 gl.drawArrays(mode, first, count); mode - 指定绘制图元的方式 first - 指定从哪个点开始绘制 count - 指定绘制需要使用到多少个点
gl.drawArrays( gl.TRIANGLES, 0, 3 )

然后 简单的写个HTML即可运行啦

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>triangle</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
 
        canvas {
            border: 3px solid blue;
            display: block;
        }
    </style>
</head>
<body>
<canvas id="triangle" width="800" height="600"></canvas>
<script type="text/javascript" src="../libs/webgl-utils.js"></script>
<script type="text/javascript" src="../libs/shader.js"></script>
<script id="vertex-shader" type="x-shader/x-vertex">
      attribute vec4 a_position;
      void main() {
          gl_Position = a_position;
      }
 </script>
 
 <script id="fragment-shader" type="x-shader/x-fragment">
      precision mediump float;//将精度设置为中等精度
      void main() {
          gl_FragColor = vec4(1, 0, 0.5, 1);
      }
 </script>
<script type="text/javascript">
.......//用于实现主要功能的脚本代码
</script>
</body>
</html>

当然 我们用的是原生WebGL的API 有不足
比如 数学基础(我线代现在还没学完啊啊啊
Bye~