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

embedded/2025/2/26 11:38:15/

大家好!我是 [数擎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/embedded/167253.html

相关文章

【Python】Python判断语句经典题(一)

Python入门——判断语句经典练习题例题&#xff08;一&#xff09;。题目来源&#xff1a;Acwing 目录 001、倍数 题目描述 AC代码 002、零食 题目描述 AC代码 003、加薪 题目描述 AC代码 004、DDD 题目描述 AC代码 005、游戏时间 题目描述 AC代码 006、简单排…

如何禁用uniapp,vue页面下拉刷新功能

在小程序开发中&#xff0c;enablePullDownRefresh 是一个常用的配置项&#xff0c;用来控制页面是否允许下拉刷新。但是&#xff0c;有时即使在 pages.json 中将其设置为 false&#xff0c;下拉刷新依然可能未被完全禁用。 1. enablePullDownRefresh: false 配置无效 enable…

kotlin 知识点三 扩展函数和运算符重载

大有用途的扩展函数 不少现代高级编程语言中有扩展函数这个概念&#xff0c;Java 却一直以来都不支持这个非常有用的功 能&#xff0c;这多少会让人有些遗憾。但值得高兴的是&#xff0c;Kotlin 对扩展函数进行了很好的支持&#xff0c;因此这个 知识点是我们无论如何都不能错…

宿主机的 root 是否等于 Docker 容器的 root?

在 Docker 容器化技术中&#xff0c;宿主机的 root 和 容器的 root 并不完全相同&#xff0c;尽管它们都称作 “root 用户”。这里需要明确的是&#xff0c;Docker 容器与宿主机之间存在隔离机制&#xff0c;容器内的 root 用户和宿主机的 root 用户有一些关键的区别。 1. 宿主…

MongoDB私人学习笔记

俗话说“好记性不如烂笔头”&#xff0c;编程的海洋如此的浩大&#xff0c;养成做笔记的习惯是成功的一步&#xff01; 此笔记主要是ZooKeeper3.4.9版本的笔记&#xff0c;并且笔记都是博主自己一字一字编写和记录&#xff0c;有错误的地方欢迎大家指正。 一、基础知识&#xf…

【Linux网络编程】 HTTP协议

目录 前言 URL 协议格式 常见的方法 状态码 cookie sessionid token 总结 HTTP协议是基于TCP的应用层协议&#xff0c;虽然我们说, 应用层协议是我们程序猿自己定的&#xff0c;但是自己定协议也是比较麻烦要解决两个问题&#xff1a; 序列化与反序列化数据粘包问题…

Java入门——猜测数字游戏

题目&#xff1a; 程序随机给出一个1-1000的整数&#xff0c;然后让你猜是什么数。你可以猜任何数字&#xff0c;游戏会提示过大或过小&#xff0c;从而缩小结果范围。经过几次猜测和提示&#xff0c;终于给出了答案。在游戏过程中&#xff0c;记录游戏结束时需要猜对的次数&a…

自动化反编译微信小程序工具-e0e1-wx

一、项目地址 https://github.com/eeeeeeeeee-code/e0e1-wx 二、简介 1.还在一个个反编译小程序吗&#xff1f;2.还在自己一个个注入hook吗&#xff1f;3.还在一个个查看找接口、查找泄露吗&#xff1f;现在有自动化辅助渗透脚本了&#xff0c;自动化辅助反编译、自动化注入…