index.vue 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. <template>
  2. <div>
  3. <canvas ref="webgl" class="webgl"></canvas>
  4. </div>
  5. </template>
  6. <script>
  7. import * as THREE from 'three'
  8. export default {
  9. name: "WebGLComponent",
  10. data() {
  11. return {
  12. mouseX: 0, // 鼠标的X坐标,用于追踪鼠标移动
  13. mouseY: 0 // 鼠标的Y坐标
  14. };
  15. },
  16. mounted() {
  17. this.initWebGL(); // 组件加载完成后,初始化WebGL
  18. },
  19. methods: {
  20. // 初始化WebGL的方法
  21. initWebGL() {
  22. const canvas = this.$refs.webgl; // 获取canvas元素
  23. canvas.width = window.innerWidth; // 设置canvas的宽度为窗口宽度
  24. canvas.height = window.innerHeight; // 设置canvas的高度为窗口高度
  25. const gl = canvas.getContext("webgl"); // 获取WebGL上下文
  26. // 顶点着色器源代码
  27. const vertexShaderSource = `
  28. attribute vec4 position; // 顶点位置
  29. attribute float scale; // 顶点大小
  30. uniform mat4 modelViewMatrix; // 视图矩阵
  31. uniform mat4 projectionMatrix; // 投影矩阵
  32. void main() {
  33. vec4 mvPosition = modelViewMatrix * position; // 计算模型视图变换后的顶点位置
  34. gl_PointSize = scale * 1.0 * (200.0 / - mvPosition.z); // 根据深度调整点的大小
  35. gl_Position = projectionMatrix * mvPosition; // 最终的顶点位置
  36. }
  37. `;
  38. // 片段着色器源代码
  39. const fragShaderSource = `
  40. void main() {
  41. if (length(gl_PointCoord - vec2(0.5, 0.5)) > 0.49) discard; // 如果超出圆形范围,则丢弃
  42. gl_FragColor = vec4(1.0, 0.51, 0.65, 1.0); // 设置点的颜色
  43. }
  44. `;
  45. // 初始化着色器程序
  46. const program = this.initShader(gl, vertexShaderSource, fragShaderSource);
  47. // 获取着色器中属性和uniform的位置
  48. const aposLocation = gl.getAttribLocation(program, 'position');
  49. const scale = gl.getAttribLocation(program, 'scale');
  50. const modelViewMatrixLoc = gl.getUniformLocation(program, 'modelViewMatrix');
  51. const projectionMatrixLoc = gl.getUniformLocation(program, 'projectionMatrix');
  52. // 粒子系统的参数
  53. const SEPARATION = 100, AMOUNTX = 50, AMOUNTY = 50;
  54. const numParticles = AMOUNTX * AMOUNTY;
  55. // 存储粒子的位置和大小
  56. const positions = new Float32Array(numParticles * 3);
  57. const scales = new Float32Array(numParticles);
  58. let i = 0, j = 0;
  59. // 初始化粒子的位置和大小
  60. for (let ix = 0; ix < AMOUNTX; ix++) {
  61. for (let iy = 0; iy < AMOUNTY; iy++) {
  62. positions[i] = ix * SEPARATION - ((AMOUNTX * SEPARATION) / 2); // x
  63. positions[i + 1] = 0; // y
  64. positions[i + 2] = iy * SEPARATION - ((AMOUNTY * SEPARATION) / 2); // z
  65. scales[j] = 1; // 默认大小
  66. i += 3;
  67. j++;
  68. }
  69. }
  70. // 创建颜色缓冲区
  71. const colorBuffer = gl.createBuffer();
  72. gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
  73. gl.bufferData(gl.ARRAY_BUFFER, scales, gl.STATIC_DRAW);
  74. gl.vertexAttribPointer(scale, 1, gl.FLOAT, false, 0, 0);
  75. gl.enableVertexAttribArray(scale);
  76. // 创建位置缓冲区
  77. const buffer = gl.createBuffer();
  78. gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  79. gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
  80. gl.vertexAttribPointer(aposLocation, 3, gl.FLOAT, false, 0, 0);
  81. gl.enableVertexAttribArray(aposLocation);
  82. // 开启深度测试
  83. gl.enable(gl.DEPTH_TEST);
  84. // 初始化相机
  85. const width = window.innerWidth;
  86. const height = window.innerHeight;
  87. const camera = new THREE.PerspectiveCamera(60, width / height, 1, 10000);
  88. camera.position.set(944, 206, -262); // 设置相机位置
  89. camera.lookAt(new THREE.Vector3(0, 0, 0)); // 设置相机朝向
  90. camera.updateProjectionMatrix(); // 更新相机的投影矩阵
  91. camera.updateMatrixWorld(true); // 更新相机的世界矩阵
  92. // 将相机的投影矩阵传递给WebGL
  93. const mat4 = new THREE.Matrix4();
  94. mat4.copy(camera.projectionMatrix);
  95. const mxArr = new Float32Array(mat4.elements);
  96. gl.uniformMatrix4fv(projectionMatrixLoc, false, mxArr);
  97. // 将相机的反向矩阵传递给WebGL
  98. const mat4y = new THREE.Matrix4();
  99. mat4y.copy(camera.matrixWorldInverse);
  100. const myArr = new Float32Array(mat4y.elements);
  101. gl.uniformMatrix4fv(modelViewMatrixLoc, false, myArr);
  102. let count = 0;
  103. // 动画绘制函数
  104. const draw = () => {
  105. // 根据鼠标位置更新相机的位置
  106. camera.position.x += (this.mouseX - camera.position.x) * 0.01;
  107. camera.updateMatrixWorld(true); // 更新相机世界矩阵
  108. mat4y.copy(camera.matrixWorldInverse); // 获取更新后的相机反向矩阵
  109. const myArr = new Float32Array(mat4y.elements);
  110. gl.uniformMatrix4fv(modelViewMatrixLoc, false, myArr); // 传递新的矩阵给WebGL
  111. // 更新粒子的位置和大小
  112. let i = 0, j = 0;
  113. for (let ix = 0; ix < AMOUNTX; ix++) {
  114. for (let iy = 0; iy < AMOUNTY; iy++) {
  115. positions[i + 1] = (Math.sin((ix + count) * 0.3) * 50) + (Math.sin((iy + count) * 0.5) * 50);
  116. scales[j] = (Math.sin((ix + count) * 0.3) + 1.3) * 8 + (Math.sin((iy + count) * 0.5) + 1.3) * 8;
  117. i += 3;
  118. j++;
  119. }
  120. }
  121. count += 0.1;
  122. // 更新缓冲区的数据
  123. gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
  124. gl.bufferData(gl.ARRAY_BUFFER, scales, gl.STATIC_DRAW);
  125. gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  126. gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
  127. requestAnimationFrame(draw); // 请求下一帧绘制
  128. gl.drawArrays(gl.POINTS, 0, 2500); // 绘制粒子
  129. };
  130. draw(); // 启动绘制
  131. // 监听鼠标和触摸事件
  132. document.addEventListener('mousemove', this.onDocumentMouseMove, false);
  133. document.addEventListener('touchstart', this.onDocumentTouchStart, false);
  134. document.addEventListener('touchmove', this.onDocumentTouchMove, false);
  135. // 监听窗口大小变化,更新画布和相机的投影矩阵
  136. window.onresize = () => {
  137. canvas.width = window.innerWidth;
  138. canvas.height = window.innerHeight;
  139. gl.viewport(0, 0, window.innerWidth, window.innerHeight);
  140. camera.aspect = window.innerWidth / window.innerHeight;
  141. camera.updateProjectionMatrix();
  142. mat4.copy(camera.projectionMatrix);
  143. const mxArr = new Float32Array(mat4.elements);
  144. gl.uniformMatrix4fv(projectionMatrixLoc, false, mxArr);
  145. };
  146. },
  147. // 初始化着色器的方法
  148. initShader(gl, vertexShaderSource, fragmentShaderSource) {
  149. const vertexShader = gl.createShader(gl.VERTEX_SHADER);
  150. const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
  151. gl.shaderSource(vertexShader, vertexShaderSource);
  152. gl.shaderSource(fragmentShader, fragmentShaderSource);
  153. gl.compileShader(vertexShader);
  154. gl.compileShader(fragmentShader);
  155. const program = gl.createProgram();
  156. gl.attachShader(program, vertexShader);
  157. gl.attachShader(program, fragmentShader);
  158. gl.linkProgram(program);
  159. gl.useProgram(program);
  160. return program;
  161. },
  162. // 鼠标移动事件处理
  163. onDocumentMouseMove(event) {
  164. this.mouseX = event.clientX - window.innerWidth / 2;
  165. this.mouseY = event.clientY - window.innerHeight / 2;
  166. },
  167. // 触摸开始事件处理
  168. onDocumentTouchStart(event) {
  169. if (event.touches.length === 1) {
  170. event.preventDefault();
  171. this.mouseX = event.touches[0].pageX - window.innerWidth / 2;
  172. this.mouseY = event.touches[0].pageY - window.innerHeight / 2;
  173. }
  174. },
  175. // 触摸移动事件处理
  176. onDocumentTouchMove(event) {
  177. if (event.touches.length === 1) {
  178. event.preventDefault();
  179. this.mouseX = event.touches[0].pageX - window.innerWidth / 2;
  180. this.mouseY = event.touches[0].pageY - window.innerHeight / 2;
  181. }
  182. }
  183. }
  184. };
  185. </script>
  186. <style scoped>
  187. body {
  188. padding: 0;
  189. margin: 0;
  190. background-color: transparent;
  191. font-family: "微软雅黑";
  192. overflow: hidden;
  193. }
  194. .webgl {
  195. position: absolute;
  196. bottom: 0;
  197. left: 0;
  198. /* height: 100%; */
  199. background-color: transparent;
  200. width: 100%;
  201. }
  202. </style>