稀土掘金技术社区 2024年11月27日
Three.js + AI:AI 算法生成 3D 萤火虫飞舞效果~
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文探讨将AI与Three.js结合,实现智能、动态和个性化的3D动画体验,以创建萤火虫飞舞效果为例,涵盖项目搭建、粒子系统创建、发光效果实现及借助AI实现飞舞等内容。

🎈Three.js与AI结合,实现3D动画体验,如动作生成、路径优化等

💡创建粒子系统模拟萤火虫,设置其位置、数量和自发光属性

✨借助EffectComposer实现萤火虫发光效果,引入相关依赖项

🦋使用Simplex Noise的AI算法模拟萤火虫飞舞,改变粒子位置

🌌进行场景优化,如添加背景图片、轨道控制器等

原创 石小石Orz 2024-11-27 08:30 重庆

点击关注公众号,“技术干货”及时达!

点击关注公众号,“技术干货” 及时达!

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

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

本文将带你探讨如何使用 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, 201000);
    camera.position.set(0150500); // 设置相机位置
    camera.lookAt(000); // 看向原点
    // 创建渲染器
    renderer = new THREE.WebGLRenderer({ antialiastrue });
    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(555); // 创建球体
      const material = new THREE.MeshStandardMaterial({
          emissivenew THREE.Color("#CCFF00"), // 自发光颜色
          emissiveIntensity1.5// 自发光强度      });
      const particle = new THREE.Mesh(geometry, material);
      // 设置萤火虫的随机位置
      particle.position.x = Math.random() * 1000 - 500// X
      particle.position.y = Math.random() * 1000 - 500// Y
      particle.position.z = Math.random() * 1000 - 500// Z        particles.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);
    // 场景渲染 Pass
    const renderPass = new RenderPass(scene, camera);    composer.addPass(renderPass);
    // 光晕效果 Pass
    const 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

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

    「运动是平滑的」:不能有突兀的跳跃和不规则的抖动。

    「运动轨迹是自然的」:看起来像随机飞舞,但又遵循某种自然规律。

    「可控性强」:可以基于噪声的值控制位置、发光强度等。

「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位置; // X
        particle.position.y += AI位置; // Y
        particle.position.z += AI位置; // Z    });};

完成的代码如下

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; // X        particle.position.y = Math.random() * 1000 - 500; // Y        particle.position.z = Math.random() * 1000 - 500; // Z        particles.add(particle);    }    scene.add(particles);    // 初始化后期处理    initPostProcessing();};// 初始化后期处理const initPostProcessing = () => {    composer = new EffectComposer(renderer);    // 场景渲染 Pass    const renderPass = new RenderPass(scene, camera);    composer.addPass(renderPass);    // 光晕效果 Pass    const 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; // X        particle.position.y += noise(y, time) * 3; // Y        particle.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 生成的一个二维噪声生成器,作为驱动萤火虫位置变化的核心工具。它为每个粒子生成平滑且自然的随机运动,使粒子看起来像真实的萤火虫飞舞。

代码中的粒子更新函数:

particle.position.x += noise(x, time) * 3// X
particle.position.y += noise(y, time) * 3// Y
particle.position.z += noise(z, time) * 3// Z

场景优化

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

    // 创建场景
    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;            }        );

注意,贴图需要自己准备

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

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();};

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

// 初始化 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();});
<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; // X        particle.position.y = Math.random() * 1000 - 500; // Y        particle.position.z = Math.random() * 1000 - 500; // Z        particles.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);    // 场景渲染 Pass    const renderPass = new RenderPass(scene, camera);    composer.addPass(renderPass);    // 光晕效果 Pass    const 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; // X        particle.position.y += noise(y, time) * 3; // Y        particle.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噪声算法)来模拟萤火虫的动态运动轨迹。

它的核心步骤如下:

    「创建 3D 场景和渲染器」:通过 Three.js 创建了一个基本的场景和渲染器,摄像机的位置设置使其可以从远处观察场景中的粒子。

    「生成粒子(萤火虫)」 :使用 THREE.SphereGeometry 创建球体作为粒子,并设置自发光材质,模拟萤火虫的发光效果。

    「后期处理(发光效果)」 :利用 Three.js 的 EffectComposerUnrealBloomPass 实现了萤火虫的光晕发光效果,使其看起来更自然。

    「使用 AI(Simplex Noise)模拟萤火虫飞舞」:通过 simplex-noise 库生成的噪声数据,控制每个粒子的运动轨迹,产生平滑且自然的飞舞效果。

    「场景优化」:添加了背景图片,模拟夜空效果;并通过 OrbitControls 实现了场景的交互式旋转和缩放。

如果本文对大家有帮助,欢迎「点赞收藏」~

关注我,学习前端不迷路!

点击关注公众号,“技术干货” 及时达!

阅读原文

跳转微信打开

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

Three.js AI算法 萤火虫飞舞 3D动画 场景优化
相关文章