使用 Three.js 转换 GLSL 粒子结果着色器

打印 上一主题 下一主题

主题 933|帖子 933|积分 2803

各人好!我是 [数擎AI],一位热爱探索新技能的前端开辟者,在这里分享前端和 Web3D、AI 技能的干货与实战经验。假如你对技能有热情,接待关注我的文章,我们一起发展、进步!
开辟领域:前端开辟 | AI 应用 | Web3D | 元宇宙
技能栈:JavaScript、React、ThreeJs、WebGL、Go
经验经验:6 年+ 前端开辟经验,专注于图形渲染和 AI 技能
开源项目:AI简历、元宇宙、数字孪生
  在这篇博客中,我们将探究如何将一个经典的 GLSL 粒子结果着色器转换成 Three.js 可用的自定义着色器,并通过交互控制其行为。这将是一个渐渐教程,帮助你理解如何在 Three.js 中应用自定义 GLSL 代码并通过鼠标交互调整结果。
目标

我们将使用 Three.js 渲染一个基于粒子的视觉结果,类似于一个动态生成的渐变色圆环,随着时间的推移,颜色和外形会发生厘革。用户还可以通过鼠标移动来控制缩放和动画连续时间。
步骤一:预备工作

首先,你需要确保你的项目中包罗了 Three.js。假如你还没有安装 Three.js,可以通过 npm 安装:
  1. npm install three
复制代码
假如你使用的是纯 HTML 文件,也可以直接在 HTML 中引入 Three.js:
  1. <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
复制代码
步骤二:GLSL 粒子结果着色器代码解析

原始的 GLSL 代码创建了一个基于时间厘革的粒子系统,颜色从绿色到蓝色渐变,而且粒子的大小和分布随时间厘革。核心的功能包括:


  • 粒子数量(n):控制生成的粒子数量。
  • 起始和竣事颜色(startColor 和 endColor):粒子颜色的渐变。
  • 粒子半径厘革:随着粒子隔断中心的厘革,粒子的半径渐渐增大。
  • 交互性:鼠标控制缩放和动画的连续时间。
步骤三:在 Three.js 中实现 GLSL 着色器

接下来,我们将创建一个简单的 Three.js 场景,并将这个 GLSL 着色器嵌入其中。
完整代码实现

  1. import * as THREE from 'three';
  2. const scene = new THREE.Scene();
  3. const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
  4. const renderer = new THREE.WebGLRenderer();
  5. renderer.setSize(window.innerWidth, window.innerHeight);
  6. document.body.appendChild(renderer.domElement);
  7. // Shader code
  8. const vertexShader = `
  9.     void main() {
  10.         gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
  11.     }
  12. `;
  13. const fragmentShader = `
  14.     uniform float iTime;
  15.     uniform vec3 iResolution;
  16.     uniform vec3 iMouse;
  17.     void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
  18.         float t = iTime+5.0;
  19.         float z = 6.0;
  20.         const int n = 100; // particle count
  21.         vec3 startColor = vec3(0.0, 0.64, 0.2);
  22.         vec3 endColor = vec3(0.06, 0.35, 0.85);
  23.         float startRadius = 0.84;
  24.         float endRadius = 1.6;
  25.         float power = 0.51;
  26.         float duration = 4.0;
  27.         vec2 s = iResolution.xy;
  28.         vec2 v = z * (2.0 * fragCoord.xy - s) / s.y;
  29.         // Mouse axis y => zoom
  30.         if(iMouse.z > 0.0) v *= iMouse.y / s.y * 20.0;
  31.         // Mouse axis x => duration
  32.         if(iMouse.z > 0.0) duration = iMouse.x / s.x * 10.0;
  33.         vec3 col = vec3(0.0);
  34.         vec2 pm = v.yx * 2.8;
  35.         float dMax = duration;
  36.         float evo = (sin(iTime * 0.01 + 400.0) * 0.5 + 0.5) * 99.0 + 1.0;
  37.         float mb = 0.0;
  38.         float mbRadius = 0.0;
  39.         float sum = 0.0;
  40.         for(int i = 0; i < n; i++) {
  41.             float d = fract(t * power + 48934.4238 * sin(float(i / int(evo)) * 692.7398));
  42.             float tt = 0.0;
  43.             float a = 6.28 * float(i) / float(n);
  44.             float x = d * cos(a) * duration;
  45.             float y = d * sin(a) * duration;
  46.             float distRatio = d / dMax;
  47.             mbRadius = mix(startRadius, endRadius, distRatio);
  48.             vec2 p = v - vec2(x, y);
  49.             mb = mbRadius / dot(p, p);
  50.             sum += mb;
  51.             col = mix(col, mix(startColor, endColor, distRatio), mb / sum);
  52.         }
  53.         sum /= float(n);
  54.         col = normalize(col) * sum;
  55.         sum = clamp(sum, 0.0, 0.4);
  56.         vec3 tex = vec3(1.0);
  57.         col *= smoothstep(tex, vec3(0.0), vec3(sum));
  58.         fragColor.rgb = col;
  59.     }
  60.     void main() {
  61.         vec2 fragCoord = gl_FragCoord.xy;
  62.         mainImage(gl_FragColor, fragCoord);
  63.     }
  64. `;
  65. const uniforms = {
  66.     iTime: { value: 0.0 },
  67.     iResolution: { value: new THREE.Vector3(window.innerWidth, window.innerHeight, 1) },
  68.     iMouse: { value: new THREE.Vector3(0, 0, 0) }
  69. };
  70. const material = new THREE.ShaderMaterial({
  71.     vertexShader: vertexShader,
  72.     fragmentShader: fragmentShader,
  73.     uniforms: uniforms
  74. });
  75. const geometry = new THREE.PlaneGeometry(2, 2);
  76. const mesh = new THREE.Mesh(geometry, material);
  77. scene.add(mesh);
  78. // Set camera position
  79. camera.position.z = 5;
  80. function animate() {
  81.     requestAnimationFrame(animate);
  82.    
  83.     uniforms.iTime.value += 0.05;
  84.     window.addEventListener('mousemove', (event) => {
  85.         uniforms.iMouse.value.x = event.clientX;
  86.         uniforms.iMouse.value.y = window.innerHeight - event.clientY;
  87.         uniforms.iMouse.value.z = 1;
  88.     });
  89.     renderer.render(scene, camera);
  90. }
  91. animate();
复制代码
步骤四:代码解析


  • ShaderMaterial 创建:我们通过 THREE.ShaderMaterial 来定义自定义的着色器,并将 GLSL 代码传入其中。通过 uniforms 对象转达外部变量(如时间、分辨率、鼠标位置)到着色器中。
  • 极点着色器:这是一个简单的极点着色器,它将每个极点的坐标从模型空间转换到裁剪空间。
  • 片段着色器:在片段着色器中,我们实现了粒子结果的核心逻辑,包括动态盘算粒子颜色、大小以及它们的分布。我们使用 sin 函数和一些数学操纵来实现粒子结果的厘革。
  • 鼠标交互:通过 mousemove 事件监听器,我们捕捉到鼠标的 X 和 Y 位置,并转达给着色器。鼠标的 Y 轴控制图形的缩放,而 X 轴控制动画的连续时间。
  • 动画循环:使用 requestAnimationFrame 来实现平滑的动画结果,定期更新时间和鼠标状态,并重新渲染场景。
步骤五:优化与扩展

在实际开辟中,你可以根据需要对该代码进行优化和扩展。以下是一些常见的改进方式:


  • 性能优化:增加粒子数量时可能会导致性能降落,可以通过减少 n 或优化粒子盘算逻辑来改善性能。
  • 扩展交互性:除了鼠标交互,你还可以加入键盘控制、触摸事件等方式,使用户体验更加丰富。
  • 增加更多粒子结果:可以通过修改着色器中的数学公式,增加更多动态的粒子结果,如旋转、外形厘革等。
总结

通过本教程,你学会了如何将一个 GLSL 粒子结果着色器转化为 Three.js 中的自定义着色器,并通过鼠标交互来控制图形的缩放和动画时间。这种方法让你可以在 Web 上实现复杂的视觉结果,具有高度的自定义性和机动性。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

东湖之滨

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表