我们可以看看对应的二维中图形的平移,其实就可以理解为对应的点的移动,我们来看看顶点着色器中的核心代码

一. 核心代码

  1. <canvas id="canvas"></canvas>
  2. <div id="uiContainer">
  3. <div id="ui">
  4. <div id="x"></div>
  5. <div id="y"></div>
  6. </div>
  7. </div>
  8. <!-- vertex shader -->
  9. <script id="vertex-shader-2d" type="x-shader/x-vertex">
  10. attribute vec2 a_position;
  11. uniform vec2 u_resolution;
  12. void main() {
  13. // 将矩形点从像素转换为 0.0 到 1.0
  14. vec2 zeroToOne = a_position / u_resolution;
  15. // 从 0->1 转换为 0->2
  16. vec2 zeroToTwo = zeroToOne * 2.0;
  17. // 从 0->2 转换为 -1 -> 1
  18. vec2 clipSpace = zeroToTwo - 1.0;
  19. gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
  20. }
  21. </script>
  22. <!-- fragment shader -->
  23. <script id="fragment-shader-2d" type="x-shader/x-fragment">
  24. precision mediump float;
  25. uniform vec4 u_color;
  26. void main() {
  27. gl_FragColor = u_color;
  28. }
  29. </script>

二. 逻辑流程

首先我们来定义一些变量存储矩形的平移,宽,高和颜色。

  1. var translation = [0, 0];
  2. var width = 100;
  3. var height = 30;
  4. var color = [Math.random(), Math.random(), Math.random(), 1];

然后定义一个方法重绘所有东西,我们可以在更新变换之后调用这个方法。

  1. // 绘制场景
  2. function drawScene() {
  3. webglUtils.resizeCanvasToDisplaySize(gl.canvas);
  4. // 告诉WebGL如何从裁剪空间对应到像素
  5. gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  6. // 清空画布
  7. gl.clear(gl.COLOR_BUFFER_BIT);
  8. // 使用我们的程序
  9. gl.useProgram(program);
  10. // 启用属性
  11. gl.enableVertexAttribArray(positionLocation);
  12. // 绑定位置缓冲
  13. gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  14. // 设置矩形参数
  15. setRectangle(gl, translation[0], translation[1], width, height);
  16. // 告诉属性怎么从positionBuffer中读取数据 (ARRAY_BUFFER)
  17. var size = 2; // 每次迭代运行提取两个单位数据
  18. var type = gl.FLOAT; // 每个单位的数据类型是32位浮点型
  19. var normalize = false; // 不需要归一化数据
  20. var stride = 0; // 0 = 移动单位数量 * 每个单位占用内存(sizeof(type))
  21. var offset = 0; // 从缓冲起始位置开始读取
  22. gl.vertexAttribPointer(
  23. positionLocation, size, type, normalize, stride, offset)
  24. // 设置分辨率
  25. gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height);
  26. // 设置颜色
  27. gl.uniform4fv(colorLocation, color);
  28. // 绘制矩形
  29. var primitiveType = gl.TRIANGLES;
  30. var offset = 0;
  31. var count = 6;
  32. gl.drawArrays(primitiveType, offset, count);
  33. }

但是想象一下如果对一个更复杂的图形做类似操作怎么办。
假设我们想绘制一个由六个三角形组成的 ‘F’ ,像这样
image.png
接着当前的代码我们需要修改setRectangle,像这样

  1. // 在缓冲存储构成 'F' 的值
  2. function setGeometry(gl, x, y) {
  3. var width = 100;
  4. var height = 150;
  5. var thickness = 30;
  6. gl.bufferData(
  7. gl.ARRAY_BUFFER,
  8. new Float32Array([
  9. // 左竖
  10. x, y,
  11. x + thickness, y,
  12. x, y + height,
  13. x, y + height,
  14. x + thickness, y,
  15. x + thickness, y + height,
  16. // 上横
  17. x + thickness, y,
  18. x + width, y,
  19. x + thickness, y + thickness,
  20. x + thickness, y + thickness,
  21. x + width, y,
  22. x + width, y + thickness,
  23. // 中横
  24. x + thickness, y + thickness * 2,
  25. x + width * 2 / 3, y + thickness * 2,
  26. x + thickness, y + thickness * 3,
  27. x + thickness, y + thickness * 3,
  28. x + width * 2 / 3, y + thickness * 2,
  29. x + width * 2 / 3, y + thickness * 3,
  30. ]),
  31. gl.STATIC_DRAW);
  32. }
  33. // 在缓冲存储构成 'F' 的值,这样就不需要很多点一起集合
  34. function setGeometry(gl) {
  35. gl.bufferData(
  36. gl.ARRAY_BUFFER,
  37. new Float32Array([
  38. // 左竖
  39. 0, 0,
  40. 30, 0,
  41. 0, 150,
  42. 0, 150,
  43. 30, 0,
  44. 30, 150,
  45. // 上横
  46. 30, 0,
  47. 100, 0,
  48. 30, 30,
  49. 30, 30,
  50. 100, 0,
  51. 100, 30,
  52. // 中横
  53. 30, 60,
  54. 67, 60,
  55. 30, 90,
  56. 30, 90,
  57. 67, 60,
  58. 67, 90,
  59. ]),
  60. gl.STATIC_DRAW);
  61. }
  62. // 通过定点着色器同步移动
  63. var translationLocation = gl.getUniformLocation(
  64. program, "u_translation");
  65. ...
  66. // 创建一个存放位置信息的缓冲
  67. var positionBuffer = gl.createBuffer();
  68. // 绑定到 ARRAY_BUFFER (简单的理解为 ARRAY_BUFFER = positionBuffer)
  69. gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  70. // 将几何数据存到缓冲
  71. setGeometry(gl);
  72. ...
  73. // 绘制场景
  74. function drawScene() {
  75. ...
  76. // 设置平移
  77. gl.uniform2fv(translationLocation, translation);
  78. // 绘制矩形
  79. var primitiveType = gl.TRIANGLES;
  80. var offset = 0;
  81. var count = 18;
  82. gl.drawArrays(primitiveType, offset, count);
  83. }

注意到setGeometry只调用了一次,它不在drawScene内部了。

三. 全部代码

  1. "use strict";
  2. function main() {
  3. // Get A WebGL context
  4. /** @type {HTMLCanvasElement} */
  5. var canvas = document.querySelector("#canvas");
  6. var gl = canvas.getContext("webgl");
  7. if (!gl) {
  8. return;
  9. }
  10. // setup GLSL program
  11. var program = webglUtils.createProgramFromScripts(gl, ["vertex-shader-2d", "fragment-shader-2d"]);
  12. gl.useProgram(program);
  13. // look up where the vertex data needs to go.
  14. var positionLocation = gl.getAttribLocation(program, "a_position");
  15. // lookup uniforms
  16. var resolutionLocation = gl.getUniformLocation(program, "u_resolution");
  17. var colorLocation = gl.getUniformLocation(program, "u_color");
  18. var translationLocation = gl.getUniformLocation(program, "u_translation");
  19. // Create a buffer to put positions in
  20. var positionBuffer = gl.createBuffer();
  21. // Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
  22. gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  23. // Put geometry data into buffer
  24. setGeometry(gl);
  25. var translation = [0, 0];
  26. var color = [Math.random(), Math.random(), Math.random(), 1];
  27. drawScene();
  28. // Setup a ui.
  29. webglLessonsUI.setupSlider("#x", {slide: updatePosition(0), max: gl.canvas.width });
  30. webglLessonsUI.setupSlider("#y", {slide: updatePosition(1), max: gl.canvas.height});
  31. function updatePosition(index) {
  32. return function(event, ui) {
  33. translation[index] = ui.value;
  34. drawScene();
  35. };
  36. }
  37. // Draw the scene.
  38. function drawScene() {
  39. webglUtils.resizeCanvasToDisplaySize(gl.canvas);
  40. // Tell WebGL how to convert from clip space to pixels
  41. gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  42. // Clear the canvas.
  43. gl.clear(gl.COLOR_BUFFER_BIT);
  44. // Tell it to use our program (pair of shaders)
  45. gl.useProgram(program);
  46. // Turn on the attribute
  47. gl.enableVertexAttribArray(positionLocation);
  48. // Bind the position buffer.
  49. gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  50. // Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
  51. var size = 2; // 2 components per iteration
  52. var type = gl.FLOAT; // the data is 32bit floats
  53. var normalize = false; // don't normalize the data
  54. var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
  55. var offset = 0; // start at the beginning of the buffer
  56. gl.vertexAttribPointer(
  57. positionLocation, size, type, normalize, stride, offset);
  58. // set the resolution
  59. gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height);
  60. // set the color
  61. gl.uniform4fv(colorLocation, color);
  62. // Set the translation.
  63. gl.uniform2fv(translationLocation, translation);
  64. // Draw the geometry.
  65. var primitiveType = gl.TRIANGLES;
  66. var offset = 0;
  67. var count = 18; // 6 triangles in the 'F', 3 points per triangle
  68. gl.drawArrays(primitiveType, offset, count);
  69. }
  70. }
  71. // Fill the buffer with the values that define a letter 'F'.
  72. function setGeometry(gl) {
  73. gl.bufferData(
  74. gl.ARRAY_BUFFER,
  75. new Float32Array([
  76. // left column
  77. 0, 0,
  78. 30, 0,
  79. 0, 150,
  80. 0, 150,
  81. 30, 0,
  82. 30, 150,
  83. // top rung
  84. 30, 0,
  85. 100, 0,
  86. 30, 30,
  87. 30, 30,
  88. 100, 0,
  89. 100, 30,
  90. // middle rung
  91. 30, 60,
  92. 67, 60,
  93. 30, 90,
  94. 30, 90,
  95. 67, 60,
  96. 67, 90,
  97. ]),
  98. gl.STATIC_DRAW);
  99. }
  100. main();
  101. // html代码
  102. <canvas id="canvas"></canvas>
  103. <div id="uiContainer">
  104. <div id="ui">
  105. <div id="x"></div>
  106. <div id="y"></div>
  107. </div>
  108. </div>
  109. <!-- vertex shader -->
  110. <script id="vertex-shader-2d" type="x-shader/x-vertex">
  111. attribute vec2 a_position;
  112. uniform vec2 u_resolution;
  113. uniform vec2 u_translation;
  114. void main() {
  115. // Add in the translation.
  116. vec2 position = a_position + u_translation;
  117. // convert the position from pixels to 0.0 to 1.0
  118. vec2 zeroToOne = position / u_resolution;
  119. // convert from 0->1 to 0->2
  120. vec2 zeroToTwo = zeroToOne * 2.0;
  121. // convert from 0->2 to -1->+1 (clipspace)
  122. vec2 clipSpace = zeroToTwo - 1.0;
  123. gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
  124. }
  125. </script>
  126. <!-- fragment shader -->
  127. <script id="fragment-shader-2d" type="x-shader/x-fragment">
  128. precision mediump float;
  129. uniform vec4 u_color;
  130. void main() {
  131. gl_FragColor = u_color;
  132. }
  133. </script>
  134. <script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
  135. <script src="https://webglfundamentals.org/webgl/resources/webgl-lessons-ui.js"></script>