1. webgl是什么

getElementById首先看一个例子:

  1. const div = document.getElementById('happy');
  2. div.style.color = "#FFFFFF";

getElementById是浏览器提供给开发者操作DOM的api接口,通过这个方法,我们可以更改div的样式,属性甚至改变它的类型。类似其他的还有getElementsByClassName, getElementByTagName等等。这些方法统称为DOM API。
类似地,我们看另外一个方法drawArrays:

  1. const canvas = document.getElementById('canvas');
  2. const gl = canvas.getContext("webgl");
  3. ...
  4. //开始绘制图形
  5. gl.drawArrays(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);

这个方法的作用是绘制一个三角形。类似的方法还有很多很多:

  • gl.clearColor
  • gl.getAttribLocation
  • gl.getUniformLocation

这些方法的集合,我们就称之为webgl。浏览器提供这些方法让我们可以通过javascript传输数据到GPU,从而绘制——2D或者3D——图形. 所以webgl其实本质上就是用来与Gpu通信的API。
图:webgl -> 传输信息 -> Gpu绘图案 -> 显示在Canvas上。
image.png
简单来说webgl就做一件事情,将数据信息处理然后传递给GPU进行图形绘制学习webgl就是学习如何使用这些方法,

2.基础实践

2D:
代码演示1:绘制基本的图形
绘制一个点

  1. function initShaders(gl, vshader, fshader) {
  2. var program = createProgram(gl, vshader, fshader);
  3. if (!program) {
  4. console.log('Failed to create program');
  5. return false;
  6. }
  7. gl.useProgram(program);
  8. gl.program = program;
  9. return true;
  10. }
  11. /**
  12. * Create the linked program object
  13. * @param gl GL context
  14. * @param vshader a vertex shader program (string)
  15. * @param fshader a fragment shader program (string)
  16. * @return created program object, or null if the creation has failed
  17. */
  18. function createProgram(gl, vshader, fshader) {
  19. // Create shader object
  20. var vertexShader = loadShader(gl, gl.VERTEX_SHADER, vshader);
  21. var fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fshader);
  22. if (!vertexShader || !fragmentShader) {
  23. return null;
  24. }
  25. // Create a program object
  26. var program = gl.createProgram();
  27. if (!program) {
  28. return null;
  29. }
  30. // Attach the shader objects
  31. gl.attachShader(program, vertexShader);
  32. gl.attachShader(program, fragmentShader);
  33. // Link the program object
  34. gl.linkProgram(program);
  35. // Check the result of linking
  36. var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
  37. if (!linked) {
  38. var error = gl.getProgramInfoLog(program);
  39. console.log('Failed to link program: ' + error);
  40. gl.deleteProgram(program);
  41. gl.deleteShader(fragmentShader);
  42. gl.deleteShader(vertexShader);
  43. return null;
  44. }
  45. return program;
  46. }
  47. /**
  48. * Create a shader object
  49. * @param gl GL context
  50. * @param type the type of the shader object to be created
  51. * @param source shader program (string)
  52. * @return created shader object, or null if the creation has failed.
  53. */
  54. function loadShader(gl, type, source) {
  55. // Create shader object
  56. var shader = gl.createShader(type);
  57. // Set the shader program
  58. gl.shaderSource(shader, source);
  59. // Compile the shader
  60. gl.compileShader(shader);
  61. // Check the result of compilation
  62. var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
  63. if (!compiled) {
  64. var error = gl.getShaderInfoLog(shader);
  65. console.log('Failed to compile shader: ' + error);
  66. gl.deleteShader(shader);
  67. return null;
  68. }
  69. return shader;
  70. }
  71. /**
  72. * Initialize and get the rendering for WebGL
  73. * @param canvas <cavnas> element
  74. * @param opt_debug flag to initialize the context for debugging
  75. * @return the rendering context for WebGL
  76. */
  77. function getWebGLContext(canvas, opt_debug) {
  78. // Get the rendering context for WebGL
  79. var gl = WebGLUtils.setupWebGL(canvas);
  80. if (!gl) return null;
  81. // if opt_debug is explicitly false, create the context for debugging
  82. if (arguments.length < 2 || opt_debug) {
  83. gl = WebGLDebugUtils.makeDebugContext(gl);
  84. }
  85. return gl;
  86. }
  87. // HelloPoint1.js (c) 2012 matsuda
  88. function main() {
  89. // 获取canvas元素,后续的绘制都需要在它上面进行
  90. var canvas = document.getElementById('canvas');
  91. // 获取canvas中的上下文
  92. var gl = canvas.getContext("webgl")
  93. if (!gl) {
  94. console.log('Failed to get the rendering context for WebGL');
  95. return;
  96. }
  97. // 创建初始化程序
  98. var program = gl.createProgram();
  99. // 创建顶点着色器
  100. var vShader = gl.createShader(gl.VERTEX_SHADER);
  101. // 创建片元着色器
  102. var fShader = gl.createShader(gl.FRAGMENT_SHADER);
  103. // 顶点着色器
  104. var VSHADER_SOURCE =
  105. 'void main() {\n' +
  106. ' gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n' + // Set the vertex coordinates of the point
  107. ' gl_PointSize = 10.0;\n' + // Set the point size
  108. '}\n';
  109. // 片元着色器
  110. var FSHADER_SOURCE =
  111. 'void main() {\n' +
  112. ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + // Set the point color
  113. '}\n';
  114. // shader容器与着色器绑定
  115. gl.shaderSource(vShader, VSHADER_SOURCE);
  116. gl.shaderSource(fShader, FSHADER_SOURCE);
  117. // 将GLSE语言编译成浏览器可用代码
  118. gl.compileShader(vShader);
  119. gl.compileShader(fShader);
  120. // 将着色器添加到程序上
  121. gl.attachShader(program, vShader);
  122. gl.attachShader(program, fShader);
  123. // 链接程序,在链接操作执行以后,可以任意修改shader的源代码,
  124. //对shader重新编译不会影响整个程序,除非重新链接程序
  125. gl.linkProgram(program);
  126. // 加载并使用链接好的程序
  127. gl.useProgram(program);
  128. gl.clearColor(0.0,0.0,0.0,1.0);
  129. gl.clear(gl.COLOR_BUFFER_BIT);
  130. gl.drawArrays(gl.POINTS, 0 ,1);
  131. // 初始化着色器语言
  132. if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
  133. console.log('Failed to intialize shaders.');
  134. return;
  135. }
  136. // 指定canvas的刷新颜
  137. gl.clearColor(0.0, 0.0, 0.0, 1.0);
  138. // 用之前设置的颜色刷新canvas
  139. gl.clear(gl.COLOR_BUFFER_BIT);
  140. // 告诉GPU在Canvas上绘制一个点
  141. gl.drawArrays(gl.POINTS, 0, 1);
  142. }

gl.drawArrays(gl.POINTS, 0, 1); 意思是告诉GPU绘制一个点,第一个参数标识的是绘制类型。这个参数的值基本上只有三种:POINTS 点,LINE线和TRIANGLES三角形。其他的图像都是由这个三种图形组成的。这个是计算机图形的基本概念。
例如我画以下的图形:
image.pngspengl-sphere.jpeg
不管是基础的图形还是复杂的图形,都能用三角形拼凑而成。重点在于三角形的数量的多少。

3D:
代码演示2:绘制复合的图形
绘制一个立方体

  1. // ColoredCube.js (c) 2012 matsuda
  2. // Vertex shader program
  3. var VSHADER_SOURCE =
  4. 'attribute vec4 a_Position;\n' +
  5. 'attribute vec4 a_Color;\n' +
  6. 'uniform mat4 u_MvpMatrix;\n' +
  7. 'varying vec4 v_Color;\n' +
  8. 'void main() {\n' +
  9. ' gl_Position = u_MvpMatrix * a_Position;\n' +
  10. ' v_Color = a_Color;\n' +
  11. '}\n';
  12. // Fragment shader program
  13. var FSHADER_SOURCE =
  14. '#ifdef GL_ES\n' +
  15. 'precision mediump float;\n' +
  16. '#endif\n' +
  17. 'varying vec4 v_Color;\n' +
  18. 'void main() {\n' +
  19. ' gl_FragColor = v_Color;\n' +
  20. '}\n';
  21. function main() {
  22. // Retrieve <canvas> element
  23. var canvas = document.getElementById('webgl');
  24. // Get the rendering context for WebGL
  25. var gl = getWebGLContext(canvas);
  26. if (!gl) {
  27. console.log('Failed to get the rendering context for WebGL');
  28. return;
  29. }
  30. // Initialize shaders
  31. if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
  32. console.log('Failed to intialize shaders.');
  33. return;
  34. }
  35. // Set the vertex information
  36. var n = initVertexBuffers(gl);
  37. if (n < 0) {
  38. console.log('Failed to set the vertex information');
  39. return;
  40. }
  41. // Set the clear color and enable the depth test
  42. gl.clearColor(0.0, 0.0, 0.0, 1.0);
  43. gl.enable(gl.DEPTH_TEST);
  44. // Get the storage location of u_MvpMatrix
  45. var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
  46. if (!u_MvpMatrix) {
  47. console.log('Failed to get the storage location of u_MvpMatrix');
  48. return;
  49. }
  50. // Set the eye point and the viewing volume
  51. var mvpMatrix = new Matrix4();
  52. mvpMatrix.setPerspective(30, 1, 1, 100);
  53. mvpMatrix.lookAt(3, 3, 7, 0, 0, 0, 0, 1, 0);
  54. // Pass the model view projection matrix to u_MvpMatrix
  55. gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);
  56. // Clear color and depth buffer
  57. gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  58. // Draw the cube
  59. gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);
  60. }
  61. function initVertexBuffers(gl) {
  62. // Create a cube
  63. // v6----- v5
  64. // /| /|
  65. // v1------v0|
  66. // | | | |
  67. // | |v7---|-|v4
  68. // |/ |/
  69. // v2------v3
  70. var vertices = new Float32Array([ // Vertex coordinates
  71. 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0,-1.0, 1.0, 1.0,-1.0, 1.0, // v0-v1-v2-v3 front
  72. 1.0, 1.0, 1.0, 1.0,-1.0, 1.0, 1.0,-1.0,-1.0, 1.0, 1.0,-1.0, // v0-v3-v4-v5 right
  73. 1.0, 1.0, 1.0, 1.0, 1.0,-1.0, -1.0, 1.0,-1.0, -1.0, 1.0, 1.0, // v0-v5-v6-v1 up
  74. -1.0, 1.0, 1.0, -1.0, 1.0,-1.0, -1.0,-1.0,-1.0, -1.0,-1.0, 1.0, // v1-v6-v7-v2 left
  75. -1.0,-1.0,-1.0, 1.0,-1.0,-1.0, 1.0,-1.0, 1.0, -1.0,-1.0, 1.0, // v7-v4-v3-v2 down
  76. 1.0,-1.0,-1.0, -1.0,-1.0,-1.0, -1.0, 1.0,-1.0, 1.0, 1.0,-1.0 // v4-v7-v6-v5 back
  77. ]);
  78. var colors = new Float32Array([ // Colors
  79. 0.4, 0.4, 1.0, 0.4, 0.4, 1.0, 0.4, 0.4, 1.0, 0.4, 0.4, 1.0, // v0-v1-v2-v3 front(blue)
  80. 0.4, 1.0, 0.4, 0.4, 1.0, 0.4, 0.4, 1.0, 0.4, 0.4, 1.0, 0.4, // v0-v3-v4-v5 right(green)
  81. 1.0, 0.4, 0.4, 1.0, 0.4, 0.4, 1.0, 0.4, 0.4, 1.0, 0.4, 0.4, // v0-v5-v6-v1 up(red)
  82. 1.0, 1.0, 0.4, 1.0, 1.0, 0.4, 1.0, 1.0, 0.4, 1.0, 1.0, 0.4, // v1-v6-v7-v2 left
  83. 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, // v7-v4-v3-v2 down
  84. 0.4, 1.0, 1.0, 0.4, 1.0, 1.0, 0.4, 1.0, 1.0, 0.4, 1.0, 1.0 // v4-v7-v6-v5 back
  85. ]);
  86. var indices = new Uint8Array([ // Indices of the vertices
  87. 0, 1, 2, 0, 2, 3, // front
  88. 4, 5, 6, 4, 6, 7, // right
  89. 8, 9,10, 8,10,11, // up
  90. 12,13,14, 12,14,15, // left
  91. 16,17,18, 16,18,19, // down
  92. 20,21,22, 20,22,23 // back
  93. ]);
  94. // Create a buffer object
  95. var indexBuffer = gl.createBuffer();
  96. if (!indexBuffer)
  97. return -1;
  98. // Write the vertex coordinates and color to the buffer object
  99. if (!initArrayBuffer(gl, vertices, 3, gl.FLOAT, 'a_Position'))
  100. return -1;
  101. if (!initArrayBuffer(gl, colors, 3, gl.FLOAT, 'a_Color'))
  102. return -1;
  103. // Write the indices to the buffer object
  104. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  105. gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
  106. return indices.length;
  107. }
  108. function initArrayBuffer(gl, data, num, type, attribute) {
  109. var buffer = gl.createBuffer(); // Create a buffer object
  110. if (!buffer) {
  111. console.log('Failed to create the buffer object');
  112. return false;
  113. }
  114. // Write date into the buffer object
  115. gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  116. gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
  117. // Assign the buffer object to the attribute variable
  118. var a_attribute = gl.getAttribLocation(gl.program, attribute);
  119. if (a_attribute < 0) {
  120. console.log('Failed to get the storage location of ' + attribute);
  121. return false;
  122. }
  123. gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);
  124. // Enable the assignment of the buffer object to the attribute variable
  125. gl.enableVertexAttribArray(a_attribute);
  126. return true;
  127. }

shader着色器

webgl除了告诉GPU需要画什么,还要告诉它怎么去画。所以webgl包含了将一种shaer着色器语言传递给gpu的过程。
顶点着色器:绘制所有的点

  1. var VSHADER_SOURCE =
  2. 'attribute vec4 a_Position;\n' +
  3. 'attribute vec4 a_Color;\n' +
  4. 'uniform mat4 u_MvpMatrix;\n' +
  5. 'varying vec4 v_Color;\n' +
  6. 'void main() {\n' +
  7. ' gl_Position = u_MvpMatrix * a_Position;\n' +
  8. ' v_Color = a_Color;\n' +
  9. '}\n';
  10. // Fragment shader program
  11. var FSHADER_SOURCE =
  12. '#ifdef GL_ES\n' +
  13. 'precision mediump float;\n' +
  14. '#endif\n' +
  15. 'varying vec4 v_Color;\n' +
  16. 'void main() {\n' +
  17. ' gl_FragColor = v_Color;\n' +
  18. '}\n';

片元着色器:补充每个店之间的颜色

image.png

2. 动画

image.png
西洋镜:
rotation.gif
所有的计算机和电影技术的基本原理和一百多年前的西洋镜的原理是一样的,因为人的眼睛的延迟效应,所以,以一定的速度切换连续的画面,会造成动画的效果,切换的速度越高,动画效果就越是流畅。这个速度在电脑上表现为屏幕的刷新率,它有一个单位来计算它:帧率。
帧(frame):帧是一个动画概念,指的是在单位时间内屏幕的刷新速率。例如60帧表示的就是一秒内显示器有60次刷新。
webgl绘制动画的工作方式:
webgl绘制动画的工作方式和GPU的这种方式类似。就是说没一段很短的世界就会将最新的数据传递给GPU,显示。例如:一个点要从A运动到B,那么这个过程就是webgl多次地将点的运动信息传递GPU以更新点的位置信息,最终形成了我们看到的动画。所以我们在webgl应用程序中经常会使用requestAnimationFrame,跟踪电脑的刷新速度更新动画,来传递信息。
3D,帧,requestAnimationFrame,矩阵,变化,电脑如何绘制3D图形,人的眼睛,webgl的显示原理
webgl运动的分解图
如果动画的帧率是60的话,那么已在位置每隔16.6毫秒左右,webgl会计算一次点的信息,然后传递给GPU,实现动画效果。这样就实现了动画的效果。原理和js动画一样。

3. webgl的代码演示

立体正方形移动并且旋转

4. webgl的应用

游戏,

丰富的交互

5. 相关的框架

由于webgl的语法非常底层,而且调试webgl极其繁琐,直接用webgl做项目是非常需要耗费时间和精力的。因此,很多框架应运而生其中几个主要的有:
three.js
three绘制一个上面的运动整体比较:
code
babylon.js
babylon绘制一个运动的正方体比较:
code

4. 更多的深入的知识

webgl性能优化
变化矩阵
电脑自动计算颜色插值
笛卡尔坐标系
颜色和纹理

最后 提问环节