阅读 205

webgl学习笔记(一)

webgl是什么?

webgl 是在网页上绘制和渲染三维图形的技术,可以让用户与其进行交互。

我们之前学过的div+css、canvas 2d 都是专注于二维图形的,它们虽然也能模拟一部分三维效果,但它们和webgl 比起来,那就是玩具枪和AK47的差别。

webgl行业背景

随着 5G 时代的到来,3D可视化需求大量涌现。3D 游戏,酷炫的活动宣传页,三维数字城市,VR全景展示、3D 产品展示等领域中,很多项目都是用 WebGL 实现的,也只能用WebGL来做,也就是说,WebGL 的时代就在眼前了

通过一些实际案例,我们可以知道WebGL 能做什么:

webgl简单使用

<body>
  <!-- 1、建立canvas画布 -->
  <canvas id="canvas"></canvas>
  <script>
    // 2、获取画布
    const canvas = document.getElementById('canvas')
    // 3、使用canvas获取webgl绘图上下文
    const gl = canvas.getContext('webgl')
    // 4、指定将要用来清空绘图区的颜色
    gl.clearColor(255,0,0,1) // 参数 r g b a
    // 5、使用指定的颜色,清空绘图区
    gl.clear(gl.COLOR_BUFFER_BIT)
  </script>
</body>
复制代码

案例 - 多姿多彩的动画

<body>
  <!-- 1、建立canvas画布 -->
  <canvas id="canvas"></canvas>
  <script type="module">
    // 引入Color对象
    import { Color } from "https://unpkg.com/three/build/three.module.js";
    // 2、获取画布
    const canvas = document.getElementById('canvas')
    // 3、使用canvas获取webgl绘图上下文
    const gl = canvas.getContext('webgl')
    // 实例化Color对象
    const color = new Color(1,0,0)
    // 建立色相偏移动画
    !(function ani(){
      color.offsetHSL(0.005,0,0)
      gl.clearColor(color.r,color.g,color.b,1)
      gl.clear(gl.COLOR_BUFFER_BIT)
      requestAnimationFrame(ani)
    })()
  </script>
</body>
复制代码

补充:

  • 色相 颜色的主色调 比如 红 绿 蓝

  • 饱和度 颜色的鲜艳程度 饱和度越大,颜色越鲜艳

  • 亮度 颜色的明暗

参考

一张图让开发人员理解色相、亮度、饱和度

webgl 坐标系

canvas 2d 画布和webgl 画布使用的坐标系都是二维直角坐标系,只不过它们坐标原点、y 轴的坐标方向,坐标基底都不一样了。

canvas 2d坐标系

canvas 2d 坐标系的原点在左上角。

canvas 2d 坐标系的y 轴方向是朝下的。

canvas 2d 坐标系的坐标基底有两个分量,分别是一个像素的宽和一个像素的高,即1个单位的宽便是1个像素的宽,1个单位的高便是一个像素的高。

webgl 坐标系

webgl坐标系的坐标原点在画布中心。

webgl坐标系的y 轴方向是朝上的。

webgl坐标基底中的两个分量分别是半个canvas的宽和canvas的高,即1个单位的宽便是半个个canvas的宽,1个单位的高便是半个canvas的高。

使用webgl画一个点

canvas绘图基本步骤

<body>
  <canvas id='canvas'></canvas>
  <script>
    // canvas画布
    const canvas = document.getElementById('canvas')
    // 二维画笔
    const ctx = canvas.getContext('2d')
    // 设置画笔颜色
    ctx.fillStyle = 'red'
    // 画一个矩形
    ctx.fillRect(20,20,300,200)
  </script>
</body>
复制代码

实际上,webgl 的绘图逻辑和canvas 2d 的绘图逻辑还有一个本质的差别。

大家在学习html 的时候应该知道,浏览器有三大线程: js 引擎线程、GUI 渲染线程、浏览器事件触发线程。

其中GUI 渲染线程就是用于渲图的,在这个渲染线程里,有负责不同渲染工作的工人。比如有负责渲HTML+css的工人,有负责渲染二维图形的工人,有负责渲染三维图形的工人。

渲染二维图形的工人和渲染三维图形的工人不是一个国家的,他们说的语言不一样。

渲染二维图形的工人说的是js语言。

渲染三维图形的工人说的是GLSL ES 语言。

而我们在做web项目时,业务逻辑、交互操作都是用js 写的。

我们在用js 绘制canvas 2d 图形的时候,渲染二维图形的工人认识js 语言,所以它可以正常渲图。

但我们在用js 绘制webgl图形时,渲染三维图形的工人就不认识这个js 语言了,因为它只认识GLSL ES 语言。

因此,这个时候我们就需要找人翻译翻译,它在webgl 里叫“程序对象”。

接下来咱们从手绘板的绘图步骤中捋一下webgl 的绘图思路

webgl 绘图思路

  1. 找一台电脑 - 浏览器里内置的webgl 渲染引擎,负责渲染webgl 图形,只认GLSL ES语言。

  2. 找一块手绘板 - 程序对象,承载GLSL ES语言,翻译GLSL ES语言和js语言,使两者可以相互通信。

  3. 找一支触控笔 - 通过canvas 获取的webgl 类型的上下文对象,可以向手绘板传递绘图命令,并接收手绘板的状态信息。

  4. 开始画画 - 通过webgl 类型的上下文对象,用js 画画。

在上面的思路中,大家对其中的一些名词可能还没有太深的概念,比如程序对象。接下来咱们就详细说一下webgl 实际的绘图步骤。

webgl绘图步骤

1.在html中建立canvas 画布

<canvas id="canvas"></canvas>
复制代码

2.在js中获取canvas画布

const canvas=document.getElementById('canvas');
复制代码

3.使用canvas 获取webgl 绘图上下文

const gl=canvas.getContext('webgl');
复制代码

4.在script中建立顶点着色器和片元着色器,glsl es

//顶点着色器
<script id="vertexShader" type="x-shader/x-vertex">
    void main() {
        gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
        gl_PointSize = 100.0;
    }
</script>
//片元着色器
<script id="fragmentShader" type="x-shader/x-fragment">
    void main() {
        gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
    }
</script>
复制代码

5.在js中获取顶点着色器和片元着色器的文本

const vsSource = document.getElementById('vertexShader').innerText;
const fsSource = document.getElementById('fragmentShader').innerText;
复制代码

6.初始化着色器

initShaders(gl, vsSource, fsSource);
复制代码

7.指定将要用来清空绘图区的颜色

gl.clearColor(0,0,0,1);
复制代码

8.使用之前指定的颜色,清空绘图区

gl.clear(gl.COLOR_BUFFER_BIT);
复制代码

9.绘制顶点

gl.drawArrays(gl.POINTS, 0, 1);
复制代码

完整代码:

<canvas id="canvas"></canvas>
<!-- 顶点着色器 -->
<script id="vertexShader" type="x-shader/x-vertex">
    void main() {
        gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
        gl_PointSize = 100.0;
    }
</script>
<!-- 片元着色器 -->
<script id="fragmentShader" type="x-shader/x-fragment">
    void main() {
        gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
    }
</script>
<script>
    // canvas 画布
    const canvas = document.getElementById('canvas');
    canvas.width=window.innerWidth;
    canvas.height=window.innerHeight;
    // webgl画笔
    const gl = canvas.getContext('webgl');
    // 顶点着色器
    const vsSource = document.getElementById('vertexShader').innerText;
    // 片元着色器
    const fsSource = document.getElementById('fragmentShader').innerText;
    // 初始化着色器
    initShaders(gl, vsSource, fsSource);
    // 指定将要用来清理绘图区的颜色
    gl.clearColor(0., 0.0, 0.0, 1.0);
    // 清理绘图区
    gl.clear(gl.COLOR_BUFFER_BIT);
    // 绘制顶点
    gl.drawArrays(gl.POINTS, 0, 1);

    function initShaders(gl,vsSource,fsSource){
        //创建程序对象
        const program = gl.createProgram();
        //建立着色对象
        const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
        const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
        //把顶点着色对象装进程序对象中
        gl.attachShader(program, vertexShader);
        //把片元着色对象装进程序对象中
        gl.attachShader(program, fragmentShader);
        //连接webgl上下文对象和程序对象
        gl.linkProgram(program);
        //启动程序对象
        gl.useProgram(program);
        //将程序对象挂到上下文对象上
        gl.program = program;
        return true;
    }

    function loadShader(gl, type, source) {
        //根据着色类型,建立着色器对象
        const shader = gl.createShader(type);
        //将着色器源文件传入着色器对象中
        gl.shaderSource(shader, source);
        //编译着色器对象
        gl.compileShader(shader);
        //返回着色器对象
        return shader;
    }
</script>
复制代码

着色器

概念

webgl绘图需要两种着色器:

  • 顶点着色器(vertex shader):描述顶点的特征,如位置、颜色等
  • 片元着色器(fragment shader):进行片元处理,如光照

两点决定一条直线,顶点着色器里的顶点就是决定这一条直线的两个点,片元着色器里的片元就是把直线画到画布上后,这两个点之间构成直线的每个像素。

着色器语言

webgl 的着色器语言是GLSL ES语言。

  • 顶点着色程序,要写在type=“x-shader/x-vertex” 的script中。

  • 片元着色程序,要写在type=“x-shader/x-fragment” 的script中。

vec4是一个四维矢量对象,

将vec4() 赋值给顶点点位gl_Position 的时候,其中的前三个参数是x、y、z,第4个参数默认1.0。

将vec4() 赋值给片元颜色gl_FragColor 的时候,其中的参数是r,g,b,a。

着色器初始化 --- initShaders()

着色器初始化步骤:

1、建立程序对象。

const shaderProgram = gl.createProgram();
复制代码

2、建立顶点着色器对象和片元着色器对象,把JS信号解析为计算机语言(GLSL ES),然后让计算机(浏览器的webgl渲染引擎)识别显示。

const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
复制代码

loadShader的参数:webgl上下文对象,着色器类型,着色器源文件。

function loadShader(gl, type, source) {
   // 根据着色器类型建立着色器对象的方法
    const shader = gl.createShader(type);
   // 将着色器源文件传入着色器对象中
    gl.shaderSource(shader, source);
   // 编译着色器对象
    gl.compileShader(shader);
    return shader;
}
复制代码

3、将顶点着色器对象和片元着色器对象装进程序对象中。

gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
复制代码

4、连接webgl上下文对象和程序对象。

gl.linkProgram(shaderProgram)
复制代码

5、启动程序对象

gl.useProgram(program)
复制代码
文章分类
前端
文章标签