经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 软件/图像 » WebGL » 查看文章
WebGL简易教程(六):第一个三维示例(使用模型视图投影变换)
来源:cnblogs  作者:charlee44  时间:2019/10/8 9:15:28  对本文有异议

1. 概述

在上一篇教程《WebGL简易教程(五):图形变换(模型、视图、投影变换)》中,详细讲解了OpenGL\WebGL关于绘制场景的模型变换、视图变换以及投影变换的过程。不过那篇教程是纯理论知识,这里就具体结合一个实际的例子,进一步理解WebGL中是如何通过图形变换让一个真正的三维场景显示出来。

2. 示例:绘制多个三角形

继续改进之前的代码,这次就更进一步,在一个场景中绘制了三个三角形。

2.1. Triangle_MVPMatrix.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8" />
  5. <title>Hello Triangle</title>
  6. </head>
  7. <body onload="main()">
  8. <canvas id="webgl" width="400" height="400">
  9. Please use a browser that supports "canvas"
  10. </canvas>
  11. <script src="../lib/webgl-utils.js"></script>
  12. <script src="../lib/webgl-debug.js"></script>
  13. <script src="../lib/cuon-utils.js"></script>
  14. <script src="../lib/cuon-matrix.js"></script>
  15. <script src="Triangle_MVPMatrix.js"></script>
  16. </body>
  17. </html>

与之间的代码相比,这段代码主要是引入了一个cuon-matrix.js,这个是一个图形矩阵的处理库,能够方便与GLSL进行交互。

2.2. Triangle_MVPMatrix.js

  1. // 顶点着色器程序
  2. var VSHADER_SOURCE =
  3. 'attribute vec4 a_Position;\n' + // attribute variable
  4. 'attribute vec4 a_Color;\n' +
  5. 'uniform mat4 u_MvpMatrix;\n' +
  6. 'varying vec4 v_Color;\n' +
  7. 'void main() {\n' +
  8. ' gl_Position = u_MvpMatrix * a_Position;\n' + // Set the vertex coordinates of the point
  9. ' v_Color = a_Color;\n' +
  10. '}\n';
  11. // 片元着色器程序
  12. var FSHADER_SOURCE =
  13. 'precision mediump float;\n' +
  14. 'varying vec4 v_Color;\n' +
  15. 'void main() {\n' +
  16. ' gl_FragColor = v_Color;\n' +
  17. '}\n';
  18. function main() {
  19. // 获取 <canvas> 元素
  20. var canvas = document.getElementById('webgl');
  21. // 获取WebGL渲染上下文
  22. var gl = getWebGLContext(canvas);
  23. if (!gl) {
  24. console.log('Failed to get the rendering context for WebGL');
  25. return;
  26. }
  27. // 初始化着色器
  28. if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
  29. console.log('Failed to intialize shaders.');
  30. return;
  31. }
  32. // 设置顶点位置
  33. var n = initVertexBuffers(gl);
  34. if (n < 0) {
  35. console.log('Failed to set the positions of the vertices');
  36. return;
  37. }
  38. //设置MVP矩阵
  39. setMVPMatrix(gl,canvas);
  40. // 指定清空<canvas>的颜色
  41. gl.clearColor(0.0, 0.0, 0.0, 1.0);
  42. // 开启深度测试
  43. gl.enable(gl.DEPTH_TEST);
  44. // 清空颜色和深度缓冲区
  45. gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  46. // 绘制三角形
  47. gl.drawArrays(gl.TRIANGLES, 0, n);
  48. }
  49. //设置MVP矩阵
  50. function setMVPMatrix(gl,canvas) {
  51. // Get the storage location of u_MvpMatrix
  52. var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
  53. if (!u_MvpMatrix) {
  54. console.log('Failed to get the storage location of u_MvpMatrix');
  55. return;
  56. }
  57. //模型矩阵
  58. var modelMatrix = new Matrix4();
  59. modelMatrix.setTranslate(0.75, 0, 0);
  60. //视图矩阵
  61. var viewMatrix = new Matrix4(); // View matrix
  62. viewMatrix.setLookAt(0, 0, 5, 0, 0, -100, 0, 1, 0);
  63. //投影矩阵
  64. var projMatrix = new Matrix4(); // Projection matrix
  65. projMatrix.setPerspective(30, canvas.width / canvas.height, 1, 100);
  66. //MVP矩阵
  67. var mvpMatrix = new Matrix4();
  68. mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix);
  69. //将MVP矩阵传输到着色器的uniform变量u_MvpMatrix
  70. gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);
  71. }
  72. //
  73. function initVertexBuffers(gl) {
  74. // 顶点坐标和颜色
  75. var verticesColors = new Float32Array([
  76. 0.0, 1.0, -4.0, 0.4, 1.0, 0.4, //绿色在后
  77. -0.5, -1.0, -4.0, 0.4, 1.0, 0.4,
  78. 0.5, -1.0, -4.0, 1.0, 0.4, 0.4,
  79. 0.0, 1.0, -2.0, 1.0, 1.0, 0.4, //黄色在中
  80. -0.5, -1.0, -2.0, 1.0, 1.0, 0.4,
  81. 0.5, -1.0, -2.0, 1.0, 0.4, 0.4,
  82. 0.0, 1.0, 0.0, 0.4, 0.4, 1.0, //蓝色在前
  83. -0.5, -1.0, 0.0, 0.4, 0.4, 1.0,
  84. 0.5, -1.0, 0.0, 1.0, 0.4, 0.4,
  85. ]);
  86. //
  87. var n = 9; // 点的个数
  88. var FSIZE = verticesColors.BYTES_PER_ELEMENT; //数组中每个元素的字节数
  89. // 创建缓冲区对象
  90. var vertexBuffer = gl.createBuffer();
  91. if (!vertexBuffer) {
  92. console.log('Failed to create the buffer object');
  93. return -1;
  94. }
  95. // 将缓冲区对象绑定到目标
  96. gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
  97. // 向缓冲区对象写入数据
  98. gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);
  99. //获取着色器中attribute变量a_Position的地址
  100. var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
  101. if (a_Position < 0) {
  102. console.log('Failed to get the storage location of a_Position');
  103. return -1;
  104. }
  105. // 将缓冲区对象分配给a_Position变量
  106. gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE * 6, 0);
  107. // 连接a_Position变量与分配给它的缓冲区对象
  108. gl.enableVertexAttribArray(a_Position);
  109. //获取着色器中attribute变量a_Color的地址
  110. var a_Color = gl.getAttribLocation(gl.program, 'a_Color');
  111. if (a_Color < 0) {
  112. console.log('Failed to get the storage location of a_Color');
  113. return -1;
  114. }
  115. // 将缓冲区对象分配给a_Color变量
  116. gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3);
  117. // 连接a_Color变量与分配给它的缓冲区对象
  118. gl.enableVertexAttribArray(a_Color);
  119. // 解除绑定
  120. gl.bindBuffer(gl.ARRAY_BUFFER, null);
  121. return n;
  122. }

相比之前的代码,主要做了3点改进:

  1. 数据加入Z值;
  2. 加入了深度测试;
  3. MVP矩阵设置;

2.2.1. 数据加入Z值

之前绘制的三角形,只有X坐标和Y坐标,Z值坐标自动补足为默认为0的。在这里会绘制了3个三角形,每个三角形的深度不同。如下代码所示,定义了3个三角形9个点,每个点包含xyz信息和rgb信息:

  1. // 顶点坐标和颜色
  2. var verticesColors = new Float32Array([
  3. 0.0, 1.0, -4.0, 0.4, 1.0, 0.4, //绿色在后
  4. -0.5, -1.0, -4.0, 0.4, 1.0, 0.4,
  5. 0.5, -1.0, -4.0, 1.0, 0.4, 0.4,
  6. 0.0, 1.0, -2.0, 1.0, 1.0, 0.4, //黄色在中
  7. -0.5, -1.0, -2.0, 1.0, 1.0, 0.4,
  8. 0.5, -1.0, -2.0, 1.0, 0.4, 0.4,
  9. 0.0, 1.0, 0.0, 0.4, 0.4, 1.0, //蓝色在前
  10. -0.5, -1.0, 0.0, 0.4, 0.4, 1.0,
  11. 0.5, -1.0, 0.0, 1.0, 0.4, 0.4,
  12. ]);

这意味着与着色器传输变量的函数gl.vertexAttribPointer()的参数也得相应的变化。注意要深入理解这个函数每个参数代表的含义:

  1. // ...
  2. // 将缓冲区对象分配给a_Position变量
  3. gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE * 6, 0);
  4. // ...
  5. // 将缓冲区对象分配给a_Color变量
  6. gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3);

2.2.2. 加入深度测试

在默认情况下,WebGL是根据顶点在缓冲区的顺序来进行绘制的,后绘制的图形会覆盖已经绘制好的图形。但是这样往往与实际物体遮挡情况不同,造成一些很怪异的现象,比如远的物体反而遮挡了近的物体。所以WebGL提供了一种深度检测(DEPTH_TEST)的功能,启用该功能就会检测物体(实际是每个像素)的深度,来决定是否绘制。其启用函数为:
2
除此之外,还应该注意在绘制每一帧之前都应该清除深度缓冲区(depth buffer)。WebGL有多种缓冲区。我们之前用到的与顶点着色器交互的缓冲区对象就是顶点缓冲区,每次重新绘制刷新的就是颜色缓冲区。深度缓冲区记录的就是每个几何图形的深度信息,每绘制一帧,都应清除深度缓冲区:
3
在本例中的相关代码为:

  1. // ...
  2. // 开启深度测试
  3. gl.enable(gl.DEPTH_TEST);
  4. // 清空颜色和深度缓冲区
  5. gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  6. // ...

2.2.3. MVP矩阵设置

在上一篇教程中提到过,WebGL的任何图形变换过程影响的都是物体的顶点,模型变换、视图变换、投影变换都是在顶点着色器中实现的。由于每个顶点都是要进行模型视图投影变换的,所以可以合并成一个MVP矩阵,将其传入到顶点着色器中的:

  1. //...
  2. 'uniform mat4 u_MvpMatrix;\n' +
  3. 'void main() {\n' +
  4. ' gl_Position = u_MvpMatrix * a_Position;\n' + // Set the vertex coordinates of the point
  5. //...
  6. '}\n';

在函数setMVPMatrix()中,创建了MVP矩阵,并将其传入到着色器:

  1. //设置MVP矩阵
  2. function setMVPMatrix(gl,canvas) {
  3. // Get the storage location of u_MvpMatrix
  4. var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
  5. if (!u_MvpMatrix) {
  6. console.log('Failed to get the storage location of u_MvpMatrix');
  7. return;
  8. }
  9. //模型矩阵
  10. var modelMatrix = new Matrix4();
  11. modelMatrix.setTranslate(0.75, 0, 0);
  12. //视图矩阵
  13. var viewMatrix = new Matrix4(); // View matrix
  14. viewMatrix.setLookAt(0, 0, 5, 0, 0, -100, 0, 1, 0);
  15. //投影矩阵
  16. var projMatrix = new Matrix4(); // Projection matrix
  17. projMatrix.setPerspective(30, canvas.width / canvas.height, 1, 100);
  18. //MVP矩阵
  19. var mvpMatrix = new Matrix4();
  20. mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix);
  21. //将MVP矩阵传输到着色器的uniform变量u_MvpMatrix
  22. gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);
  23. }

在上述代码中,依次分别设置了:

  • 模型矩阵:X方向上平移了0.75个单位。
  • 视图矩阵:视点为(0,0,5),观察点为(0,0,-100),上方向为(0,1,0)的观察视角。
  • 投影矩阵:垂直张角为30,画图视图的宽高比,近截面距离为1,远截面为100的视锥体。

三者级联,得到MVP矩阵,将其传入到顶点着色器中。

3. 结果

用浏览器打开Triangle_MVPMatrix.html,就会发现浏览器页面显示了一个由远及近,近大远小的三个三角形。如图所示:
1

4. 参考

本来部分代码和插图来自《WebGL编程指南》,源代码链接:https://share.weiyun.com/5VjlUKo ,密码:sw0x2x。会在此共享目录中持续更新后续的内容。

原文链接:http://www.cnblogs.com/charlee44/p/11625869.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号