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

server/2025/2/27 10:57:59/

大家好!我是 [数擎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 安装:

npm install three

如果你使用的是纯 HTML 文件,也可以直接在 HTML 中引入 Three.js:

<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 着色器嵌入其中。

完整代码实现

javascript">
import * as THREE from 'three';const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);// Shader code
const vertexShader = `void main() {gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);}
`;const fragmentShader = `uniform float iTime;uniform vec3 iResolution;uniform vec3 iMouse;void mainImage( out vec4 fragColor, in vec2 fragCoord ) {float t = iTime+5.0;float z = 6.0;const int n = 100; // particle countvec3 startColor = vec3(0.0, 0.64, 0.2);vec3 endColor = vec3(0.06, 0.35, 0.85);float startRadius = 0.84;float endRadius = 1.6;float power = 0.51;float duration = 4.0;vec2 s = iResolution.xy;vec2 v = z * (2.0 * fragCoord.xy - s) / s.y;// Mouse axis y => zoomif(iMouse.z > 0.0) v *= iMouse.y / s.y * 20.0;// Mouse axis x => durationif(iMouse.z > 0.0) duration = iMouse.x / s.x * 10.0;vec3 col = vec3(0.0);vec2 pm = v.yx * 2.8;float dMax = duration;float evo = (sin(iTime * 0.01 + 400.0) * 0.5 + 0.5) * 99.0 + 1.0;float mb = 0.0;float mbRadius = 0.0;float sum = 0.0;for(int i = 0; i < n; i++) {float d = fract(t * power + 48934.4238 * sin(float(i / int(evo)) * 692.7398));float tt = 0.0;float a = 6.28 * float(i) / float(n);float x = d * cos(a) * duration;float y = d * sin(a) * duration;float distRatio = d / dMax;mbRadius = mix(startRadius, endRadius, distRatio);vec2 p = v - vec2(x, y);mb = mbRadius / dot(p, p);sum += mb;col = mix(col, mix(startColor, endColor, distRatio), mb / sum);}sum /= float(n);col = normalize(col) * sum;sum = clamp(sum, 0.0, 0.4);vec3 tex = vec3(1.0);col *= smoothstep(tex, vec3(0.0), vec3(sum));fragColor.rgb = col;}void main() {vec2 fragCoord = gl_FragCoord.xy;mainImage(gl_FragColor, fragCoord);}
`;const uniforms = {iTime: { value: 0.0 },iResolution: { value: new THREE.Vector3(window.innerWidth, window.innerHeight, 1) },iMouse: { value: new THREE.Vector3(0, 0, 0) }
};const material = new THREE.ShaderMaterial({vertexShader: vertexShader,fragmentShader: fragmentShader,uniforms: uniforms
});const geometry = new THREE.PlaneGeometry(2, 2);
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);// Set camera position
camera.position.z = 5;function animate() {requestAnimationFrame(animate);uniforms.iTime.value += 0.05; window.addEventListener('mousemove', (event) => {uniforms.iMouse.value.x = event.clientX;uniforms.iMouse.value.y = window.innerHeight - event.clientY; uniforms.iMouse.value.z = 1; });renderer.render(scene, camera);
}animate();

步骤四:代码解析

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

步骤五:优化与扩展

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

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

总结

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


http://www.ppmy.cn/server/171016.html

相关文章

怎么修改node_modules里的文件,怎么使用patch-package修改node_modules的文件,怎么修改第三方库原文件。

在开发中会遇到需要node_modules里第三方库有bug&#xff0c;然后需要修改node_modules文件的情况 使用patch-package包可以修改node_modules里的文件 patch-package npm 官网&#xff1a;patch-package - npm 安装 npm i patch-package 修改文件后 npx patch-package s…

SGMII(Serial Gigabit Media Independent Interface)详解

一、SGMII的定义与作用 SGMII&#xff08;串行千兆介质无关接口&#xff09;是一种用于千兆以太网&#xff08;1Gbps&#xff09;的串行接口标准&#xff0c;旨在通过减少引脚数量和简化设计&#xff0c;实现MAC层与PHY芯片之间的高速通信。其核心作用包括&#xff1a; 引脚精…

STM32+ESP8266局域网通信

若该文为原创文章&#xff0c;转载请注明原文出处。 此篇记录如何使用APP&#xff0c;在局域网内和STM32通信&#xff0c;并远程控制。 一、原理 STM32通过串口AT指令控制ESP8266(ESP-01S), ESP8266会产生一个热点&#xff0c;并创建了TCP服务&#xff0c;手机连接热点&#…

C# tostring 转换成16进制

在 C# 中&#xff0c;将整数或其他数据类型转换为十六进制字符串可以使用 ToString 方法&#xff0c;并指定格式化字符串为 "X" 或 "x"。以下是详细的实现方法和示例&#xff1a; 1. 整数转换为十六进制字符串 使用 ToString 方法并指定格式化字符串为 &q…

Redis Lua Script 溢出漏洞(CVE-2024-31449)

目录 漏洞描述 目前受影响的Redis版本&#xff1a; 安全版本 解决建议 升级Redis版本 查看旧redis版本信息 备份Redis数据 1.查看目前redis的key 2.备份数据 3.查看备份文件地址 4.将旧Redis安装目录备份 安装新版本Redis 1.下载redis安装包 2.安装redis 3.启动…

图像处理之图像边缘检测算法

目录 1 图像边缘检测算法简介 2 Sobel边缘检测 3 经典的Canny边缘检测算法 4 演示Demo 4.1 开发环境 4.2 功能介绍 4.3 下载地址 参考 1 图像边缘检测算法简介 图像边缘检测是计算机视觉和图像处理中的基本问题&#xff0c;主要目的是提取图像中明暗变化明显的边缘细节…

MySQL中replace into详解、批量更新、不存在插入存在则更新、replace into的坑

文章目录 一、replace into原理二、replace into的三种形式三、replace into 使用案例 3.1、replace into values 3.1.1、只有主键且主键冲突3.1.2、有主键有唯一索引且主键冲突3.1.3、有主键有唯一索引且唯一索引冲突(有坑)3.1.4、有主键有唯一索引且与一条主键冲突与另一条唯…

计算机基础:二进制基础01,比特与字节

专栏导航 本节文章分别属于《Win32 学习笔记》和《MFC 学习笔记》两个专栏&#xff0c;故划分为两个专栏导航。读者可以自行选择前往哪个专栏。 &#xff08;一&#xff09;WIn32 专栏导航 上一篇&#xff1a;WIn32 笔记&#xff1a;本专栏课件 回到目录 下一篇&#xff1…