Three.js + AI:AI 算法生成 3D 萤火虫飞舞效果~

embedded/2024/11/26 1:25:52/

AI 驱动 3D 动画

大家好,我是石小石!随着 Web 技术的发展,Three.js 成为构建 3D 图形和动画的主流工具。与此同时,人工智能(AI)在图像处理、动作生成等领域表现出强大能力。将 AI 与 Three.js 结合,可以创造更加智能、动态和个性化的 3D 动画体验。

在传统的 3D 动画中,我们需要通过手动编码或关键帧技术定义每一帧动画。然而,AI 可以帮助我们:

  • 动作生成:通过神经网络生成复杂的人体动作(如走路、跳跃)。
  • 路径优化:使用 AI 规划摄像机路径或物体运动轨迹。
  • 实时反应:通过用户输入或传感器数据,动态生成响应动画。
  • 纹理与场景生成:利用生成对抗网络(GAN)实时生成纹理或场景内容。

本文将带你探讨如何使用 AI 算法驱动 3D 动画的生成,实现一个简单的案例:萤火虫飞舞效果。

前置知识及目标

核心目标

本文将借助Threejs生成一组粒子系统,用于模拟场景中的萤火虫,并借助Threejs的效果合成器模拟萤火虫的发光效果。最后,我们将通过 AI 算法(如噪声函数或简单的强化学习策略),生成一组动态轨迹,控制萤火虫在 3D 空间中的运动,呈现自然且流畅的效果。

必备知识

本文核心是探讨AI算法在Threejs中的应用,不会着重介绍Threejs的使用。如果你对Threejs不熟悉或者有遗忘,可以参考我的其他文章复习threejs的基础知识。

一个案例带你从零入门Three.js,深度好文!

threejs专栏

技术方案

项目及场景搭建

我们采用vite的vue脚手架搭建此项目,此处为语雀卡片,点击链接查看

先安装threejs的依赖,然后搭建最基本的Threejs场景

npm i three --save
// App.vue
<template><div ref="threeContainer" class="three-container"></div>
</template><script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import * as THREE from 'three';// 用于渲染的 DOM 容器
const threeContainer = ref(null); let scene, camera, renderer;// 初始化 Three.js 场景
const initScene = () => {// 创建场景scene = new THREE.Scene();// 创建摄像机camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 20, 1000);camera.position.set(0, 150, 500); // 设置相机位置camera.lookAt(0, 0, 0); // 看向原点// 创建渲染器renderer = new THREE.WebGLRenderer({ antialias: true });renderer.setSize(window.innerWidth, window.innerHeight);renderer.setPixelRatio(window.devicePixelRatio);threeContainer.value.appendChild(renderer.domElement);
};// 生命周期钩子
onMounted(() => {initScene();
});</script><style>
.three-container {position: fixed;left: 0;right: 0;bottom: 0;top: 0;overflow: hidden;
}
</style>

现在,我们就得到了一个没有任何内容的黑色场景。

创建粒子系统(萤火虫)

我们可以将萤火虫理解为一个个位置不一样的自发光球体,那么萤火虫的实现就非常简单。

let scene, camera, renderer, particles;const initScene = () => {// ...// 创建粒子系统(球体粒子-萤火虫)particles = new THREE.Group();const particlesCount = 200;for (let i = 0; i < particlesCount; i++) {const geometry = new THREE.SphereGeometry(5, 5, 5); // 创建球体const material = new THREE.MeshStandardMaterial({emissive: new THREE.Color("#CCFF00"), // 自发光颜色emissiveIntensity: 1.5, // 自发光强度});const particle = new THREE.Mesh(geometry, material);// 设置萤火虫的随机位置particle.position.x = Math.random() * 1000 - 500; // Xparticle.position.y = Math.random() * 1000 - 500; // Yparticle.position.z = Math.random() * 1000 - 500; // Zparticles.add(particle);}scene.add(particles);
};// 动画循环
const animate = () => {requestAnimationFrame(animate);renderer.render(scene, camera);
};// 生命周期钩子
onMounted(() => {initScene();animate();
});

上述代码中,particles用于储存粒子系统对象,方便后期更改参数。particlesCount代表的是萤火虫的数量,由于萤火虫的光一般是黄绿色,所以我们将其自发光颜色设置为"#CCFF00"。

实现萤火虫的发光效果

要实现萤火虫的发光效果,可以借助Threejs的EffectComposer效果合成器。EffectComposer的知识点可以参考我的其他文章:

threejs做特效:实现物体的发光效果-EffectComposer详解!

我们先引入效果合成器的一些threejs依赖项

import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';

然后创建一个处理发光效果的函数

let scene, camera, renderer, composer, particles;// 初始化 Three.js 场景
const initScene = () => {// ....initPostProcessing()
};// 初始化后期处理
const initPostProcessing = () => {composer = new EffectComposer(renderer);// 场景渲染 Passconst renderPass = new RenderPass(scene, camera);composer.addPass(renderPass);// 光晕效果 Passconst bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight),1.5, // 强度0.3, // 半径0.25 // 阈值);composer.addPass(bloomPass);
};// 动画循环
const animate = () => {animationFrameId = requestAnimationFrame(animate);// 渲染场景(使用后期处理)composer.render();
};// 生命周期钩子
onMounted(() => {initScene();animate();
});</script>

借助AI实现萤火虫的飞舞

要实现萤火虫的运动效果,我们手写运动轨迹太复杂了,我们可以借助AI算法来实现其无规则或者规则的运动轨迹。

Simplex Noise

**simplex-noise** 是一种改良的噪声算法,由 Perlin 噪声的发明者 Ken Perlin 提出。它适合用于生成平滑的伪随机数据,在许多场景中(如自然模拟、动画、程序化生成纹理等)都有应用。

官方传送门:https://www.npmjs.com/package/simplex-noise

萤火虫飞舞的效果应该满足以下特点:

  1. 运动是平滑的:不能有突兀的跳跃和不规则的抖动。
  2. 运动轨迹是自然的:看起来像随机飞舞,但又遵循某种自然规律。
  3. 可控性强:可以基于噪声的值控制位置、发光强度等。

Simplex Noise 的平滑、自然和伪随机特性,特别适合这种需求

simplex-noise 库中,可以用 createNoise2DcreateNoise3D 创建 2D 或 3D 噪声生成器,用于生成平滑的、随时间变化的伪随机数据。可以看出,用Simplex Noise的AI算法数据模拟萤火虫的飞舞效果是非常合适的。

使用simplex-noise模拟运动轨迹

更改萤火虫的运动轨迹其实非常容易,我们只需要用simplex-noise的AI数据,改变每一个萤火虫在场景中的x,y,z轴位置即可。它的核心伪代码应该如下:

// 更新粒子位置
const updateParticles = () => {particles.children.forEach((particle) => {const { x, y, z } = particle.position;// 使用噪声来更新粒子的位置particle.position.x += AI位置; // Xparticle.position.y += AI位置; // Yparticle.position.z += AI位置; // Z});
};

完成的代码如下

javascript">npm i simplex-noise
<template><div ref="threeContainer" class="three-container"></div>
</template><script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import * as THREE from 'three';
import { createNoise2D } from 'simplex-noise';
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';// 设置引用和变量
const threeContainer = ref(null); // 用于渲染的 DOM 容器let scene, camera, renderer, composer, particles;const noise = createNoise2D(); // 使用 SimplexNoise 创建噪声生成器
let time = 0; // 时间变量,用于驱动噪声的变化// 初始化 Three.js 场景
const initScene = () => {// 创建场景scene = new THREE.Scene();// 创建摄像机camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 20, 1000);camera.position.set(0, 150, 500); // 设置相机位置camera.lookAt(0, 0, 0); // 看向原点// 创建渲染器renderer = new THREE.WebGLRenderer({ antialias: true });renderer.setSize(window.innerWidth, window.innerHeight);renderer.setPixelRatio(window.devicePixelRatio);threeContainer.value.appendChild(renderer.domElement);// 创建粒子系统(球体粒子)particles = new THREE.Group();const particlesCount = 200;for (let i = 0; i < particlesCount; i++) {const geometry = new THREE.SphereGeometry(5, 5, 5); // 创建球体const material = new THREE.MeshStandardMaterial({emissive: new THREE.Color("#CCFF00"), // 自发光颜色emissiveIntensity: 1.5, // 自发光强度});const particle = new THREE.Mesh(geometry, material);particle.position.x = Math.random() * 1000 - 500; // Xparticle.position.y = Math.random() * 1000 - 500; // Yparticle.position.z = Math.random() * 1000 - 500; // Zparticles.add(particle);}scene.add(particles);// 初始化后期处理initPostProcessing();};// 初始化后期处理
const initPostProcessing = () => {composer = new EffectComposer(renderer);// 场景渲染 Passconst renderPass = new RenderPass(scene, camera);composer.addPass(renderPass);// 光晕效果 Passconst bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight),1.5, // 强度0.3, // 半径0.25 // 阈值);composer.addPass(bloomPass);
};// 更新粒子位置
const updateParticles = () => {particles.children.forEach((particle) => {const { x, y, z } = particle.position;// 使用噪声来更新粒子的位置particle.position.x += noise(x, time) * 3; // Xparticle.position.y += noise(y, time) * 3; // Yparticle.position.z += noise(z, time) * 3; // Z});time += 0.1; // 增加时间,控制噪声变化
};// 动画循环
const animate = () => {requestAnimationFrame(animate);updateParticles(); // 更新粒子的位置// 渲染场景(使用后期处理)composer.render();
};// 生命周期钩子
onMounted(() => {initScene();animate();
});</script><style>
.three-container {position: fixed;left: 0;right: 0;bottom: 0;top: 0;overflow: hidden;
}
</style>

上述代码中,Simplex Noise 通过 createNoise2D 生成的一个二维噪声生成器,作为驱动萤火虫位置变化的核心工具。它为每个粒子生成平滑且自然的随机运动,使粒子看起来像真实的萤火虫飞舞。

代码中的粒子更新函数:

javascript">particle.position.x += noise(x, time) * 3; // X
particle.position.y += noise(y, time) * 3; // Y
particle.position.z += noise(z, time) * 3; // Z
  • **noise(x, time)**
    • 生成一个基于粒子当前位置 x 和时间 time 的噪声值。
    • xtime 作为输入,确保噪声在空间和时间上的连续性。
  • **乘以一个因子 ****3**
    • 噪声的值通常在 [-1, 1],通过乘以 3 将其映射到更大的运动幅度。
  • 时间 **time** 控制动态变化
    • 时间的推进使得噪声生成的值不断变化,从而推动粒子的位置变化。

场景优化

为了让场景更加真实,我们可以给场景添加背景图片,模拟更好的夜晚效果

javascript">    // 创建场景scene = new THREE.Scene();new THREE.CubeTextureLoader().setPath("/sky/").load(["posx.jpg","negx.jpg","posy.jpg","negy.jpg","posz.jpg","negz.jpg",],(texture) => {scene.background = texture;});

注意,贴图需要自己准备

我们还可以添加轨道控制器,让场景支持旋转移动,以更丰富的视角观察萤火虫效果。

javascript">import { OrbitControls } from 'three/addons/controls/OrbitControls.js';// 初始化 Three.js 场景
const initScene = () => {// ....// 添加相机控制器const controls = new OrbitControls(camera, renderer.domElement);controls.enabled = true;controls.update();// 初始化后期处理initPostProcessing();
};

此外,我们也可以添加场景的尺寸动态变化、注销等等其他优化逻辑。

javascript">// 初始化 Three.js 场景
const initScene = () => {// ...// 处理窗口大小变化window.addEventListener('resize', onWindowResize);
};// 窗口大小变化时更新渲染器和相机
const onWindowResize = () => {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);composer.setSize(window.innerWidth, window.innerHeight);
};// 销毁场景
const destroyScene = () => {cancelAnimationFrame(animationFrameId);composer.dispose();renderer.dispose();window.removeEventListener('resize', onWindowResize);
};onUnmounted(() => {destroyScene();
});

可运行的完整代码

javascript"><template><div ref="threeContainer" class="three-container"></div>
</template><script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import * as THREE from 'three';
import { createNoise2D } from 'simplex-noise';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';// 设置引用和变量
const threeContainer = ref(null); // 用于渲染的 DOM 容器let scene, camera, renderer, composer, particles, animationFrameId;
const noise = createNoise2D(); // 使用 SimplexNoise 创建噪声生成器
let time = 0; // 时间变量,用于驱动噪声的变化// 初始化 Three.js 场景
const initScene = () => {// 创建场景scene = new THREE.Scene();// 创建摄像机camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 20, 1000);camera.position.set(0, 150, 500); // 设置相机位置camera.lookAt(0, 0, 0); // 看向原点// 创建渲染器renderer = new THREE.WebGLRenderer({ antialias: true });renderer.setSize(window.innerWidth, window.innerHeight);renderer.setPixelRatio(window.devicePixelRatio);threeContainer.value.appendChild(renderer.domElement);// 创建粒子系统(球体粒子)particles = new THREE.Group();const particlesCount = 200;for (let i = 0; i < particlesCount; i++) {const geometry = new THREE.SphereGeometry(5, 5, 5); // 创建球体const material = new THREE.MeshStandardMaterial({emissive: new THREE.Color("#CCFF00"), // 自发光颜色emissiveIntensity: 1.5, // 自发光强度// color: new THREE.Color(Math.random(), Math.random(), Math.random()), // 随机颜色});const particle = new THREE.Mesh(geometry, material);particle.position.x = Math.random() * 1000 - 500; // Xparticle.position.y = Math.random() * 1000 - 500; // Yparticle.position.z = Math.random() * 1000 - 500; // Zparticles.add(particle);}scene.add(particles);// 添加光源const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);scene.add(ambientLight);const pointLight = new THREE.PointLight(0xffffff, 1.5, 1000);pointLight.position.set(200, 300, 400);scene.add(pointLight);// 添加相机控制器const controls = new OrbitControls(camera, renderer.domElement);controls.enabled = true;controls.update();// 初始化后期处理initPostProcessing();// 处理窗口大小变化window.addEventListener('resize', onWindowResize);
};// 初始化后期处理
const initPostProcessing = () => {composer = new EffectComposer(renderer);// 场景渲染 Passconst renderPass = new RenderPass(scene, camera);composer.addPass(renderPass);// 光晕效果 Passconst bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight),1.5, // 强度0.3, // 半径0.25 // 阈值);composer.addPass(bloomPass);
};// 窗口大小变化时更新渲染器和相机
const onWindowResize = () => {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);composer.setSize(window.innerWidth, window.innerHeight);
};// 更新粒子位置
const updateParticles = () => {particles.children.forEach((particle) => {const { x, y, z } = particle.position;// 使用噪声来更新粒子的位置particle.position.x += noise(x, time) * 3; // Xparticle.position.y += noise(y, time) * 3; // Yparticle.position.z += noise(z, time) * 3; // Z});time += 0.1; // 增加时间,控制噪声变化
};// 动画循环
const animate = () => {animationFrameId = requestAnimationFrame(animate);updateParticles(); // 更新粒子的位置// 渲染场景(使用后期处理)composer.render();
};// 销毁场景
const destroyScene = () => {cancelAnimationFrame(animationFrameId);composer.dispose();renderer.dispose();window.removeEventListener('resize', onWindowResize);
};// 生命周期钩子
onMounted(() => {initScene();animate();
});onUnmounted(() => {destroyScene();
});
</script><style>
.three-container {position: fixed;left: 0;right: 0;bottom: 0;top: 0;overflow: hidden;
}
</style>

总结

本文使用 Three.js 创建一个 3D 场景,生成粒子(代表萤火虫),并使用 AI 算法(如 Simplex Noise噪声算法)来模拟萤火虫的动态运动轨迹。

它的核心步骤如下:

  1. 创建 3D 场景和渲染器:通过 Three.js 创建了一个基本的场景和渲染器,摄像机的位置设置使其可以从远处观察场景中的粒子。
  2. 生成粒子(萤火虫):使用 THREE.SphereGeometry 创建球体作为粒子,并设置自发光材质,模拟萤火虫的发光效果。
  3. 后期处理(发光效果):利用 Three.js 的 EffectComposerUnrealBloomPass 实现了萤火虫的光晕发光效果,使其看起来更自然。
  4. 使用 AI(Simplex Noise)模拟萤火虫飞舞:通过 simplex-noise 库生成的噪声数据,控制每个粒子的运动轨迹,产生平滑且自然的飞舞效果。
  5. 场景优化:添加了背景图片,模拟夜空效果;并通过 OrbitControls 实现了场景的交互式旋转和缩放。

如果本文对大家有帮助,欢迎点赞收藏~关注我,学习前端不迷路!


http://www.ppmy.cn/embedded/140518.html

相关文章

Java-08 深入浅出 MyBatis - 多对多模型 SqlMapConfig 与 Mapper 详细讲解测试

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 大数据篇正在更新&#xff01;https://blog.csdn.net/w776341482/category_12713819.html 目前已经更新到了&#xff1a; MyBatis&#xff…

Python学习——字符串操作方法

mystr “hello word goodbye” str “bye” Find函数&#xff1a;检测一个字符串中是否包含另一个字符串,找到了返回索引值&#xff0c;找不到了返回-1 print(mystr.find(str,0,len(mystr))) print(mystr.find(str,0,13)) index函数&#xff1a;检测一个字符串是否包含另一…

移动端自动化环境搭建_Android

adb的安装与使用 adb安装adb环境变量adb使用adb常用命令adb简介adb作用 adb安装 选择对应系统进入下载界面&#xff0c;选中版本下载即可&#xff1a; Windows版本&#xff1a;Windows Mac版本&#xff1a;Mac Linux版本&#xff1a;Linux 安装完成后&#xff0c;进行压缩&…

Linux上安装单机版Kafka

1、上传Kafka安装包至Linux并进行解压 kafka_2.12-1.1.1.tgz 链接&#xff1a;https://pan.baidu.com/s/1i41RKHlCbp0q2xQ1PEgT5g 提取码&#xff1a;vofa 将安装包解压 tar -zxvf kafka_2.12-1.1.1.tgz 2、修改kafka配置 vi config/server.properties 只修改绑定 IP &#…

深度优先搜索题目合集

本片为洛谷题目 纯手打&#xff0c;请您放心食用&#xff01; 目录 U121029 全排列(可重复) 题目描述 输入格式 输出格式 输入输出样例 题解 P1157 组合的输出 题目描述 输入格式 输出格式 输入输出样例 题解 P2404 自然数的拆分问题 题目描述 输入格式 输出格…

鸿蒙NEXT开发-Navigation组件导航

注意&#xff1a;博主有个鸿蒙专栏&#xff0c;里面从上到下有关于鸿蒙next的教学文档&#xff0c;大家感兴趣可以学习下 如果大家觉得博主文章写的好的话&#xff0c;可以点下关注&#xff0c;博主会一直更新鸿蒙next相关知识 专栏地址: https://blog.csdn.net/qq_56760790/…

Spark SQL操作

Spark SQL操作 文章目录 Spark SQL操作一、DataFrame的创建与保存1.前提操作2.数据准备3.创建4.保存DataFrame 二、DataFrame的操作1.printSchema2.show3.select4.filter5.groupBy(filed)6.sort(field) 三、临时表操作1.创建临时表2.通过临时表及SQL语句进行查询 四、从RDD转换…

Python 使用 OpenCV 将 MP4 转换为 GIF图

以下是使用 Python 和 OpenCV 将 MP4 转换为 GIF 的示例代码&#xff1a; python import cv2 import imageiodef mp4_to_gif(mp4_path, gif_path, fps10, start_timeNone, end_timeNone):"""将MP4视频转换为GIF动图。:param mp4_path: 输入MP4视频的路径。:pa…