向attribute变量赋值

接上一节代码, 一旦将attribute变量存储地址保存在js变量a_Position中, 下面就需要使用该变量来想着色器传入值。我们使用gl.vertexAttrib3f()函数来完成这一步

image.png

下面是该函数的规范
gl.vertexAttrib3f(location, v0, v1,v2)

参数 location 指定要修改的attribute变量的存储位置
v0 指定填充attribute变量第一个分量的值
v1 指定填充attribute变量第二个分量的值
v2 指定填充attribute变量第三个分量的值
返回值
错误 INVALID_OPERATION 没有当前的program对象
INVALID_VALUE location大于等于attribute变量的最大数目

该函数第一个参数是attribute变量的存储地址,即gl.getAttribLocation()的返回值。第二三四个参数是三个浮点型数值。即点的x, y, z坐标值。函数被调用后,这三个值被一起传给顶点着色器中的a_Position变量。

gl.vertexAttrib3f()是一系列同族函数中的一个,该系列函数的任务就是从js向顶点着色器中的attribute变量传值。

通过鼠标点击绘点

效果如图
image.png

  1. <!doctype html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport"
  6. content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  7. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  8. <title>Document</title>
  9. </head>
  10. <body onload="main()">
  11. <canvas id="canvas" height="400" width="400">
  12. 你的浏览器不支持WebGL,请更换新的浏览器
  13. </canvas>
  14. </body>
  15. <script src="../lib/webgl-util.js" ></script>
  16. <script src="../lib/webgl-debug.js" ></script>
  17. <script src="../lib/cuon-utils.js" ></script>
  18. <script>
  19. //顶点着色器程序
  20. var VSHADER_SOURCE="" +
  21. "attribute vec4 a_Position;\n" +
  22. "void main(){\n" +
  23. " gl_Position = a_Position;\n" +//设置坐标
  24. " gl_PointSize = 10.0;\n" +//设置尺寸
  25. "}\n";
  26. //片元着色器程序
  27. var FSHADER_SOURCE = "" +
  28. "void main(){\n" +
  29. " gl_FragColor = vec4(1.0,0.0,0.0,1.0);\n" +//设置颜色
  30. "}\n";
  31. function main() {
  32. //首先获取到canvas的dom对象
  33. var canvas = document.getElementById("canvas");
  34. //获取到WebGL的上下文
  35. var gl = getWebGLContext(canvas);
  36. //不支持WebGL的浏览器将打印一个错误,并结束代码运行
  37. if (!gl) {
  38. console.log("浏览器不支持WebGL");
  39. return;
  40. }
  41. //初始化着色器
  42. if(!initShaders(gl,VSHADER_SOURCE,FSHADER_SOURCE)){
  43. console.log("初始化着色器失败");
  44. return;
  45. }
  46. // 获取attribute变量的存储位置
  47. var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
  48. // 将顶点位置传输给attribute变量
  49. // gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0);
  50. // 注册鼠标点击事件响应函数
  51. canvas.onmousedown = function(ev) {
  52. click(ev, gl, canvas, a_Position);
  53. }
  54. //指定一个覆盖(清空)canvas的颜色
  55. gl.clearColor(0.0, 0.0, 0.0, 1.0);
  56. //执行清空
  57. gl.clear(gl.COLOR_BUFFER_BIT);
  58. // //绘制一个点
  59. // gl.drawArrays(gl.POINTS,0,1);
  60. }
  61. var g_points = [];
  62. function click(ev, gl, canvas, a_Position) {
  63. var x = ev.clientX;
  64. var y = ev.clientY;
  65. var rect = ev.target.getBoundingClientRect();
  66. x = ((x - rect.left) - canvas.width/2)/(canvas.height/2);
  67. y = (canvas.height/2 - (y-rect.top)) / (canvas.width/2);
  68. // 将坐标存储到g_points数组中
  69. g_points.push(x);
  70. g_points.push(y);
  71. gl.clear(gl.COLOR_BUFFER_BIT);
  72. var len = g_points.length;
  73. for (let i = 0; i < len; i+=2) {
  74. gl.vertexAttrib3f(a_Position, g_points[i], g_points[i+1], 0.0);
  75. gl.drawArrays(gl.POINTS,0,1);
  76. }
  77. }
  78. </script>
  79. </html>

看完上面一段吧啦吧啦一堆代码之后,最终就会实现我们想要的效果。 可以发现,80%的代码都是前面写过的代码,只有后面做了一些调整。 下面就来分析下原理

注册事件响应函数

获取了webGL上下文,初始化了着色器,获取了attribute变量的存储地址 53-74行。 这些流程与前面的demo一致。 主要区别是后面定义和注册了click函数。
事件响应函数能够响应用户在网页上的操作。关于如何注册一个事件响应函数这里就不多说了,是js的基础知识。

响应鼠标点击事件

来看下click函数到底做了什么

  1. 获取鼠标点击的位置,并存储在一个数组中
  2. 清空canvas
  3. 根据数组的每个元素,在相应的位置绘制点

鼠标点击的位置信息存储在ev对象中,我们可以直接获取到点击坐标。但是,由于以下两个原因,不能直接使用这两个坐标值

  1. 鼠标点击的位置是在浏览器客户区中的坐标(client area中), 而不是在canvas中的
  2. canvas的坐标系与webGL的坐标系其原点位置和y轴的正方向位置也不一样。

image.png

回顾这张图
我们需要先把鼠标坐标转为canvas坐标,然后再转为WebGL坐标
image.png

这里就是坐标转换代码
首先, 获取canvas在浏览器中的坐标, rect.left, rect.top是canvas原点在浏览器中的坐标。
x - rect.left和y - rect.top就是把鼠标点击坐标转为canvas坐标。
接下来, canvas坐标转为WebGL坐标。 做这一步我们需要知道canvas的中心点坐标。 canvas.height和canvas.width获取canvas的高和宽 而中心点坐标就是(canvas.height/2, canvas.width/2)
然后利用((x - rect.left) - canvas.width/2) 和 (canvas.height/2 - (y-rect.top))将canvas原点平移到中心点
因为webGL坐标系区间是从-1到1 所以x坐除以 canvas.height/2 y轴坐标除以canvas.height/2 将canvas坐标映射到webGL坐标。

每一次鼠标点击的时候都会将坐标存储在g_points中。WebGL使用的是颜色缓冲区。系统中的绘制操作实际上是在颜色缓冲区中进行绘制的,绘制结束后系统将缓冲区中的内容显示在屏幕上,然后颜色缓冲区就会被重置。其中的内容会丢失。因此需要把每次的点击坐标都记录下来,鼠标每次点击之后,程序都重新绘制了所有的点。
代码中我们清空了canvas, 然后把点依次绘制出来。