WebGL技术储备指南,技术储备指南

皇家赌场手机版 ,WebGL技术储备指南

2015/12/22 · HTML5 · 1
评论 ·
WebGL

原稿出处: 天猫前端团队(FED)-
叶斋   

皇家赌场手机版 1

WebGL 是 HTML 5 草案的一有的,能够使得 Canvas 渲染三维场景。WebGL
就算还未有广泛应用,但极具潜力和设想空间。本文是自身上学 WebGL
时梳理知识系统的产物,花点时间整理出来与我们享受。

WebGL 是 HTML 5 草案的一某些,能够使得 Canvas 渲染三维场景。WebGL
纵然还未有广泛应用,但极具潜力和想象空间。本文是自个儿就学 WebGL
时梳理知识系统的产物,花点时间整理出来与我们分享。

WebGL 是 HTML 5 草案的一片段,能够使得 Canvas 渲染三维场景。WebGL
即使还未有广泛应用,但极具潜力和设想空间。本文是本身就学 WebGL
时梳理知识系统的产物,花点时间整理出来与我们大快朵颐。

着色器只可以用在OpenGLES 2.X上述等可编制程序管道里,而在OpenGLES
1.X是不能够利用的。

示例

WebGL 很酷,有以下 demos 为证:

检索奥兹国
赛车游戏
泛舟的男孩(Goo
Engine Demo)

示例

WebGL 很酷,有以下 demos 为证:

搜索奥兹国
超跑游戏
泛舟的男孩(Goo
Engine Demo)

示例

管线,Pipeline,显卡执行的、从几何体到最后渲染图像的、数据传输处理总括的进度

本文的对象

本文的意料读者是:不熟知图形学,熟习前端,希望通晓或系统学习 WebGL
的同学。

正文不是 WebGL 的概述性小说,也不是总体详细的 WebGL
教程。本文只希望变成一篇供 WebGL 初学者使用的提纲。

正文的靶子

正文的料想读者是:素不相识图形学,熟谙前端,希望理解或种类学习 WebGL
的同校。

本文不是 WebGL 的概述性小说,也不是完全详细的 WebGL
教程。本文只希望成为一篇供 WebGL 初学者使用的提纲。

WebGL 很酷,有以下 demos 为证:

OpenGLES1.X中它是固定管道,全部式封闭的,中间的各道工艺按一定的流程顺序走。如图所示:

Canvas

WebGL技术储备指南,技术储备指南。熟知 Canvas 的校友都领会,Canvas 绘图先要获取绘图上下文:

JavaScript

var context = canvas.getContext(‘2d’);

1
var context = canvas.getContext(‘2d’);

context上调用各样函数绘制图形,比如:

JavaScript

// 绘制左上角为(0,0),右下角为(50, 50)的矩形 context.fillRect(0, 0, 50,
50);

1
2
// 绘制左上角为(0,0),右下角为(50, 50)的矩形
context.fillRect(0, 0, 50, 50);

WebGL 同样须求得到绘图上下文:

JavaScript

var gl = canvas.getContext(‘webgl’); // 或 experimental-webgl

1
var gl = canvas.getContext(‘webgl’); // 或 experimental-webgl

但是接下去,假设想画叁个矩形的话,就没这么简单了。实际上,Canvas
是浏览器封装好的三个制图环境,在实质上举办绘图操作时,浏览器还是需求调用
OpenGL API。而 WebGL API 差不多就是 OpenGL API 未经封装,直接套了一层壳。

Canvas 的更加多文化,能够参见:

  • JS
    权威指南的
    21.4 节或 JS
    高级程序设计中的
    15 章
  • W3CSchool
  • 阮一峰的 Canvas
    教程

Canvas

熟识 Canvas 的同桌都明白,Canvas 绘图先要获取绘图上下文:

var context = canvas.getContext('2d');

context上调用各类函数绘制图形,比如:

// 绘制左上角为(0,0),右下角为(50, 50)的矩形
context.fillRect(0, 0, 50, 50);

WebGL 同样须要取得绘图上下文:

var gl = canvas.getContext('webgl'); // 或 experimental-webgl

可是接下去,如若想画贰个矩形的话,就没那样不难了。实际上,Canvas
是浏览器封装好的三个绘制环境,在骨子里开始展览绘图操作时,浏览器还是需求调用
OpenGL API。而 WebGL API 大致正是 OpenGL API 未经封装,直接套了一层壳。

Canvas 的更多知识,能够参见:

  • JS
    权威指南的
    21.4 节或 JS
    高级程序设计中的
    15 章
  • W3CSchool
  • 阮一峰的 Canvas
    教程

搜索奥兹国

皇家赌场手机版 2

矩阵变换

三维模型,从文件中读出来,到绘制在 Canvas 中,经历了反复坐标变换。

假设有一个最简便易行的模子:三角形,多个终端分别为(-1,-1,0),(1,-1,0),(0,1,0)。那八个数据是从文件中读出来的,是三角形最起先的坐标(局地坐标)。如下图所示,右手坐标系。

皇家赌场手机版 3

模型日常不会放在场景的原点,假如三角形的原点位于(0,0,-1)处,没有转动或缩放,四个极端分别为(-1,-1,-1),(1,-1,-1),(0,1,-1),即世界坐标。

皇家赌场手机版 4

制图三维场景必须钦赐一个观看者,借使观看者位于(0,0,1)处而且看向三角形,那么四个极端相对于阅览者的坐标为(-1,-1,-2),(1,-1,-2),(0,1,-2),即视图坐标。

皇家赌场手机版 5

阅览者的肉眼是二个点(那是看破投影的前提),水平视角和垂直视角都以90度,视野范围(目力所及)为[0,2]在Z轴上,观望者能够见到的区域是1个四棱台体。

皇家赌场手机版 6

将四棱台体映射为行业内部立方(CCV,中央为原点,边长为2,边与坐标轴平行)。顶点在
CCV 中的坐标,离它说到底在 Canvas 中的坐标已经很类似了,假设把 CCV
的前表面看成 Canvas,那么最后三角形就画在图中血红三角形的地点。

皇家赌场手机版 7

上述变换是用矩阵来实行的。

一对坐标 –(模型变换)-> 世界坐标 –(视图变换)-> 视图坐标
–(投影变换)–> CCV 坐标。

以(0,1,0)为例,它的齐次向量为(0,0,1,1),上述变换的象征经过能够是:

皇家赌场手机版 8

上边四个矩阵依次是看破投影矩阵,视图矩阵,模型矩阵。四个矩阵的值分别取决于:观望者的见识和视野距离,观望者在世界中的状态(地点和动向),模型在世界中的状态(地点和方向)。总括的结果是(0,1,1,2),化成齐次坐标是(0,0.5,0.5,1),正是以此点在CCV中的坐标,那么(0,0.5)正是在Canvas中的坐标(认为
Canvas 中央为原点,长度宽度都为2)。

上面出现的(0,0,1,1)是(0,0,1)的齐次向量。齐次向量(x,y,z,w)能够表示三维向量(x,y,z)加入矩阵运算,通俗地说,w
分量为 1 时表示地方,w 分量为 0 时表示位移。

WebGL 没有提供别的关于上述变换的机制,开发者要求亲自计算顶点的 CCV
坐标。

关于坐标变换的越多内容,能够参照:

  • 总结机图形学中的5-7章
  • 转换矩阵@维基百科
  • 透视投影详解

相比复杂的是模型变换中的绕任意轴旋转(常常用四元数生成矩阵)和投影变换(上边的事例都没收涉及到)。

关于绕任意轴旋转和四元数,能够参照:

  • 四元数@维基百科
  • 2个鬼子对四元数公式的认证

关于齐次向量的更加多内容,可以参照。

  • 处理器图形学的5.2节
  • 齐次坐标@维基百科

矩阵变换

三维模型,从文件中读出来,到绘制在 Canvas 中,经历了反复坐标变换。

假设有多个最简便的模子:三角形,多个极端分别为(-1,-1,0),(1,-1,0),(0,1,0)。那多少个数据是从文件中读出来的,是三角形最开头的坐标(局地坐标)。如下图所示,右手坐标系。

皇家赌场手机版 9

模型常常不会放在场景的原点,倘若三角形的原点位于(0,0,-1)处,没有转动或缩放,四个顶峰分别为(-1,-1,-1),(1,-1,-1),(0,1,-1),即世界坐标。

皇家赌场手机版 10

制图三维场景必须钦赐二个旁观者,若是观望者位于(0,0,1)处而且看向三角形,那么七个顶峰相对于观望者的坐标为(-1,-1,-2),(1,-1,-2),(0,1,-2),即视图坐标。

皇家赌场手机版 11

观看者的肉眼是二个点(这是看破投影的前提),水平视角和垂直视角都以90度,视野范围(目力所及)为[0,2]在Z轴上,旁观者能够看出的区域是多少个四棱台体。

皇家赌场手机版 12

将四棱台体映射为行业内部立方(CCV,中央为原点,边长为2,边与坐标轴平行)。顶点在
CCV 中的坐标,离它说到底在 Canvas 中的坐标已经很类似了,即使把 CCV
的前表面看成 Canvas,那么最终三角形就画在图中天蓝三角形的职位。

皇家赌场手机版 13

上述变换是用矩阵来拓展的。

部分坐标 –(模型变换)-> 世界坐标 –(视图变换)-> 视图坐标
–(投影变换)–> CCV 坐标。

以(0,1,0)为例,它的齐次向量为(0,0,1,1),上述变换的意味经过能够是:

皇家赌场手机版 14

地点几个矩阵依次是看破投影矩阵,视图矩阵,模型矩阵。三个矩阵的值分别取决于:观看者的理念和视野距离,观望者在世界中的状态(地点和自由化),模型在世界中的状态(地方和趋势)。总计的结果是(0,1,1,2),化成齐次坐标是(0,0.5,0.5,1),就是以此点在CCV中的坐标,那么(0,0.5)正是在Canvas中的坐标(认为
Canvas 核心为原点,长度宽度都为2)。

地方出现的(0,0,1,1)是(0,0,1)的齐次向量。齐次向量(x,y,z,w)能够表示三维向量(x,y,z)参预矩阵运算,通俗地说,w
分量为 1 时表示地方,w 分量为 0 时表示位移。

WebGL 没有提供任何有关上述变换的建制,开发者须要亲自总计顶点的 CCV
坐标。

关于坐标变换的更加多内容,能够参照:

  • 处理器图形学中的5-7章
  • 改换矩阵@维基百科
  • 透视投影详解

比较复杂的是模型变换中的绕任意轴旋转(日常用四元数生成矩阵)和投影变换(上边的例子都没收涉及到)。

关于绕任意轴旋转和四元数,能够参照:

  • 四元数@维基百科
  • 一个鬼子对四元数公式的证实

有关齐次向量的更加多内容,能够参照。

  • 电脑图形学的5.2节
  • 齐次坐标@维基百科

赛车游戏

从上海体育场地能够看出,那么些工艺顺序是稳定的,整个经过又分为:处理顶点,处理片元,验证片元音讯并存入内部存款和储蓄器

着色器和光栅化

在 WebGL
中,开发者是透过着色器来成功上述变换的。着色器是运作在显卡中的程序,以
GLSL 语言编写,开发者要求将着色器的源码以字符串的格局传给 WebGL
上下文的有关函数。

着色器有三种,顶点着色器和片元(像素)着色器,它们成对出现。顶点着色器职责是吸收顶点的局地坐标,输出
CCV 坐标。CCV
坐标经过光栅化,转化为逐像素的数据,传给片元着色器。片元着色器的任务是鲜明各样片元的颜料。

极限着色器接收的是 attribute 变量,是逐顶点的多寡。顶点着色器输出
varying 变量,也是逐顶点的。逐顶点的 varying
变量数据经过光栅化,成为逐片元的 varying
变量数据,输入片元着色器,片元着色器输出的结果就会显得在 Canvas 上。

皇家赌场手机版 15

着色器效能很多,上述只是基本功效。大部分炫酷的效应都以凭借着色器的。假诺您对着色器完全没有定义,能够试着明亮下一节
hello world 程序中的着色器再回首一下本节。

有关更加多着色器的知识,可以参考:

  • GLSL@维基百科
  • WebGL@MSDN

着色器和光栅化

在 WebGL
中,开发者是因而着色器来形成上述变换的。着色器是运行在显卡中的程序,以
GLSL 语言编写,开发者须要将着色器的源码以字符串的款式传给 WebGL
上下文的连锁函数。

着色器有三种,顶点着色器和片元(像素)着色器,它们成对出现。顶点着色器任务是接到顶点的部分坐标,输出
CCV 坐标。CCV
坐标经过光栅化,转化为逐像素的数目,传给片元着色器。片元着色器的天职是规定各样片元的颜色。

极端着色器接收的是 attribute 变量,是逐顶点的数量。顶点着色器输出
varying 变量,也是逐顶点的。逐顶点的 varying
变量数据通过光栅化,成为逐片元的 varying
变量数据,输入片元着色器,片元着色器输出的结果就会来得在 Canvas 上。

皇家赌场手机版 16

着色器功用很多,上述只是基本功效。抢先四分之二炫酷的机能都以凭借着色器的。要是你对着色器完全没有定义,能够试着明亮下一节
hello world 程序中的着色器再回想一下本节。

至于更加多着色器的学问,能够参考:

  • GLSL@维基百科
  • WebGL@MSDN

泛舟的男孩(Goo
EngineDemo)

Rasterizer:光栅化处理,当顶点处理完,会提交rasterizer来展开光栅化处理,结果会吧顶点的坐标音讯转换来能在显示屏显示的像素消息,即片元(Fragments)

程序

这一节解释绘制上述场景(三角形)的 WebGL
程序。点本条链接,查看源代码,试图了然一下。那段代码出自WebGL
Programming
GuideWebGL技术储备指南,技术储备指南。,作者作了部分修改以适应本文内容。要是一切不奇怪,你看到的应当是底下那样:

皇家赌场手机版 17

诠释几点(如若在此以前不精通 WebGL ,多半会对下边包车型地铁代码猜忌,无碍):

  1. 字符串 VSHADER_SOURCE 和 FSHADER_SOU翼虎CE
    是终端着色器和片元着色器的源码。能够将着色器通晓为有固定输入和出口格式的主次。开发者须求事先编写好着色器,再依照一定格式着色器发送绘图命令。
  2. Part2 将着色器源码编写翻译为 program
    对象:先分别编译顶点着色器和片元着色器,然后连接两者。假使编写翻译源码错误,不会报
    JS 错误,但可以通过其余API(如gl.getShaderInfo等)获取编写翻译状态消息(成功与否,如若出错的错误新闻)。
JavaScript

// 顶点着色器 var vshader = gl.createShader(gl.VERTEX\_SHADER);
gl.shaderSource(vshader, VSHADER\_SOURCE);
gl.compileShader(vshader); // 同样新建 fshader var program =
gl.createProgram(); gl.attachShader(program, vshader);
gl.attachShader(program, fshader); gl.linkProgram(program);

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f14b3a671c960813930-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f14b3a671c960813930-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f14b3a671c960813930-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f14b3a671c960813930-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f14b3a671c960813930-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f14b3a671c960813930-6">
6
</div>
<div class="crayon-num" data-line="crayon-5b8f14b3a671c960813930-7">
7
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f14b3a671c960813930-8">
8
</div>
<div class="crayon-num" data-line="crayon-5b8f14b3a671c960813930-9">
9
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f14b3a671c960813930-1" class="crayon-line">
// 顶点着色器
</div>
<div id="crayon-5b8f14b3a671c960813930-2" class="crayon-line crayon-striped-line">
var vshader = gl.createShader(gl.VERTEX_SHADER);
</div>
<div id="crayon-5b8f14b3a671c960813930-3" class="crayon-line">
gl.shaderSource(vshader, VSHADER_SOURCE);
</div>
<div id="crayon-5b8f14b3a671c960813930-4" class="crayon-line crayon-striped-line">
gl.compileShader(vshader);
</div>
<div id="crayon-5b8f14b3a671c960813930-5" class="crayon-line">
// 同样新建 fshader
</div>
<div id="crayon-5b8f14b3a671c960813930-6" class="crayon-line crayon-striped-line">
var program = gl.createProgram();
</div>
<div id="crayon-5b8f14b3a671c960813930-7" class="crayon-line">
gl.attachShader(program, vshader);
</div>
<div id="crayon-5b8f14b3a671c960813930-8" class="crayon-line crayon-striped-line">
gl.attachShader(program, fshader);
</div>
<div id="crayon-5b8f14b3a671c960813930-9" class="crayon-line">
gl.linkProgram(program);
</div>
</div></td>
</tr>
</tbody>
</table>
  1. program
    对象须要钦定使用它,才足以向着色器传数据并绘制。复杂的顺序通常有多少个program 对 象,(绘制每一帧时)通过切换 program
    对象绘制场景中的不一样作用。
JavaScript

gl.useProgram(program);

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f14b3a6720232020477-1">
1
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f14b3a6720232020477-1" class="crayon-line">
gl.useProgram(program);
</div>
</div></td>
</tr>
</tbody>
</table>
  1. Part3 向正在使用的着色器传入数据,包涵逐顶点的 attribute
    变量和全局的 uniform 变量。向着色器传入数据必须使用
    ArrayBuffer,而不是健康的 JS 数组。
JavaScript

var varray = new Float32Array(\[-1, -1, 0, 1, -1, 0, 0, 1, 0\])

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f14b3a6723482450329-1">
1
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f14b3a6723482450329-1" class="crayon-line">
var varray = new Float32Array([-1, -1, 0, 1, -1, 0, 0, 1, 0])
</div>
</div></td>
</tr>
</tbody>
</table>
  1. WebGL API 对 ArrayBuffer
    的操作(填充缓冲区,传入着色器,绘制等)都以由此 gl.AKugaRAY_BUFFE宝马7系进行的。在 WebGL 系统中又很多像样的图景。
JavaScript

// 只有将 vbuffer 绑定到 gl.ARRAY\_BUFFER,才可以填充数据
gl.bindBuffer(gl.ARRAY\_BUFFER, vbuffer); // 这里的意思是,向“绑定到
gl.ARRAY\_BUFFER”的缓冲区中填充数据 gl.bufferData(gl.ARRAY\_BUFFER,
varray, gl.STATIC\_DRAW); // 获取 a\_Position
变量在着色器程序中的位置,参考顶点着色器源码 var aloc =
gl.getAttribLocation(program, 'a\_Position'); // 将 gl.ARRAY\_BUFFER
中的数据传入 aloc 表示的变量,即 a\_Position
gl.vertexAttribPointer(aloc, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aloc);

<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<tbody>
<tr class="odd">
<td><div class="crayon-nums-content" style="font-size: 13px !important; line-height: 15px !important;">
<div class="crayon-num" data-line="crayon-5b8f14b3a6727492492738-1">
1
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f14b3a6727492492738-2">
2
</div>
<div class="crayon-num" data-line="crayon-5b8f14b3a6727492492738-3">
3
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f14b3a6727492492738-4">
4
</div>
<div class="crayon-num" data-line="crayon-5b8f14b3a6727492492738-5">
5
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f14b3a6727492492738-6">
6
</div>
<div class="crayon-num" data-line="crayon-5b8f14b3a6727492492738-7">
7
</div>
<div class="crayon-num crayon-striped-num" data-line="crayon-5b8f14b3a6727492492738-8">
8
</div>
<div class="crayon-num" data-line="crayon-5b8f14b3a6727492492738-9">
9
</div>
</div></td>
<td><div class="crayon-pre" style="font-size: 13px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;">
<div id="crayon-5b8f14b3a6727492492738-1" class="crayon-line">
// 只有将 vbuffer 绑定到 gl.ARRAY_BUFFER,才可以填充数据
</div>
<div id="crayon-5b8f14b3a6727492492738-2" class="crayon-line crayon-striped-line">
gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer);
</div>
<div id="crayon-5b8f14b3a6727492492738-3" class="crayon-line">
// 这里的意思是,向“绑定到 gl.ARRAY_BUFFER”的缓冲区中填充数据
</div>
<div id="crayon-5b8f14b3a6727492492738-4" class="crayon-line crayon-striped-line">
gl.bufferData(gl.ARRAY_BUFFER, varray, gl.STATIC_DRAW);
</div>
<div id="crayon-5b8f14b3a6727492492738-5" class="crayon-line">
// 获取 a_Position 变量在着色器程序中的位置,参考顶点着色器源码
</div>
<div id="crayon-5b8f14b3a6727492492738-6" class="crayon-line crayon-striped-line">
var aloc = gl.getAttribLocation(program, 'a_Position');
</div>
<div id="crayon-5b8f14b3a6727492492738-7" class="crayon-line">
// 将 gl.ARRAY_BUFFER 中的数据传入 aloc 表示的变量,即 a_Position
</div>
<div id="crayon-5b8f14b3a6727492492738-8" class="crayon-line crayon-striped-line">
gl.vertexAttribPointer(aloc, 3, gl.FLOAT, false, 0, 0);
</div>
<div id="crayon-5b8f14b3a6727492492738-9" class="crayon-line">
gl.enableVertexAttribArray(aloc);
</div>
</div></td>
</tr>
</tbody>
</table>
  1. 向着色器传入矩阵时,是按列存款和储蓄的。能够相比较一下 mmatrix
    和矩阵变换一节中的模型矩阵(第 3 个)。
  2. 极端着色器计算出的 gl_Position 正是 CCV
    中的坐标,比如最下面的终点(木色)的 gl_Position
    化成齐次坐标便是(0,0.5,0.5,1)。
  3. 向终点着色器传入的只是八个顶峰的颜料值,而三角形表面包车型客车颜料渐变是由那四个颜色值内插出的。光栅化不仅会对
    gl_Position 实行,还会对 varying 变量插值。
  4. gl.drawArrays()方法使得缓冲区实行绘图,gl.TSportageIANGLES
    钦定绘制三角形,也足以转移参数绘制点、折线等等。

有关 ArrayBuffer 的详细音讯,能够参考:

  • ArrayBuffer@MDN
  • 阮一峰的 ArrayBuffer
    介绍
  • 张鑫旭的 ArrayBuffer
    介绍

有关 gl.T凯雷德IANGLES
等别的绘制方式,能够参考上面那张图或那篇博文。

皇家赌场手机版 18

程序

这一节解释绘制上述现象(三角形)的 WebGL
程序。点本条链接,查看源代码,试图精通一下。那段代码出自WebGL
Programming
Guide,小编作了一部分改动以适应本文内容。若是一切经常,你看来的相应是上边那样:

皇家赌场手机版 19

说明几点(假如以前不了然 WebGL ,多半会对下边包车型大巴代码嫌疑,无碍):

  1. 字符串 VSHADER_SOURCE 和 FSHADER_SOUSportageCE
    是终点着色器和片元着色器的源码。能够将着色器通晓为有定位输入和出口格式的顺序。开发者须求事先编写好着色器,再根据一定格式着色器发送绘图命令。

  2. Part2 将着色器源码编写翻译为 program
    对象:先分别编写翻译顶点着色器和片元着色器,然后连接两者。假设编译源码错误,不会报
    JS 错误,但能够通过别的API(如gl.getShaderInfo等)获取编写翻译状态新闻(成功与否,要是出错的错误消息)。

    // 顶点着色器
    var vshader = gl.createShader(gl.VERTEX_SHADER);
    gl.shaderSource(vshader, VSHADER_SOURCE);
    gl.compileShader(vshader);
    // 同样新建 fshader
    var program = gl.createProgram();
    gl.attachShader(program, vshader);
    gl.attachShader(program, fshader);
    gl.linkProgram(program);
    
  3. program
    对象要求钦点使用它,才方可向着色器传数据并绘制。复杂的程序平日有多少个program 对 象,(绘制每一帧时)通过切换 program
    对象绘制场景中的差别效率。

    gl.useProgram(program);
    
  4. Part3 向正在选拔的着色器传入数据,包罗逐顶点的 attribute
    变量和大局的 uniform 变量。向着色器传入数据必须运用
    ArrayBuffer,而不是例行的 JS 数组。

    var varray = new Float32Array([-1, -1, 0, 1, -1, 0, 0, 1, 0])
    
  5. WebGL API 对 ArrayBuffer
    的操作(填充缓冲区,传入着色器,绘制等)都以通过 gl.ARAV4RAY_BUFFE汉兰达进行的。在 WebGL 系统中又很多类似的事态。

    // 只有将 vbuffer 绑定到 gl.ARRAY_BUFFER,才可以填充数据
    gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer);
    // 这里的意思是,向“绑定到 gl.ARRAY_BUFFER”的缓冲区中填充数据
    gl.bufferData(gl.ARRAY_BUFFER, varray, gl.STATIC_DRAW);
    // 获取 a_Position 变量在着色器程序中的位置,参考顶点着色器源码
    var aloc = gl.getAttribLocation(program, 'a_Position');
    // 将 gl.ARRAY_BUFFER 中的数据传入 aloc 表示的变量,即 a_Position
    gl.vertexAttribPointer(aloc, 3, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(aloc);
    
  6. 向着色器传入矩阵时,是按列存款和储蓄的。能够相比一下 mmatrix
    和矩阵变换一节中的模型矩阵(第 3 个)。

  7. 极限着色器总结出的 gl_Position 正是 CCV
    中的坐标,比如最上边的终端(银灰)的 gl_Position
    化成齐次坐标正是(0,0.5,0.5,1)。

  8. 向终极着色器传入的只是多少个极点的水彩值,而三角形表面包车型客车颜料渐变是由这八个颜色值内插出的。光栅化不仅会对
    gl_Position 进行,还会对 varying 变量插值。

  9. gl.drawArrays()方法使得缓冲区举办绘图,gl.T帕杰罗IANGLES
    钦命绘制三角形,也得以转移参数绘制点、折线等等。

有关 ArrayBuffer 的详细音讯,能够参考:

  • ArrayBuffer@MDN
  • 阮一峰的 ArrayBuffer
    介绍
  • 张鑫旭的 ArrayBuffer
    介绍

有关 gl.TMuranoIANGLES
等此外绘制情势,能够参考下边那张图或那篇博文。

皇家赌场手机版 20

本文的靶子

生成片元后,接下去就是对fragments片元的各个注解,即过滤掉无用的片元,裁剪掉不在视野内的片元,最后把实用片元存款和储蓄入内部存储器中。

深度检查和测试

当几个外表重叠时,前边的模子会遮掩前面包车型大巴模子。比如本条例子,绘制了多少个交叉的三角(
varray 和 carray 的尺寸变为 18,gl.drawArrays 最后一个参数变为
6)。为了简单,那些例子去掉了矩阵变换进度,直接向着色器传入 CCV 坐标。

皇家赌场手机版 21

皇家赌场手机版 22

顶点着色器给出了 6 个顶峰的 gl_Position ,经过光栅化,片元着色器获得了
2X 个片元(假如 X 为各类三角形的像素个数),每一个片元都离散的 x,y
坐标值,还有 z 值。x,y 坐标正是三角形在 Canvas
上的坐标,但假若有四个具有相同 x,y 坐标的片元同时出现,那么 WebGL
就会取 z 坐标值较小的不胜片元。

在深度检查和测试以前,必须在绘制前拉开一个常量。不然,WebGL 就会根据在 varray
中定义的次第绘制了,后边的会覆盖后边的。

JavaScript

gl.enable(gl.DEPTH_TEST);

1
gl.enable(gl.DEPTH_TEST);

其实,WebGL 的逻辑是那样的:依次拍卖片元,固然渲染缓冲区(那里就是Canvas
了)的百般与当前片元对应的像素还从未绘制时,就把片元的颜料画到渲染缓冲区对应像素里,同时把片元的
z
值缓存在另1个纵深缓冲区的一致地点;假若当前缓冲区的呼应像素已经绘制过了,就去查看深度缓冲区中对应地点的
z 值,要是当前片元 z 值小,就重绘,不然就屏弃当前片元。

WebGL 的那套逻辑,对理解蒙版(后边会说到)有一些增派。

纵深检查和测试

当两个外表重叠时,前边的模子会遮掩前边的模型。比如那么些事例,绘制了三个交叉的三角(
varray 和 carray 的长度变为 18,gl.drawArrays 最终2个参数变为
6)。为了简单,那一个例子去掉了矩阵变换进度,直接向着色器传入 CCV 坐标。

皇家赌场手机版 23

皇家赌场手机版 24

终点着色器给出了 6 个极点的 gl_Position ,经过光栅化,片元着色器获得了
2X 个片元(如果 X 为各种三角形的像素个数),每一种片元都离散的 x,y
坐标值,还有 z 值。x,y 坐标正是三角形在 Canvas
上的坐标,但一旦有七个有着同等 x,y 坐标的片元同时出现,那么 WebGL
就会取 z 坐标值较小的十分片元。

在深度检查和测试以前,必须在绘制前拉开叁个常量。不然,WebGL 就会依据在 varray
中定义的一一绘制了,前边的会覆盖前边的。

gl.enable(gl.DEPTH_TEST);

实际上,WebGL 的逻辑是这么的:依次拍卖片元,如若渲染缓冲区(那里便是Canvas
了)的特别与当前片元对应的像素还一直不绘制时,就把片元的水彩画到渲染缓冲区对应像素里,同时把片元的
z
值缓存在另2个纵深缓冲区的一模一样地方;假设当前缓冲区的对应像素已经绘制过了,就去查看深度缓冲区中对应地方的
z 值,假如当前片元 z 值小,就重绘,不然就丢弃当前片元。

WebGL 的那套逻辑,对精晓蒙版(前边会说到)有局地帮手。

本文的意料读者是:不纯熟图形学,纯熟前端,希望精晓或系统学习 WebGL
的同桌。

光栅化处理进度,就是把矢量图转化成像素点的进度。大家显示器上出示的镜头都以由像素结合,而三维物体都是点线面构成的。要让点线面变成能在显示器上海展览中心示的像素,就要求Rasterizer那些进度。

顶点索引

gl.drawArrays()是安分守己顶点的相继绘制的,而
gl.drawElements()能够令着色器以二个索引数组为顺序绘制顶点。比如以此例子。

皇家赌场手机版 25

那边画了多少个三角形,但只用了 五个终端,有1个终极被多个三角共用。那时必要建立索引数组,数组的种种成分表示顶点的索引值。将数组填充至gl.ELEMENT_ARRAY,然后调用
gl.drawElements()。

JavaScript

var iarray = new Uint8Array([0,1,2,2,3,4]); var ibuffer =
gl.createBuffer(gl.ARRAY_BUFFER, ibuffer);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, iarray, gl.STATIC_DRAW);

1
2
3
4
var iarray = new Uint8Array([0,1,2,2,3,4]);
var ibuffer = gl.createBuffer(gl.ARRAY_BUFFER, ibuffer);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, iarray, gl.STATIC_DRAW);

顶点索引

gl.drawArrays()是依照顶点的逐一绘制的,而
gl.drawElements()能够令着色器以1个索引数组为顺序绘制顶点。比如本条例子。

皇家赌场手机版 26

此地画了七个三角形,但只用了 多少个终端,有三个终端被七个三角共用。那时须要建立索引数组,数组的每种成分表示顶点的索引值。将数组填充至gl.ELEMENT_ARRAY,然后调用
gl.drawElements()。

var iarray = new Uint8Array([0,1,2,2,3,4]);
var ibuffer = gl.createBuffer(gl.ARRAY_BUFFER, ibuffer);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, iarray, gl.STATIC_DRAW);

本文不是 WebGL 的概述性文章,也不是完整详细的 WebGL
教程。本文只希望成为一篇供 WebGL 初学者使用的提纲。

OpenGLES2.X可编制程序管道,由两VertexShader(顶点着色器)、FragmentsShader(片元着色器)组成,分别对应上海教室中的Coordinates
和Texture等紫褐块

纹理

attribute
变量不仅能够传递顶点的坐标,还能传递别的任何逐顶点的多寡。比如
HelloTriangle 程序把单个顶点的颜色传入了 a_Color,片元着色器收到
v_Color 后从来赋给 gl_FragmentColor,就控制了颜色。

attribute
变量还足以帮忙绘制纹理。绘制纹理的基本原理是,为各类终端钦点一个纹理坐标(在(0,0)与(1,1,)的方框形中),然后传入纹理对象。片元着色器得到的是对应片元的内插后的纹理坐标,就应用这一个纹理坐标去纹理对象上取颜色,再画到片元上。内插后的纹理坐标很或许不正好对应纹理上的有个别像素,而是在几个像素之间(因为一般的图样纹理也是离散),那时只怕会透过周围多少个像素的加权平均算出该像素的值(具体有若干种不相同格局,能够参考)。

比如本条事例。

皇家赌场手机版 27

纹理对象和缓冲区目的很类似:使用 gl 的 API 函数创制,须要绑定至常量
gl.A汉兰达RAY_BUFFER 和 gl.TEXTURE_2D
,都通过常量对象向里面填入图像和数据。不相同的是,纹理对象在绑定时还亟需激活多少个纹理单元(此处的gl.TEXTURE0),而
WebGL 系统接济的纹路单元个数是很有限的(一般为 8 个)。

JavaScript

var texture = gl.createTexture();
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
gl.activeTexture(gl.TEXTURE0); 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,
textureImage); var sloc = gl.getUniformLocation(program, ‘u_Sampler’);
gl.uniform1i(sloc, 0);

1
2
3
4
5
6
7
8
var texture = gl.createTexture();
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
gl.activeTexture(gl.TEXTURE0);
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, textureImage);
var sloc = gl.getUniformLocation(program, ‘u_Sampler’);
gl.uniform1i(sloc, 0);

片元着色器内评释了 sampler2D 类型的 uniform
变量,通过texture2D函数取样。

JavaScript

precision mediump float; uniform sampler2D u_Sampler; varying vec2
v_TexCoord; void main() { gl_FragColor = texture2D(u_Sampler,
v_TexCoord); };

1
2
3
4
5
6
precision mediump float;
uniform sampler2D u_Sampler;
varying vec2 v_TexCoord;
void main() {
  gl_FragColor = texture2D(u_Sampler, v_TexCoord);
};

纹理

attribute
变量不仅能够传递顶点的坐标,还能传递别的任何逐顶点的数据。比如
HelloTriangle 程序把单个顶点的颜色传入了 a_Color,片元着色器收到
v_Color 后直接赋给 gl_FragmentColor,就决定了颜色。

attribute
变量还足以帮助绘制纹理。绘制纹理的基本原理是,为种种终端钦定3个纹理坐标(在(0,0)与(1,1,)的方框形中),然后传入纹理对象。片元着色器获得的是对应片元的内插后的纹路坐标,就动用这些纹理坐标去纹理对象上取颜色,再画到片元上。内插后的纹理坐标很可能不凑巧对应纹理上的有个别像素,而是在几个像素之间(因为一般而言的图形纹理也是离散),那时恐怕会经过周围多少个像素的加权平均算出该像素的值(具体有好多样分化措施,能够参考)。

比如以此事例。

皇家赌场手机版 28

纹理对象和缓冲区指标很接近:使用 gl 的 API 函数创设,须要绑定至常量
gl.AOdysseyRAY_BUFFER 和 gl.TEXTURE_2D
,都因而常量对象向里面填入图像和数据。不相同的是,纹理对象在绑定时还索要激活八个纹理单元(此处的gl.TEXTURE0),而
WebGL 系统帮忙的纹路单元个数是很不难的(一般为 8 个)。

var texture = gl.createTexture();
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
gl.activeTexture(gl.TEXTURE0);
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, textureImage);
var sloc = gl.getUniformLocation(program, 'u_Sampler');
gl.uniform1i(sloc, 0);

片元着色器内注解了 sampler2D 类型的 uniform
变量,通过texture2D函数取样。

precision mediump float;
uniform sampler2D u_Sampler;
varying vec2 v_TexCoord;
void main() {
  gl_FragColor = texture2D(u_Sampler, v_TexCoord);
};

Canvas

OpenGLES2.0可渲染管道图:

混合与蒙版

晶莹剔透效果是用混合机制形成的。混合机制与深度检查和测试类似,也时有产生在盘算向有些已填写的像素填充颜色时。深度检查和测试通过相比较z值来分明像素的水彩,而掺杂机制会将三种颜色混合。比如这些事例。

皇家赌场手机版 29

混合的次第是遵照绘制的次第实行的,假设绘制的顺序有变动,混合的结果日常也不比。假使模型既有非透明表面又有透明表面,绘制透明表面时打开蒙版,其指标是锁定深度缓冲区,因为半透明物体后边的物体照旧得以见见的,要是不这么做,半透明物体前面包车型客车物体将会被深度检查和测试机制排除。

拉开混合的代码如下。gl.blendFunc主意钦定了交集的不二法门,那里的意趣是,使用源(待混合)颜色的
α 值乘以源颜色,加上 1-[源颜色的 α]乘以目的颜色。

JavaScript

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

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

所谓 α 值,正是颜色的第 4 个轻重。

JavaScript

var carray = new Float32Array([ 1,0,0,0.7,1,0,0,0.7,1,0,0,0.7,
0,0,1,0.4,0,0,1,0.4,0,0,1,0.4 ]);

1
2
3
4
var carray = new Float32Array([
  1,0,0,0.7,1,0,0,0.7,1,0,0,0.7,
  0,0,1,0.4,0,0,1,0.4,0,0,1,0.4
  ]);

错落与蒙版

晶莹剔透效果是用混合机制成功的。混合机制与深度检查和测试类似,也发生在打算向有个别已填写的像素填充颜色时。深度检查和测试通过相比z值来规定像素的颜色,而掺杂机制会将三种颜色混合。比如其一事例。

皇家赌场手机版 30

掺杂的种种是比照绘制的逐条举办的,假诺绘制的逐条有转移,混合的结果经常也不比。借使模型既有非透明表面又有晶莹剔透表面,绘制透明表面时打开蒙版,其目标是锁定深度缓冲区,因为半晶莹剔透物体前面包车型大巴物体仍是可以见到的,假如不这么做,半晶莹剔透物体前边的实体将会被深度检查和测试机制排除。

打开混合的代码如下。gl.blendFunc措施钦点了混合的办法,那里的意味是,使用源(待混合)颜色的
α 值乘以源颜色,加上 1-[源颜色的 α]乘以指标颜色。

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

所谓 α 值,就是颜色的第 4 个轻重。

var carray = new Float32Array([
  1,0,0,0.7,1,0,0,0.7,1,0,0,0.7,
  0,0,1,0.4,0,0,1,0.4,0,0,1,0.4
  ]);

纯熟 Canvas 的同桌都知情,Canvas 绘图先要获取绘图上下文:

皇家赌场手机版 31

浏览器的WebGL系统

WebGL 系统依次组成都部队分在既定规则下相互协作。稍作梳理如下。

皇家赌场手机版 32

那张图相比较自由,箭头上的文字表示
API,箭头方向大约表现了数额的流淌方向,不必深究。

浏览器的WebGL系统

WebGL 系统依次组成都部队分在既定规则下互相同盟。稍作梳理如下。

皇家赌场手机版 33

那张图相比较轻易,箭头上的文字表示
API,箭头方向大约表现了数码的流动方向,不必深究。

var context = canvas.getContext(‘2d’);

VertexShader:顶点着色器

光照

WebGL 没有为光照提供任何内置的点子,必要开发者在着色器中落到实处光照算法。

只但是有颜色的,模型也是有颜色的。在光照下,最后物体呈现的颜色是两者联手效能的结果。

贯彻光照的办法是:将光照的多少(点光源的地点,平行光的取向,以及光的水彩和强度)作为
uniform 变量传入着色器中,将物身体表面面每一个顶点处的法线作为 attribute
变量传入着色器,遵循光照规则,修订最后片元展现的水彩。

光照又分为逐顶点的和逐片元的,两者的分歧是,将法线光线交角因素位居顶点着色器初级中学结束学业生升学考试虑还是放在片元着色器初级中学完成学业生升学考试虑。逐片元光照更是绘身绘色,三个不过的事例是:

皇家赌场手机版 34

那会儿,点光源在相距1个表面较近处,表面宗旨 A
处较亮,四周较暗。不过在逐顶点光照下,表面包车型地铁水彩(的震慑因子)是由顶点内插出来的,所以表面中心也会比较暗。而逐片元光照直接行使片元的岗位和法线计算与点光源的交角,因而表面中心会比较亮。

光照

WebGL 没有为光照提供任何内置的措施,须求开发者在着色器中贯彻光照算法。

只然而有颜色的,模型也是有颜色的。在光照下,最后物体呈现的水彩是四头联手成效的结果。

福如东海光照的章程是:将光照的数量(点光源的岗位,平行光的大势,以及光的颜料和强度)作为
uniform 变量传入着色器中,将物体表面每种顶点处的法线作为 attribute
变量传入着色器,坚守光照规则,修订最终片元突显的颜料。

光照又分为逐顶点的和逐片元的,两者的界别是,将法线光线交角因素位居顶点着色器初级中学毕业生升学考试虑或许放在片元着色器初级中学完成学业生升学考试虑。逐片元光照更是维妙维肖,三个无比的例子是:

皇家赌场手机版 35

那时,点光源在距离1个外表较近处,表面核心 A
处较亮,四周较暗。可是在逐顶点光照下,表面包车型大巴颜料(的影响因子)是由顶点内插出来的,所以表面中心也会相比较暗。而逐片元光照直接动用片元的职务和法线总结与点光源的交角,因而表面中心会比较亮。

在context上调用种种函数绘制图形,比如:

终端着色器输入包罗:

复杂模型

复杂模型也许有包含子模型,子模型大概与父模型有相对运动。比如开着雨刮器的小车,雨刮器的世界坐标是受父模型汽车,和自家的情景共同决定的。若要总结雨刮器某顶点的任务,必要用雨刮器相对汽车的模型矩阵乘第北京小车工业控制股份股份两合公司车创建厂车的模型矩阵,再乘以顶点的一部分坐标。

复杂模型也许有成都百货上千外表,大概各样表面使用的着色器就差异。日常将模型拆解为组,使用相同着色器的外表为一组,先绘制同一组中的内容,然后切换着色器。每便切换着色器都要重复将缓冲区中的数据分配给着色器中相应变量。

复杂模型

复杂模型可能有包含子模型,子模型只怕与父模型有相对运动。比如开着雨刮器的汽车,雨刮器的世界坐标是受父模型小车,和本人的景况共同决定的。若要计算雨刮器某顶点的地点,需求用雨刮器绝对小车的模型矩阵乘北京小车工业控制股份有限公司车的模型矩阵,再乘以顶点的局地坐标。

复杂模型大概有很多外部,恐怕每一个表面使用的着色器就不一致。经常将模型拆解为组,使用同样着色器的外部为一组,先绘制同一组中的内容,然后切换着色器。每一次切换着色器都要再度将缓冲区中的数据分配给着色器中相应变量。

// 绘制左上角为(0,0),右下角为(50, 50)的矩形

着色器程序——描述顶点上进行操作的终极着色器程序源代码或然可执行文件

动画

动画的原理便是高速地擦除和重绘。常用的法子是红得发紫的
requestAnimationFrame
。不领悟的同室,能够参照正美的牵线。

动画

动画片的规律正是高效地擦除和重绘。常用的艺术是赫赫有名的
requestAnimationFrame
。不领悟的同校,能够参考正美的介绍。

context.fillRect(0, 0, 50, 50);

顶点着色器输入(或性质)——用极端数组提供的每种终端的多少

WebGL库

近日最流行的 WebGL 库是
ThreeJS,很强劲,官网,代码。

WebGL库

眼前最流行的 WebGL 库是
ThreeJS,很强大,官网,代码。

WebGL 同样须要取得绘图上下文:

联合变量(uniform)——顶点(或部分)着色器使用的不变多少

调剂工具

正如早熟的 WebGL 调节和测试工具是WebGL
Inspector。

调节工具

正如早熟的 WebGL 调节和测试工具是WebGL
Inspector。

var gl = canvas.getContext(‘webgl’); // 或 experimental-webgl

采集样品器——代表顶点着色器使用纹理的 特殊统一变量类型

网络财富和书籍

英文的有关 WebGL 的财富有过多,包含:

  • learning webgl
  • WebGL@MDN
  • WebGL Cheat
    Sheet

境内最早的 WebGL 教程是由郝稼力翻译的,放在 hiwebgl 上,如今 hiwebgl
已经倒闭,但教程还足以在这里找到。郝稼力近期运维着Lao3D。

国内曾经问世的 WebGL 书籍有:

  • WebGL入门指南:其实是一本讲
    ThreeJS 的书
  • WebGL高级编程:还能够的一本
  • WebGL编制程序指南:分外可信的宏观教程

最后再混合一点私货吧。读书时期自身曾花了小七个月时光翻译了一本WebGL的书,也等于下边包车型地铁第二本。那本书真的12分可信,网上各样课程里很多没说精晓的事物,那本书说得很驾驭,而且还提供了一份很完整的API文书档案。翻译那本书的经过也使自个儿收益匪浅。借使有同学愿意系统学一下
WebGL
的,建议价收购买一本(文青提出买英文版)。

1 赞 2 收藏 1
评论

皇家赌场手机版 36

互连网财富和本本

英文的有关 WebGL 的能源有过多,包蕴:

  • learning webgl
  • WebGL@MDN
  • WebGL Cheat
    Sheet

国内最早的 WebGL 教程是由郝稼力翻译的,放在 hiwebgl 上,如今 hiwebgl
已经关闭,但教程还是可以在这里找到。郝稼力近来营业着Lao3D。

国内已经出版的 WebGL 书籍有:

  • WebGL入门指南:其实是一本讲
    ThreeJS 的书
  • WebGL高级编制程序:还不易的一本
  • WebGL编制程序指南:极度可信赖的健全教程

只是接下去,假使想画四个矩形的话,就没那样容易了。实际上,Canvas
是浏览器封装好的1个绘制环境,在实际开始展览绘图操作时,浏览器照旧需求调用
OpenGL API。而 WebGL API 大约正是 OpenGL API 未经封装,直接套了一层壳。

极限着色器的出口在OpenGLES2.0称作可变变量(varying),但在OpenGLES3.0中改名为终端着色器输出变量。

Canvas 的更加多文化,能够参考:

在光栅化阶段,为各种生成的一部分总结顶点着色器输出值,并视作输入传递给一部分着色器。

JS
权威指南的
21.4 节或JS
高级程序设计中的
15 章

插值:光栅器对从极限着色器传递的变量实行插值

W3CSchool

为了在荧屏上的确显示,必须将顶点着色器vs的出口变量设置为gl_Position,gl_Position是2个封存着顶点齐次坐标的4维向量。ZYZ分量被W分量分割(称作视角分割)并且XYZ分量上超过单位化盒子([-1,
1])的一些会被裁剪掉。最后的结果会被更换成荧屏坐标系然后三角形(或任何图元类型)被光栅器生成对应的像素。

阮一峰的 Canvas
教程

OpenGLES3.0新增了几个功用——变换反馈,使顶点着色器输出能够采取性地写入二个输出缓冲区(除了传递给一部分着色器之外,也可用那种传递替代)

矩阵变换

终端着色器的输入和出口如下图所示:

三维模型,从文件中读出来,到绘制在 Canvas 中,经历了多次坐标变换。

皇家赌场手机版 37

一经有一个最简便易行的模型:三角形,多少个极端分别为(-1,-1,0),(1,-1,0),(0,1,0)。这多少个数据是从文件中读出来的,是三角形最开头的坐标(局地坐标)。如下图所示,右手坐标系。

先看看剧本:

皇家赌场手机版 38

private final String mVertexShader =

模型常常不会放在场景的原点,借使三角形的原点位于(0,0,-1)处,没有转动或缩放,四个极端分别为(-1,-1,-1),(1,-1,-1),(0,1,-1),即世界坐标。

“uniform mat4 uMVPMatrix;\n” +

皇家赌场手机版 39

“attribute vec4 aPosition;\n” +

绘制三维场景必须钦点一个观察者,假若观察者位于(0,0,1)处而且看向三角形,那么四个终端相对于观看者的坐标为(-1,-1,-2),(1,-1,-2),(0,1,-2),即视图坐标。

“attribute vec4 a_color;\n” +

皇家赌场手机版 40

“attribute vec2 aTextureCoord;\n” +

观察者的眸子是1个点(那是看破投影的前提),水平视角和垂直视角都是90度,视野范围(目力所及)为[0,2]在Z轴上,观看者能够见到的区域是三个四棱台体。

“varying vec2 vTextureCoord;\n” +

皇家赌场手机版 41

“out vec4 v_color;\n”

将四棱台体映射为正式立方(CCV,焦点为原点,边长为2,边与坐标轴平行)。顶点在
CCV 中的坐标,离它说到底在 Canvas 中的坐标已经很接近了,若是把 CCV
的前表面看成 Canvas,那么最后三角形就画在图中猩红三角形的岗位。

“void main() {\n” +

皇家赌场手机版 42

” gl_Position = uMVPMatrix * aPosition;\n” +

上述变换是用矩阵来展开的。

” vTextureCoord = aTextureCoord;\n” +

一些坐标 –(模型变换)-> 世界坐标 –(视图变换)-> 视图坐标
–(投影变换)–> CCV 坐标。

“ v_color = a_color;\n”

以(0,1,0)为例,它的齐次向量为(0,0,1,1),上述变换的代表经过能够是:

“}\n”;

皇家赌场手机版 43

private final String mFragmentShader =

地点多个矩阵依次是看破投影矩阵,视图矩阵,模型矩阵。三个矩阵的值分别取决于:观察者的观点和视野距离,观看者在世界中的状态(地方和取向),模型在世界中的状态(地点和自由化)。总结的结果是(0,1,1,2),化成齐次坐标是(0,0.5,0.5,1),正是以此点在CCV中的坐标,那么(0,0.5)便是在Canvas中的坐标(认为
Canvas 核心为原点,长度宽度都为2)。

“precision mediump float;\n” +

地方出现的(0,0,1,1)是(0,0,1)的齐次向量。齐次向量(x,y,z,w)能够代表三维向量(x,y,z)参加矩阵运算,通俗地说,w
分量为 1 时表示地点,w 分量为 0 时表示位移。

“varying vec2 vTextureCoord;\n” +

WebGL 没有提供任何有关上述变换的体制,开发者须求亲自总结顶点的 CCV
坐标。

“uniform sampler2D sTexture;\n” +

至于坐标变换的越多内容,能够参见:

“void main() {\n” +

电脑图形学中的5-7章

“gl_FragColor = texture2D(sTexture, vTextureCoord);\n” +

转换矩阵@维基百科

“}\n”;

透视投影详解

其间脚本语句关键字:

比较复杂的是模型变换中的绕任意轴旋转(常常用四元数生成矩阵)和投影变换(上边的例证都没收涉及到)。

attribute:使用终端数组封装每一种终端的多少,一般用于种种终端都各分化的变量,如顶点地点、颜色等

关于绕任意轴旋转和四元数,可以参照:

uniform:顶点着色器使用的常量数据,不能够被着色器修改,一般用于对同一组顶点组成的单个3D物体中兼有终端都有一样的变量,如当前光源地点

四元数@维基百科

sampler:那是可选的,一种十分的uniform,表示顶点着色器使用的纹路

三个鬼子对四元数公式的表达

mat4:表示4×4浮点数矩阵,该变量存款和储蓄了组合模型视图和投影矩阵

至于齐次向量的更加多内容,能够参见。

vec4:表示包涵了5个浮点数的向量

微型总结机图形学的5.2节

varying:用于从极限着色器传递到片元或FragmentsShader传递到下一步的出口变量

齐次坐标@维基百科

uMVPMatrix * aPosition:通过4×4
的变换地点后,输出给gl_Position,gl_Position是终点着色器内置的输出变量。

着色器和光栅化

gl_FragColor:片元着色器内置的出口变量

在 WebGL
中,开发者是由此着色器来完结上述变换的。着色器是运转在显卡中的程序,以
GLSL 语言编写,开发者要求将着色器的源码以字符串的花样传给 WebGL
上下文的连带函数。

PrimitiveAssembly:图元装配

着色器有二种,顶点着色器和片元(像素)着色器,它们成对现身。顶点着色器职责是吸收接纳顶点的有个别坐标,输出
CCV 坐标。CCV
坐标经过光栅化,转化为逐像素的多寡,传给片元着色器。片元着色器的职务是规定每种片元的颜色。

图元即图形,在OpenGL有几个宗旨图元:点、线、三角形,别的的错综复杂图元都以依照那么些大旨图元来绘制而成。

极端着色器接收的是 attribute 变量,是逐顶点的数码。顶点着色器输出
varying 变量,也是逐顶点的。逐顶点的 varying
变量数据通过光栅化,成为逐片元的 varying
变量数据,输入片元着色器,片元着色器输出的结果就会显得在 Canvas 上。

在图元装配阶段,那3个通过顶点着色器(VertexShader)处理过的终端数组或缓冲区的多少(VertexArrays/BufferObjects),被组装到1个个独立的几何图形中(点,线,三角形)

皇家赌场手机版 44

对装配好的没个图元,都必须确定保障它在世界坐标系中,而对此不在世界坐标系中的图元,就亟须实行裁剪,使其处于在世界坐标系中才能流到下一道工序(光栅化处理)

着色器作用很多,上述只是基本功能。大多数炫酷的成效都以依赖着色器的。如若您对着色器完全没有定义,能够试着明亮下一节
hello world 程序中的着色器再回看一下本节。

那边还有多少个删减操作(Cull),前提是以此效果的开关是打开的:GLES20.glEnable(GLES20.GL_CULL_FACE);
剔除的是图元的背影,阴影,背面等。

至于更多着色器的学识,能够参见:

Rasterization:光栅化

GLSL@维基百科

光栅化阶段绘制对应的图元(点、线、三角形),将图元转化为一组二维数组的进程,然后传递给部分着色器处理。那些二维数组代表显示屏上制图的像素

WebGL@MSDN

皇家赌场手机版 45

程序

(PS:上图中的点天使光栅化应该是点光栅化)

这一节解释绘制上述情景(三角形)的 WebGL
程序。点以此链接,查看源代码,试图通晓一下。这段代码出自WebGL
Programming
Guide,作者作了部分改动以适应本文内容。假若一切平常,你见到的应该是上边那样:

FragmentShader:片元着色器

皇家赌场手机版 46

片元着色器首即使对光栅化处理后变更的片元各个进行拍卖。接收顶点着色器输出的值,要求传入的多少,以及它通过变换矩阵后输出值存款和储蓄地方。

释疑几点(倘若在此以前不打听 WebGL ,多半会对上边包车型客车代码疑惑,无碍):

着色器程序——描述片元所实施的片元着色器程序源代码

字符串 VSHADER_SOURCE 和 FSHADER_SOU中华VCE
是极限着色器和片元着色器的源码。能够将着色器领会为有一定输入和输出格式的程序。开发者须求事先编写好着色器,再依照一定格式着色器发送绘图命令。

输入变量——光栅器对极端着色器插值后的输出值

Part2 将着色器源码编译为 program
对象:先分别编译顶点着色器和片元着色器,然后连接两者。倘若编写翻译源码错误,不会报
JS 错误,但足以通过其他API(如gl.getShaderInfo等)获取编写翻译状态音讯(成功与否,要是出错的错误音信)。

合并变量——片元(或极端)着色器使用的不变的数据

// 顶点着色器

采集样品器——代表片元着色器所用纹理的一种非常的联结变量类型

var vshader = gl.createShader(gl.VERTEX_SHADER);

片元着色器输入和输出关系如下图所示:

gl.shaderSource(vshader, VSHADER_SOURCE);

皇家赌场手机版 47

gl.compileShader(vshader);

因为光栅化处理后,图元只是在显示器上有了像素,却从未进行颜色处理,照旧看不到事物。

// 同样新建 fshader

为此FragmentsShader主要的意义是报告GPU怎样处OLYMPUS照、阴影、遮挡、环境等,然后将结果输出到gl_FragColor变量中

var program = gl.createProgram();

FragmentsShader只输出1个颜色值——gl_FragColor,是片元着色器内置的出口变量

gl.attachShader(program, vshader);

片元着色器脚本示例:

gl.attachShader(program, fshader);

#version 300 es

gl.linkProgram(program);

precision mediump float; // 设置精度限定符

program
对象须求钦定使用它,才方可向着色器传数据并绘制。复杂的程序平时有多个program 对 象,(绘制每一帧时)通过切换 program 对象绘制场景中的差异效能。

in vec4 v_color;

gl.useProgram(program);

out vec4 fragColor;

Part3 向正在利用的着色器传入数据,包罗逐顶点的 attribute 变量和全局的
uniform 变量。向着色器传入数据必须利用 ArrayBuffer,而不是正规的 JS
数组。

void main()

var varray = new Float32Array([-1, -1, 0, 1, -1, 0, 0, 1, 0])

{

WebGL API 对 ArrayBuffer 的操作(填充缓冲区,传入着色器,绘制等)都是通过
gl.APAJERORAY_BUFFESportage 实行的。在 WebGL 系统中又很多类似的情事。

fragColor = v_color;

// 只有将 vbuffer 绑定到 gl.ARRAY_BUFFE福特Explorer,才得以填充数据

gl_FragColor = fragColor;

gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer);

}

// 那里的意趣是,向“绑定到 gl.A兰德酷路泽RAY_BUFFE福特Explorer”的缓冲区中填充数据

光栅化阶段生成的水彩、深度、模板和显示器坐标地点(Xw,
Yw)将会变成逐片元操作阶段的输入值

gl.bufferData(gl.ARRAY_BUFFER, varray, gl.STATIC_DRAW);

Pre-Fragment Operations:逐片元操作阶段

// 获取 a_Position 变量在着色器程序中的地点,参考顶点着色器源码

在片元着色器对片元进行归结的拍卖,并最终为片元生成3个颜色值,并蕴藏在gl_FragColor变量后,接下去正是各个对片元举香港行政局地列的测试。

var aloc = gl.getAttribLocation(program, ‘a_Position’);

光栅化处理时,它由于时把顶点从社会风气坐标系转换成荧屏坐标系,由此在光栅处理后,每一种片元在荧屏上都有个坐标(Xw,
Yw)。且存款和储蓄在了帧缓冲区(FrameBuffer),

// 将 gl.ARRAY_BUFFEEnclave 中的数据传入 aloc 表示的变量,即 a_Position

回顾片元着色器也是对(Xw, Yw)那个坐标的片元举办拍卖

gl.vertexAttribPointer(aloc, 3, gl.FLOAT, false, 0, 0);

皇家赌场手机版 48

gl.enableVertexAttribArray(aloc);

Pixel ownership test——像素归属测试,它控制FrameBuffer中有个别(Xw,
Yw)地点的像素是还是不是属于当前Context

向着色器传入矩阵时,是按列存款和储蓄的。能够相比一下 mmatrix
和矩阵变换一节中的模型矩阵(第 3 个)。

Scissor test——裁剪测试,决定2个地点(Xw,
Yw)的片元是或不是位于裁剪实行内,借使不在,则被扬弃

终点着色器计算出的 gl_Position 正是 CCV
中的坐标,比如最上边的极端(暗绛红)的 gl_Position
化成齐次坐标便是(0,0.5,0.5,1)。

Stencil test/Depth
test——模版和纵深测试,传入片元的沙盘和深度值,决定是不是屏弃片

向终点着色器传入的只是多少个极端的颜料值,而三角形表面包车型大巴颜料渐变是由这多少个颜色值内插出的。光栅化不仅会对
gl_Position 进行,还会对 varying 变量插值。

Blending——混合,将FragmentShader
新产生的片元颜色值和FrameBuffer中某些地方(Xw,
Yw)的片元存款和储蓄的颜色值进行混合

gl.drawArrays()方法使得缓冲区实行绘图,gl.T景逸SUVIANGLES
钦赐绘制三角形,也得以更改参数绘制点、折线等等。

Dithering——抖动,对可用颜色较少的系统,能够就义分辨率为代价,通过颜色值的颠簸来充实可用颜色值。抖动操作和硬件相关,OpenGL允许程序员全体的操作就唯有打开或关闭都懂操作。私下认可情状下震动是激活的

关于 ArrayBuffer 的详细音讯,能够参照:

在逐片元操作阶段的结尾,片元要么被放任,要么将颜色、深度、模板值写入到帧缓冲区(Xw,
Yw)地点,写入的值取决于启用的写入掩码

ArrayBuffer@MDN

写入掩码能够更精致地控制写入的颜色、深度、模板值。

阮一峰的 ArrayBuffer
介绍

备注:Alpha测试和逻辑操作不再是逐片元操作的一有个别,那五个阶段存在于OpenGL2.0盒OpenGL1.x中。

张鑫旭的 ArrayBuffer
介绍

关于 gl.T卡宴IANGLES
等其余绘制格局,可以参照上边那张图或那篇博文。

皇家赌场手机版 49

纵深检查和测试

当五个外表重叠时,前边的模型会遮掩前边的模型。比如这些事例,绘制了多少个交叉的三角形(
varray 和 carray 的尺寸变为 18,gl.drawArrays 最终叁个参数变为
6)。为了简单,这么些事例去掉了矩阵变换进程,间接向着色器传入 CCV 坐标。

皇家赌场手机版 50

皇家赌场手机版 51

终极着色器给出了 6 个极点的 gl_Position ,经过光栅化,片元着色器获得了
2X 个片元(固然 X 为每一个三角形的像素个数),每一种片元都离散的 x,y
坐标值,还有 z 值。x,y 坐标就是三角形在 Canvas
上的坐标,但万一有多少个有着相同 x,y 坐标的片元同时出现,那么 WebGL
就会取 z 坐标值较小的相当片元。

在深度检查和测试在此以前,必须在绘制前拉开一个常量。不然,WebGL 就会遵从在 varray
中定义的各样绘制了,前边的会覆盖前边的。

gl.enable(gl.DEPTH_TEST);

实质上,WebGL 的逻辑是这么的:依次拍卖片元,即使渲染缓冲区(那里正是Canvas
了)的不胜与当下片元对应的像素还平素不绘制时,就把片元的颜料画到渲染缓冲区对应像素里,同时把片元的
z
值缓存在另二个纵深缓冲区的同1个人置;假诺当前缓冲区的照应像素已经绘制过了,就去查看深度缓冲区中对应地点的
z 值,假使当前片元 z 值小,就重绘,不然就放弃当前片元。

WebGL 的那套逻辑,对领悟蒙版(后边会说到)有局部相助。

顶点索引

gl.drawArrays()是比照顶点的各类绘制的,而
gl.drawElements()能够令着色器以2个索引数组为顺序绘制顶点。比如其一事例。

皇家赌场手机版 52

此地画了多少个三角,但只用了 几个极端,有2个终端被八个三角形共用。那时急需树立索引数组,数组的各种成分表示顶点的索引值。将数组填充至gl.ELEMENT_AENCORERAY,然后调用
gl.drawElements()。

var iarray = new Uint8Array([0,1,2,2,3,4]);

var ibuffer = gl.createBuffer(gl.ARRAY_BUFFER, ibuffer);

gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibuffer);

gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, iarray, gl.STATIC_DRAW);

纹理

attribute
变量不仅能够传递顶点的坐标,还是能传递别的任何逐顶点的数码。比如
HelloTriangle 程序把单个顶点的水彩传入了 a_Color,片元着色器收到
v_Color 后直接赋给 gl_FragmentColor,就决定了颜色。

attribute
变量还足以支持绘制纹理。绘制纹理的基本原理是,为各类终端钦定三个纹理坐标(在(0,0)与(1,1,)的纺锤形中),然后传入纹理对象。片元着色器获得的是对应片元的内插后的纹理坐标,就利用那些纹理坐标去纹理对象上取颜色,再画到片元上。内插后的纹理坐标很恐怕不凑巧对应纹理上的有个别像素,而是在几个像素之间(因为一般的图样纹理也是离散),那时可能会透过周围几个像素的加权平均算出该像素的值(具体有若干种差异形式,能够参见)。

比如那么些事例。

皇家赌场手机版 53

纹理对象和缓冲区指标很相近:使用 gl 的 API 函数创造,需求绑定至常量
gl.A汉兰达RAY_BUFFER 和 gl.TEXTURE_2D
,都经过常量对象向当中填入图像和数据。不一样的是,纹理对象在绑定时还要求激活一个纹理单元(此处的gl.TEXTURE0),而
WebGL 系统匡助的纹理单元个数是很简单的(一般为 8 个)。

var texture = gl.createTexture();

gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);

gl.activeTexture(gl.TEXTURE0);

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,
textureImage);

var sloc = gl.getUniformLocation(program, ‘u_Sampler’);

gl.uniform1i(sloc, 0);

片元着色器内注解了 sampler2D 类型的 uniform
变量,通过texture2D函数取样。

precision mediump float;

uniform sampler2D u_Sampler;

varying vec2 v_TexCoord;

void main() {

  gl_FragColor = texture2D(u_Sampler, v_TexCoord);

};

掺杂与蒙版

晶莹剔透效果是用混合机制完毕的。混合机制与深度检查和测试类似,也时有产生在试图向某些已填写的像素填充颜色时。深度检查和测试通过相比z值来规定像素的颜色,而掺杂机制会将二种颜色混合。比如这一个事例。

皇家赌场手机版 54

掺杂的一一是根据绘制的一一实行的,就算绘制的各种有转移,混合的结果平日也不比。假如模型既有非透明表面又有透明表面,绘制透明表面时打开蒙版,其指标是锁定深度缓冲区,因为半晶莹剔透物体前边的物体还是能够见见的,固然不这么做,半晶莹剔透物体后边的物体将会被深度检查和测试机制排除。

打开混合的代码如下。gl.blendFunc方法钦赐了交集的主意,那里的意味是,使用源(待混合)颜色的
α 值乘以源颜色,加上 1-[源颜色的 α]乘以指标颜色。

gl.enable(gl.BLEND);

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

所谓 α 值,正是颜色的第 4 个轻重。

var carray = new Float32Array([

  1,0,0,0.7,1,0,0,0.7,1,0,0,0.7,

  0,0,1,0.4,0,0,1,0.4,0,0,1,0.4

  ]);

浏览器的WebGL系统

WebGL 系统依次组成都部队分在既定规则下相互同盟。稍作梳理如下。

皇家赌场手机版 55

那张图相比较随意,箭头上的文字表示
API,箭头方向差不多表现了数额的流动方向,不必深究。

光照

WebGL 没有为光照提供任何内置的办法,需求开发者在着色器中完毕光照算法。

只可是有颜色的,模型也是有颜色的。在光照下,最终物体展现的水彩是二者联手作用的结果。

兑现光照的办法是:将光照的数码(点光源的职位,平行光的样子,以及光的水彩和强度)作为
uniform 变量传入着色器中,将物体表面每一种顶点处的法线作为 attribute
变量传入着色器,遵从光照规则,修订最后片元呈现的水彩。

光照又分为逐顶点的和逐片元的,两者的差异是,将法线光线交角因素位居顶点着色器初级中学结束学业生升学考试虑或许放在片元着色器初级中学完成学业生升学考试虑。逐片元光照更是绘声绘色,八个极端的例子是:

皇家赌场手机版 56

此刻,点光源在相距贰个外表较近处,表面宗旨 A
处较亮,四周较暗。不过在逐顶点光照下,表面包车型地铁水彩(的影响因子)是由顶点内插出来的,所以表面中心也会比较暗。而逐片元光照间接采用片元的岗位和法线总括与点光源的交角,由此表面宗旨会相比较亮。

复杂模型

复杂模型只怕有包涵子模型,子模型可能与父模型有相对运动。比如开着雨刮器的小车,雨刮器的世界坐标是受父模型汽车,和作者的状态共同决定的。若要总结雨刮器某顶点的地方,供给用雨刮器相对小车的模型矩阵乘北京小车工业公司总公司车的模子矩阵,再乘以顶点的局部坐标。

复杂模型只怕有这几个表面,恐怕每个表面使用的着色器就区别。平常将模型拆解为组,使用同一着色器的外部为一组,先绘制同一组中的内容,然后切换着色器。每便切换着色器都要再一次将缓冲区中的数据分配给着色器中相应变量。

动画

动画的原理便是高速地擦除和重绘。常用的艺术是知名的
requestAnimationFrame
。不熟悉的同桌,可以参照正美的牵线。

WebGL库

现阶段最风靡的 WebGL 库是
ThreeJS,很强大,官网,代码。

调节工具

正如早熟的 WebGL 调节和测试工具是WebGL
Inspector。

互联网财富和书本

英文的有关 WebGL 的财富有好多,蕴含:

learning
webgl

WebGL@MDN

WebGL Cheat
Sheet

国内最早的 WebGL 教程是由郝稼力翻译的,放在 hiwebgl 上,近年来 hiwebgl
已经倒闭,但教程还是可以在这里找到。郝稼力近日营业着Lao3D。

国内已经出版的 WebGL 书籍有:

WebGL入门指南:其实是一本讲
ThreeJS 的书

WebGL高级编制程序:还不易的一本

WebGL编制程序指南:卓殊可相信的周全教程

Leave a Comment.