这里,咱们将向大家演示WebGL的一些奢侈阐明和根本应用,即便你后续应用第三方3D渲染引擎进行绘制,这里的基本概念仍旧是十分无益的,或者说是必要的。

在演示和阐明的时候,咱们抉择基于image3D.js来作为依赖库,但因为其奢侈的语法简直和原生WebGL是统一的,因而咱们认为这不是一个蹩脚的抉择(如果间接应用原生,代码会变得过于冗余,不好阐明)。

在后续的阐明中,你都无需查阅别的文档,当然,如果你想晓得的更具体,也能够间接拜访image3D.js 文档进行查阅。

绘制流程

个别最通用的绘制流程大抵如下:

筹备好着色器数据写入缓冲区并实现调配调用绘制办法进行绘制

不晓得你是否能够了解下面每个步骤是在干什么,咱们上面将通过一个逐步丰盛的例子来进行解释。

你能够提前看看咱们最终要绘制的成果:

这是二元函数 : y=x2+ z2的图像。

你能够点击此处进行查看运行成果。

着色器

绘制的第一步,就是筹备好两个着色器:顶点着色器片段着色器。前者用于形容绘制的图形的点的地位,后者用于形容每个点的色彩。

可能这样说你会无奈了解,其实简略的说就是:咱们在绘图的时候,会一次性的把数据都传递给GPU,传递给GPU的数据须要一些"整顿"后再应用,而着色器就是驻留在GPU上的这段"整顿程序"。

咱们传递的数据是什么?不就是点的地位和点的色彩吗。所以,着色器就分为了顶点着色器和片段着色器(有时候也叫片元着色器),前者解决点,后者解决色彩。

所以,让咱们先看看这里的顶点着色器的具体代码:

attribute vec4 a_position;attribute vec4 a_color;varying vec4 v_color;void main(){    gl_Position = a_position;    v_color = a_color;}

内置变量gl_Position就是绘图最终接管的点的数据,而咱们定义的变量a_position好比一个管道,咱们后续能够给这个变量赋值,也就间接的给gl_Position赋值了(也就是点的地位)。

那v_color是什么?你能够了解,绘图的时候,是以点为主的,每个点的色彩,须要借助点的地位来设置,而v_color就是地位到色彩的桥梁。还是间接看看片段着色器:

precision mediump float;varying vec4 v_color;void main(){    gl_FragColor=v_color;}

同样的存在一个内置变量,这里叫gl_FragColor, 其接管了来自顶点着色器的v_color。

我不晓得你是否了解了下面的行为,不过你可能也感觉到了,点的地位和色彩如何解决曾经筹备好了,后续咱们只须要借助a_position和a_color就能够设置数据了。

3D对象

在传递数据前,咱们先就基于这两个着色器创立3D对象,这一步非常简单,间接看代码:

var image3d = new image3D(document.getElementsByTagName('canvas')[0], {    "vertex-shader": vsCode,    "fragment-shader": fsCode,    depth: true});

后续的所有操作,包含传递绘制和绘制等,间接调用这个对象上的接口就能够了。

特地阐明:vsCode和fsCode就是下面两个着色器的代码,是字符串。

传递数据

终于,能够给GPU传递数据了,所以,咱们先来筹备好数据:

var points = [];/**    具体的写法你能够间接看最终的代码,获取的思路大略就是:    三个点拼接成一个三角形,    每个点由6个数据组成,前3个示意点的地位,后3个示意点的色彩,    而一个个三角形拼接成最终的图形。*/// 因而,点的个数就是var num = points.length / 6;

这些三角形如何确定的?对xoz面,范畴是-1 ~ 1,切割成一个个正方形,而后斜切一下就能够了:

对于y值,由 y=x2+ z2计算获取。

数据筹备好了,间接设置即可:

image3d.Buffer().write(new Float32Array(points)).use('a_position', 3, 6, 0).use('a_color', 3, 6, 3);

数据写入缓冲区,而后调配给a_position和a_color即可。

绘制

因为是三角形,一共num个,间接执行绘制办法即可:

image3d.Painter().drawTriangle(0, num);

变换

尽管下面的话,图形应该曾经进去了,不过,咱们看最终的例子如同始终在旋转,那旋转成果是如何进去的?

聪慧的你肯定想到了顶点着色器,是的,咱们只须要批改一下顶点着色器中内置变量gl_Position接管的值,让其都和一个矩阵相乘就能够了。着色器中对应代码批改:

gl_Position=u_matrix * a_position;

后续,咱们不停的从新绘制,而在绘制前,都传递一个新的矩阵即可:

//  创立相机对象var camera = image3d.Camera({    size: 2}).rotateBody(0.9, 0, 1, 0).rotateBody(0.3, 1, 0, 0, 0, 0, 1).moveBody(0.5, 0, -1, 0);setInterval(function () {    // 每次从新绘制前,都围绕射线(0,0,0)→ (0,1,0)旋转弧度0.05    camera.rotateBody(0.05, 0, 1, 0);    // 传递照相机    image3d.setUniformMatrix("u_matrix",        camera.value()    );    // 绘制    painter.drawTriangle(0, num);}, 30);

更多

如果说,咱们心愿再加上光照、投影,或者说,可不可以给图形贴上“皮肤”等,怎么写,是不是显而易见了。

是的,思路都非常简单,通过批改两个着色器最终获取的变量值即可,如何批改?那你就能够借助一系列的数学计算和着色器的内置函数了。

残缺代码

<!DOCTYPE html><html lang="zh-cn"><head>    <meta charset="UTF-8">    <meta http-equiv="X-UA-Compatible" content="IE=edge">    <meta name="viewport" content="width=device-width, initial-scale=1.0">    <script src="https://unpkg.com/image3d@3"></script>    <style>        body {            margin: 0;            text-align: center;            overflow: hidden;        }    </style>    <!-- 顶点着色器 -->    <script type='x-shader/x-vertex' id='vs'>        attribute vec4 a_position;        uniform mat4 u_matrix;        attribute vec4 a_color;        varying vec4 v_color;        void main(){            gl_Position=u_matrix * a_position;            v_color=a_color;        }    </script>    <!-- 片段着色器 -->    <script type='x-shader/x-fragment' id='fs'>        precision mediump float;        varying vec4 v_color;        void main(){            gl_FragColor=v_color;        }    </script></head><body>    <canvas width='300' height='300'>十分道歉,您的浏览器不反对canvas!</canvas>    <script>        // 创立3D对象并配置好画布和着色器        var image3d = new image3D(document.getElementsByTagName('canvas')[0], {            "vertex-shader": document.getElementById("vs").innerText,            "fragment-shader": document.getElementById("fs").innerText,            depth: true        });        /**         * 许多的点,三个点拼接成一个三角形,         * 每个点由6个数据组成,前3个示意点的地位,后3个示意点的色彩         * 而一个个三角形拼接成最终的图形        */        var points = [];        // 一个依据值来确定色彩的办法        var valToColor = function (val) {            return [val * 0.5, 0.5, 1 - val * 0.5];        }        var dist = 0.02;        var color, val;        // y=x2+z2;        for (var x = -1; x < 1; x += dist) {            for (var z = -1; z <= 1; z += dist) {                // 左上三角形                val = x * x + z * z;                color = valToColor(val);                points.push(x, val, z, color[0], color[1], color[2]);                val = (x + dist) * (x + dist) + z * z;                points.push(x + dist, val, z, color[0], color[1], color[2]);                val = x * x + (z + dist) * (z + dist);                points.push(x, val, z + dist, color[0], color[1], color[2]);                // 右下三角形                val = (x + dist) * (x + dist) + z * z;                color = valToColor(val);                points.push(x + dist, val, z, color[0], color[1], color[2]);                val = x * x + (z + dist) * (z + dist);                points.push(x, val, z + dist, color[0], color[1], color[2]);                val = (x + dist) * (x + dist) + (z + dist) * (z + dist);                points.push(x + dist, val, z + dist, color[0], color[1], color[2]);            }        }        // 点的个数        var num = points.length / 6;        // 点的坐标        image3d.Buffer().write(new Float32Array(points)).use('a_position', 3, 6, 0).use('a_color', 3, 6, 3);        var painter = image3d.Painter();        //  创立相机对象        var camera = image3d.Camera({            size: 2        }).rotateBody(0.9, 0, 1, 0).rotateBody(0.3, 1, 0, 0, 0, 0, 1).moveBody(0.5, 0, -1, 0);        setInterval(function () {            camera.rotateBody(0.05, 0, 1, 0);            // 传递照相机            image3d.setUniformMatrix("u_matrix",                camera.value()            );            // 绘制            painter.drawTriangle(0, num);        }, 30);    </script></body></html>