基础入门
书籍:【webgl 编程指南 - 2014 Kouichi Matsuda 和 Rodger Lea 著 谢光磊 译】
# 方法论
入门书籍推荐
当涉及到学习基本的 WebGL 知识时,以下是一些值得推荐的书籍,它们适合初学者入门,帮助您了解 WebGL 的基本概念和原理:
"WebGL 编程指南" - Kouichi Matsuda 和 Rodger Lea 著: 这本书是学习 WebGL 的良好起点,它详细介绍了 WebGL 的基础知识和实际示例。适合那些对 Web 开发有一些了解的初学者和开发者。
"WebGL 编程精要" - Neil Matsuba 著: 这是另一本适合初学者的入门指南,它涵盖了 WebGL 的基本原理和实用技巧,帮助您快速上手。
"Three.js 入门指南" - Jos Dirksen 著: 如果您希望在学习 WebGL 的同时使用 Three.js 库来简化开发,这本书是不错的选择。它重点介绍了使用 Three.js 创建交互式 3D 内容的过程。
"OpenGL ES 2.0 基础教程" - John Kessenich 和 Graham Sellers 著: 这本书不仅涵盖了 OpenGL ES 2.0 的基本概念,也适用于学习 WebGL,因为 WebGL 是基于 OpenGL ES 2.0 的。
虽然这些书籍中的一些可能更偏向于 OpenGL ES,但它们提供的知识对于理解和学习 WebGL 非常有用,因为 WebGL 是基于 OpenGL ES 的 API。您可以根据您的喜好和学习需求来选择适合您的书籍。同时,还可以结合在线资源、教程和实践项目,以加深对 WebGL 的理解和熟练掌握。
其他重要书籍
红宝书-OpenGL Programming Guide
金宝书-OpenGL ES2.0 Programming Guide
技术与知识
H5、JS、三维图形学理论、数学基础和 OpenGL。
运行环境简配
浏览器 Chrome25 以上;
操作系统 7、8、10、11;
显卡 NVIDIA Quadro FX 380 、NVDIA GT X 580 、NVDIA GeForce GTS 450 、Mobile Intel 4 Series Express Chipset Family、 AMD Radeon HD 6970。
# 内容概述
写这本书(2014 年出版)的两位作者是三维图形学与 WebGL 领域的先驱,博士学历。先后供职日本电气、索尼研发中心、索尼计算机科学实验室。
网页技术演变历史,从 HTML 静态网页
到 JS 动态交互网页
再到 H5 Canvas 绘制二维图形
再到 WebGL 绘制三维图形
。
JS 的野心越来越大了,在过去的十年中,H5 逐渐把报纸一般的网页变成了能与客户端程序相媲美的 Web。 JS 终于把触手伸向了最为复杂的领域之一三维图形渲染,WebGL 因此而诞生。
传统意义上来说,只有高配置的计算机或专用的游戏机才能渲染三维图形,为了显示三维图形,开发者需要使用 C 或 C++语言,辅以专门的计算机图形库,如 OpenGL 或 Direct3D,来开发一个独立的应用程序。使用 WebGL,你可以在浏览器中不依赖任何插件创建出精美可交互的三维图形。WebGL 技术使创建新一代 3D 网页游戏、用户界面、数据可视化方案成为可能,这些程序能够运行在任何支持标准浏览器的 PC、智能手机、平板电脑、家用游戏机或其他消费电子设备上。
WebGL 是一项在网页上渲染三维图形的技术,也是 H5 草案的一部分。WebGL 的 API 非常低级,需要了解图形学中的诸多概念才能熟练使用这些 API。WebGL 是一种 3D 绘图标准,这种绘图技术标准允许把 JS 和 OpenGL ES 2.0 结合在一起,通过增加 OpenGL ES 2.0 的一个 JS 绑定,WebGL 可以为 H5 Canvas 提供硬件 3D 加速渲染,这样 Web 开发人员就可以借助系统显卡来在浏览器里更流畅地展示 3D 场景和模型了,还能创建复杂的导航和数据视觉化。
# WebGL 起源
WebGL 是基于 OpenGL ES 进行开发的,WebGL1.0 版本基于 OpenGL ES2.0,而 WebGL2.0 基于 OpenGL ES3.0。
OpenGL ES 是 OpenGL 的一个子库,主要是针对嵌入式计算机,智能手机和游戏设备等的子库。
OpenGL 是计算机三维图形渲染的两大技术之一,另一个技术是大家都很熟悉了 Direct3D(微软 DirectiveX 的一部分),Direct3D 主要是针对 Window 系统的渲染,而 OpenGL 则被设计在跨平台的操作上了。
OpenGL 发展历史
总的来说借鉴一下书上的 OpenGL 世代图,大致也就是这样,最初是 SGI(Silicon Graphics Inc)开发,后被 Khronos 组织接手。
OpenGL 1.0(1992.1)——> OpenGL ES 1.1
- OpenGL 1.1(1997.1)
- OpenGL 1.2(1998.3.16)
- OpenGL 1.3(2001.8.14)
- OpenGL 1.4(2002.7.24)
- OpenGL 1.5(2003.7.29)
OpenGL 2.0(2004.9.7) ——> OpenGL ES 2.0 ——> WebGL 1.0 支持可编程着色器方法
- OpenGL 2.1(2006.7.2)
OpenGL 3.0(2008.8.11)
- OpenGL 3.1(2009.3.24)
- OpenGL 3.2(2009.8.3)
- OpenGL 3.3(2010.3.11) ——> OpenGL ES 3.0 ——> WebGL 2.0
OpenGL 4.0(2010.3.11)
- OpenGL 4.1(2010.7.26)
- OpenGL 4.2(2011.8.8)
- OpenGL 4.3(2012.8.6)
- OpenGL 4.4(2013.7.23)
# WebGL 程序结构

# WebGL 基础
WebGL 基础技术主要包括矩阵变换、动画、颜色纹理。
Canvas 画布是 JS 绘图的地方。H5 之前需要使用 img 或者 flash 来显示图像,H5 后使用 Canvas 就可以实现图形的动态绘制。Canvas 只支持一些简单的 2d 绘制,不支持 3d,更重要的是性能有限,WebGL 弥补了这两方面的不足。Canvas 是透明的,只有绘制了图形才能看到其所在区域。其原点落在浏览器窗口左上方,x 轴正方向朝右,y 轴正方向朝下。
# 初识着色器
- 顶点着色器:Vertex Shader,顶点着色器是用来描述顶点特性(如位置、颜色等)的程序。顶点是指二维或三维空间中的一个点,比如二维或三维图形的端点或交点。
- 片元着色器:Fragment Shader,进行逐片元处理过程如光照的程序。片元是webgl的一个术语,可以简单的理解为像素。
webgl系统使用着色器在浏览器上绘图的流程:浏览器执行加载的js程序,执行webgl相关方法,逐顶点操作,逐片元操作,渲染到颜色缓冲区,颜色缓冲区,显示在浏览器上。
webgl程序遵循的流程如下:获取canvas元素、获取webgl绘图上下文、初始化着色器、设置canvas背景色、清除canvas颜色、绘图。
# 例子一:在画布上绘制二维图形
<!DOCTYPE html>
<html lang="en">
<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" />
<title>绘制2d图形</title>
</head>
<body onload="main()">
<canvas id="2d">浏览器版本低不支持</canvas>
<script>
function main() {
//获取画布 canvas 元素
let canvas = document.getElementById("2d");
if (!canvas) {
console.warn("获取画布失败");
return;
}
//获取绘制二维图形的上下文
let ctx = canvas.getContext("2d");
//设置填充色为蓝色,webgl颜色缓存的数值是0-1之间
ctx.fillStyle = "rgba(0,0,255,1.0)";
//使用填充颜色填充矩形
ctx.fillRect(120, 10, 150, 150);
}
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 例子二:清空 webgl 画布内容
<!DOCTYPE html>
<html lang="en">
<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" />
<title>01我的第一个webgl程序</title>
<script src="../lib/cuon-matrix.js"></script>
<script src="../lib/cuon-utils.js"></script>
<script src="../lib/webgl-debug.js"></script>
<script src="../lib/webgl-utils.js"></script>
</head>
<body onload="main()">
<canvas id="webgl" width="200" height="200">这是第一个webgl程序!</canvas>
<script>
function main() {
//获取画布 canvas 元素
let canvas = document.querySelector("#webgl");
//获取 webgl 绘图上下文,canvas.getContex() 函数在不同浏览器上需要的参数不同,所以封装一个更加通用的函数 getWebGLContext 来隐藏不同浏览器之间的差异。
let gl = getWebGLContext(canvas, true);
if (!gl) {
console.log("failed to get the rendering context for webgl!");
return;
}
//指定清空canvas的颜色,也就是设置背景色。
//不指定颜色缓冲区时,默认的值是(0.0,0.0,0.0,0.0);
//不指定深度缓冲区时,默认的值是1;
//不指定模板缓冲区时,默认的值是0;
//这个函数对应了 opengl 中的 glClearColor()函数。
//这个指定的背景色会长久的驻存在 webgl 中,直到再次调用 gl.clearColor 函数。
gl.clearColor(1.0, 0.5, 0.0, 1.0);
//用背景色刷新颜色缓冲区,清空绘图区域实际是清空颜色缓冲区。
gl.clear(gl.COLOR_BUFFER_BIT);
//类似的缓冲区还有
//gl.DEPTH_BUFFER_BIT(深度缓冲区)
//gl.STENCIL_BUFFER_BIT(模板缓冲区)
}
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

# 例子三:绘制一个点(硬编码版本)
<!DOCTYPE html>
<html lang="en">
<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" />
<title>02绘制一个点</title>
<script src="../lib/webgl-utils.js"></script>
<script src="../lib/webgl-debug.js"></script>
<script src="../lib/cuon-utils.js"></script>
<script src="../lib/cuon-matrix.js"></script>
</head>
<body onload="main();">
<canvas width="200" height="200" id="point">
请使用支持的浏览器来实现这个案例演示
</canvas>
<script>
//gl内置变量:gl_Position 设置坐标,gl_PointSize 设置尺寸,gl_FragColor 设置片元颜色,是片元着色器中唯一内置的变量。
//齐次坐标(x,y,z,w) 等价于三维坐标(x/w,y/w,z/w)。
//w为0表示了点趋近于无穷远,w为1就可以当作一个点,w不能等于0。
//使用4个分量表示坐标(齐次坐标)可以提高处理三维数据的效率。
//必须严格使用浮点数,不能使用整数或者其他类型。
let VSHADER_SOURCE = `void main(){
gl_Position=vec4(0.0,0.0,0.0,1.0);
gl_PointSize=10.0;
}`;
let FSHADER_SOURCE = `void main(){
gl_FragColor=vec4(1.0,0.5,0.0,1.0);
}`;
function main() {
let canvas = document.querySelector("#point");
let gl = getWebGLContext(canvas, true);
if (!gl) {
console.info("Failed to initialize gl");
return;
}
//初始化着色器
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.info("Failed to initialize shaders.");
return;
}
//设置canvas背景色
gl.clearColor(0.0, 0.0, 0.0, 1.0);
//清空颜色缓冲区颜色
gl.clear(gl.COLOR_BUFFER_BIT);
//绘制图形接口,这个接口功能强大
gl.drawArrays(gl.POINTS, 0, 1);
}
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

# 例子四:绘制一个点(动态传值版本)
<!DOCTYPE html>
<html lang="en">
<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" />
<title>03动态绘制一个点</title>
<script src="./lib/cuon-matrix.js"></script>
<script src="./lib/cuon-utils.js"></script>
<script src="./lib/webgl-debug.js"></script>
<script src="./lib/webgl-utils.js"></script>
</head>
<body onload="main()">
<canvas width="200" height="200" id="dynamicPoint"
>请使用支持的浏览器来执行展示功能!</canvas
>
<script>
//attribute 修饰的变量只能用于顶点着色器中
let VSHADER_SOURCE =
`attribute vec4 a_Position;
attribute float a_PointSize;
void main(){
gl_Position=a_Position;
gl_PointSize=a_PointSize;
}`;
let FSHADER_SOURCE =
`void main(){
gl_FragColor=vec4(0.0,0.0,0.0,1.0);
}`;
function main() {
let canvas = document.querySelector("#dynamicPoint");
let gl = getWebGLContext(canvas, true);
if (!gl) {
console.info("failed initalize gl!");
return;
}
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.info("failed initalize gl.....");
return;
}
//获取attribute变量的存储位置
let a_Position = gl.getAttribLocation(gl.program, "a_Position");
if (a_Position < 0) {
console.info("failed to get the storage location of a_Position");
return;
}
//获取attribute变量的存储位置
let a_PointSize = gl.getAttribLocation(gl.program, "a_PointSize");
if (a_PointSize < 0) {
console.info("failed to get the storage location of a_PointSize");
return;
}
//将顶点位置传输给attribute变量
gl.vertexAttrib3f(a_Position, 0.0, 0.85, 0.0);
//将顶点大小尺寸传输给attribute变量
gl.vertexAttrib1f(a_PointSize, 15);
gl.clearColor(1.0, 0.5, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.POINTS, 0, 1);
}
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

# 例子五:鼠标点击画布绘制点
<!DOCTYPE html>
<html lang="en">
<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" />
<title>在画布上绘制鼠标点</title>
<script src="./lib/webgl-utils.js"></script>
<script src="./lib/cuon-matrix.js"></script>
<script src="./lib/cuon-utils.js"></script>
<script src="./lib/webgl-debug.js"></script>
</head>
<body onload="main()">
<canvas width="200" height="200" id="point"></canvas>
<script>
let VSHADER_SOURCE =
`attribute vec4 a_Position;
void main(){
gl_Position=a_Position;
gl_PointSize=10.0;
}`;
let FSHADER_SOURCE =
`precision mediump float;
uniform vec4 u_FragColor;
void main(){
gl_FragColor=u_FragColor;
}`;
function main() {
let canvas = document.querySelector('#point');
let gl = getWebGLContext(canvas);
if (!gl) {
console.info('failed to initalized gl!');
return;
}
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.info('failed to initalized initShaders!');
return;
}
let a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if (a_Position < 0) {
console.info('failed to initalized a_Position!');
return;
}
let u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor');
if (!u_FragColor) {
console.info('failed to initalized u_fragColor');
return;
}
gl.clearColor(0.0, 0.0, 0.0, 1.0);
let g_points = [];
let g_Colors = [];
canvas.addEventListener('mousedown', function (e) {
let x = e.clientX;
let y = e.clientY;
let rect = e.target.getBoundingClientRect();
console.info(rect);
console.info(x, y, canvas);
x = ((x - rect.left) - canvas.width / 2) / (canvas.width / 2);
y = (canvas.height / 2 - (y - rect.top)) / (canvas.height / 2);
g_points.push([x, y]);
if (x >= 0 && y >= 0) {
g_Colors.push([1.0, 0.0, 0.0, 1.0]); //红色
} else if (x < 0 && y < 0) {
g_Colors.push([0.0, 1.0, 0.0, 1.0]); //绿色
} else if (x < 0 && y > 0) {
g_Colors.push([1.0, 1.0, 1.0, 1.0]); //白色
} else {
g_Colors.push([0.0, 0.0, 1.0, 1.0]); //蓝色
}
gl.clear(gl.COLOR_BUFFER_BIT);
let len = g_points.length;
for (let i = 0; i < len; i++) {
gl.vertexAttrib3f(a_Position, g_points[i][0], g_points[i][1], 0.0);
gl.uniform4f(u_FragColor, g_Colors[i][0], g_Colors[i][1], g_Colors[i][2], g_Colors[i][3]);
gl.drawArrays(gl.POINTS, 0, 1);
}
console.info(g_points);
}, false);
gl.clear(gl.COLOR_BUFFER_BIT);
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

# WebGL 基础
WebGL 依赖一种新的称为着色器的绘图机制。着色器提供了灵活且强大的绘制二维或三维图形的方法,所有 WebGL 程序必须使用它。着色器是 WebGL 的一项重要核心机制。
WebGL(全写 Web Graphics Library)是一种 3D 绘图标准,这种绘图技术标准允许把 JavaScript 和 OpenGL ES 2.0 结合在一起,通过增加 OpenGL ES 2.0 的一个 JavaScript 绑定,WebGL 可以为 HTML5 Canvas 提供硬件 3D 加速渲染。
一、着色器
- 顶点着色器(vertex shader),用来描述顶点特性(如位置、颜色等)的程序。顶点(vertex)是指二维或三维空间中的一个点,比如二维或三维图形的端点或交点。
- 片元着色器(fragment shader),进行逐片元处理过程的程序。

提示
这里的顶点着色器和片元着色器代码都写在了 js 定义的两个字符串变量中,当然,也可以加载文件后并读取文件来载入着色器程序。
二、WebGL 程序执行流程

三、WebGL初始化着色器

四、WebGL 坐标系统
WebGL 处理的是三维图形,所以它使用三维坐标系统(笛卡尔坐标系),具有 X、Y、Z 轴。X 轴水平向右为正方向,Y 轴垂直向下为正方向,Z 轴垂直于屏幕向外为正方向。这套坐标系统又称为右手坐标系(right-handed coordinate system)


齐次坐标系:
