这里有个单位圆。
image.png
当你拖拽蓝色圆点的时候 X 和 Y 会改变,它们是那一点在圆上的坐标, 在最上方时 Y 是 1 并且 X 是 0 ,在最右边的时候 X 是 1 并且 Y 是 0 。
数字和 1 相乘结果不变。 例如 123 * 1 = 123 。非常基础,对吧?那么,单位圆,半径为 1.0 的圆也是 1 的一种形式,它是旋转的 1 。所以你可以把一些东西和单位圆相乘, 除了发生一些魔法和旋转之外,某种程度上和乘以 1 相似。

image.png

一.核心代码

  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 id="rotation"></div>
  7. </div>
  8. </div>
  9. <!-- vertex shader -->
  10. <script id="vertex-shader-2d" type="x-shader/x-vertex">
  11. attribute vec2 a_position;
  12. uniform vec2 u_resolution;
  13. uniform vec2 u_translation;
  14. uniform vec2 u_rotation;
  15. void main() {
  16. // Rotate the position
  17. vec2 rotatedPosition = vec2(
  18. a_position.x * u_rotation.y + a_position.y * u_rotation.x,
  19. a_position.y * u_rotation.y - a_position.x * u_rotation.x);
  20. // Add in the translation.
  21. vec2 position = rotatedPosition + u_translation;
  22. // convert the position from pixels to 0.0 to 1.0
  23. vec2 zeroToOne = position / u_resolution;
  24. // convert from 0->1 to 0->2
  25. vec2 zeroToTwo = zeroToOne * 2.0;
  26. // convert from 0->2 to -1->+1 (clipspace)
  27. vec2 clipSpace = zeroToTwo - 1.0;
  28. gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
  29. }
  30. </script>
  31. <!-- fragment shader -->
  32. <script id="fragment-shader-2d" type="x-shader/x-fragment">
  33. precision mediump float;
  34. uniform vec4 u_color;
  35. void main() {
  36. gl_FragColor = u_color;
  37. }
  38. </script><script src="https://webglfundamentals.org/webgl/resources/jquery-1.7.1.min.js"></script>
  39. <script src="https://webglfundamentals.org/webgl/resources/jquery-ui-1.8.16.custom.min.js"></script>
  40. <script src="https://webglfundamentals.org/webgl/resources/jquery.mousecapture.js"></script>
  41. <script src="https://webglfundamentals.org/webgl/resources/jquery.gman.ui.js"></script>
  42. <script src="https://webglfundamentals.org/webgl/resources/jquery-gman-circle.js"></script>
  43. <script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
  44. <script src="https://webglfundamentals.org/webgl/resources/webgl-lessons-ui.js"></script>

核心代码块
a_position.x u_rotation.y + a_position.y u_rotation.x,
a_position.y u_rotation.y - a_position.x u_rotation.x

假如你想旋转一个矩形,在开始旋转之前矩形右上角坐标是 3.0, 9.0 , 让我们在单位圆上以十二点方向为起点顺时针旋转30度后取一个点。
image.png
圆上该点的位置是 0.50 和 0.87
3.0 0.87 + 9.0 0.50 = 7.1
9.0 0.87 - 3.0 0.50 = 6.3
这个结果正好是我们需要的结果
image.png
你会发现在我们顺时针旋转到右边的过程中,X 变大 Y 变小。如果我们继续旋转超过90 度后,X 变小 Y 变大,这种形式形成了旋转。
单位圆上的点还有一个名字,叫做正弦和余弦。所以对于任意给定角, 我们只需要求出正弦和余弦,像这样

  1. function printSineAndCosineForAnAngle(angleInDegrees) {
  2. var angleInRadians = angleInDegrees * Math.PI / 180;
  3. var s = Math.sin(angleInRadians);
  4. var c = Math.cos(angleInRadians);
  5. console.log("s = " + s + " c = " + c);
  6. }

二.全部代码

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