基础入门

书籍:【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>
1
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>
1
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>
1
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>
1
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>
1
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程序到在浏览器上显示结果的过程

提示

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

二、WebGL 程序执行流程

webgl程序的执行流程

三、WebGL初始化着色器

webgl初始化着色器

四、WebGL 坐标系统

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

webgl右手坐标系
canvas转webgl坐标系

齐次坐标系:

上次更新: 2025/02/15, 13:42:25
最近更新
01
Git问题集合
01-29
02
安装 Nginx 服务器
01-25
03
安装 Docker 容器
01-25
更多文章>
×
×