【Three.js基础学习】36.particles-morphing-shader

前言

通过着色器如何实现粒子之间动态切换

一、代码

script.js

import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'
import GUI from 'lil-gui'
import gsap from 'gsap'
import particlesVertexShader from './shaders/particles/vertex.glsl'
import particlesFragmentShader from './shaders/particles/fragment.glsl'/*** Base*/
// Debug
const gui = new GUI({ width: 340 })
const debugObject = {}// Canvas
const canvas = document.querySelector('canvas.webgl')// Scene
const scene = new THREE.Scene()// Loaders
const dracoLoader = new DRACOLoader()
dracoLoader.setDecoderPath('./draco/')
const gltfLoader = new GLTFLoader()
gltfLoader.setDRACOLoader(dracoLoader)/*** Sizes*/
const sizes = {width: window.innerWidth,height: window.innerHeight,pixelRatio: Math.min(window.devicePixelRatio, 2)
}window.addEventListener('resize', () =>
{// Update sizessizes.width = window.innerWidthsizes.height = window.innerHeightsizes.pixelRatio = Math.min(window.devicePixelRatio, 2)// Materialsif(particles)particles.material.uniforms.uResolution.value.set(sizes.width * sizes.pixelRatio, sizes.height * sizes.pixelRatio)// Update cameracamera.aspect = sizes.width / sizes.heightcamera.updateProjectionMatrix()// Update rendererrenderer.setSize(sizes.width, sizes.height)renderer.setPixelRatio(sizes.pixelRatio)
})/*** Camera*/
// Base camera
const camera = new THREE.PerspectiveCamera(35, sizes.width / sizes.height, 0.1, 100)
camera.position.set(0, 0, 8 * 2)
scene.add(camera)// Controls
const controls = new OrbitControls(camera, canvas)
controls.enableDamping = true/*** Renderer*/
const renderer = new THREE.WebGLRenderer({canvas: canvas,antialias: true,
})renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(sizes.pixelRatio)debugObject.clearColor = '#160920'
gui.addColor(debugObject, 'clearColor').onChange(() => { renderer.setClearColor(debugObject.clearColor) })
renderer.setClearColor(debugObject.clearColor)/*** Particles*/
let particles = null// Load models
gltfLoader.load('./models.glb',(gltf)=>{particles = {}particles.index = 0;// Positions //需要知道为什么要位置,因为要拿到位置应用const positions = gltf.scene.children.map(child=>child.geometry.attributes.position)particles.maxCount = 0for(const position of positions){if(position.count > particles.maxCount){particles.maxCount = position.count // 获取最大计数}}particles.positions = []for(const position of positions){const originalArray = position.arrayconst newArray = new Float32Array(particles.maxCount * 3)for(let i=0;i<particles.maxCount;i++){const i3 = i * 3if(i3 < originalArray.length){newArray[i3 + 0] = originalArray[i3 + 0]newArray[i3 + 1] = originalArray[i3 + 1]newArray[i3 + 2] = originalArray[i3 + 2]}else{// 这里如果设置为0 可以看到集合体粒子 中心有白点const randomIndex = Math.floor(position.count * Math.random()) * 3newArray[i3 + 0] = originalArray[randomIndex + 0]newArray[i3 + 1] = originalArray[randomIndex + 1]newArray[i3 + 2] = originalArray[randomIndex + 2]}}particles.positions.push(new THREE.Float32BufferAttribute(newArray,3)) // 告诉gpu 是3 * 3取值}// Geometryconst sizesArray = new Float32Array(particles.maxCount) // 设置粒子的大小for(let i=0;i<particles.maxCount;i++){sizesArray[i] = Math.random()}particles.geometry = new THREE.BufferGeometry()particles.geometry.setAttribute('position',particles.positions[particles.index]) // 本次的模型particles.geometry.setAttribute('aPositionTarget',particles.positions[3]) // 目标到达的模型 particles.geometry.setAttribute('aSize',new THREE.BufferAttribute(sizesArray,1)) // 目标到达的模型 // particles.geometry.setIndex(null) // 发现圆形集合体 平面很亮 因为每个点都有六个顶点组成(多个三角形组成平面,平面交点由六个点组成一个顶点),应该停用// Materialparticles.colorA = '#ff7300'particles.colorB = '#0091ff'particles.material = new THREE.ShaderMaterial({vertexShader: particlesVertexShader,fragmentShader: particlesFragmentShader,uniforms:{uSize: new THREE.Uniform(0.4),uResolution: new THREE.Uniform(new THREE.Vector2(sizes.width * sizes.pixelRatio, sizes.height * sizes.pixelRatio)),uProgress:new THREE.Uniform(0),// 为什么这个从0-1就改变了模型,其实更改了particles中模型的顶点位置 变成aPositionTargetuColorA:new THREE.Uniform(new THREE.Color(particles.colorA)),uColorB:new THREE.Uniform(new THREE.Color(particles.colorB)),},blending: THREE.AdditiveBlending,depthWrite:false , // 深度缓冲器})// Pointsparticles.points = new THREE.Points(particles.geometry, particles.material)particles.points.frustumCulled = false // 判断边界scene.add(particles.points)/* 将粒子转换为不同形状需要3个步骤将位置属性设置为原始几何体将 aPositionTarget 属性设置为目标几何体将uProgress从0动画到1*///  Methods  保存粒子的方法particles.morph = (index) =>{ // 能够得到正确的索引// Update attributesparticles.geometry.attributes.position = particles.positions[particles.index] // particles.geometry.attributes.aPositionTarget = particles.positions[index]// Animate uProgressgsap.fromTo(particles.material.uniforms.uProgress,{value:0},{value:1, duration:3, ease:'linear'},)// save indexparticles.index = index}particles.morph0 = () => { particles.morph(0) }particles.morph1 = () => { particles.morph(1) }particles.morph2 = () => { particles.morph(2) }particles.morph3 = () => { particles.morph(3) }// Tweaks/* 能否做一些改进,因为每一个都是从头开始做线性运动然后结束这一次,我们将使用simplex Ncise。非常相似看起来更自然不那么网格状性能更高(特别是在较高维度上)同样由肯·佩林创作1.在不同时刻开始// https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83*/gui.addColor(particles,'colorA').onChange(()=>{particles.material.uniforms.uColorA.value.set(particles.colorA)})gui.addColor(particles,'colorB').onChange(()=>{particles.material.uniforms.uColorB.value.set(particles.colorB)})gui.add(particles.material.uniforms.uProgress,'value').min(0).max(1).step(0.001).name('uProgress').listen() // 监听变化gui.add(particles,'morph0')gui.add(particles,'morph1')gui.add(particles,'morph2')gui.add(particles,'morph3')})/*** Animate*/
const tick = () =>
{// Update controlscontrols.update()// Render normal scenerenderer.render(scene, camera)// Call tick again on the next framewindow.requestAnimationFrame(tick)
}tick()

fragment.glsl

varying vec3 vColor;void main()
{vec2 uv = gl_PointCoord;float distanceToCenter = length(uv - 0.5);float alpha = 0.05 / distanceToCenter - 0.1; // 为什么减去0.1 ,因为到正方形的边界没有变成0 ,而是无限接近gl_FragColor = vec4(vColor, alpha);#include <tonemapping_fragment>#include <colorspace_fragment>
}

vertex.glsl

uniform vec2 uResolution;
uniform float uSize;
uniform float uProgress;
uniform vec3 uColorA; 
uniform vec3 uColorB; varying vec3 vColor;attribute vec3  aPositionTarget;
attribute float  aSize;// 引入噪音 一种表现形式
#include ../includes/simplexNoise3d.glslvoid main()
{// Mixed positionfloat nosieOrigin = simplexNoise3d(position * 0.2);float nosieTarget = simplexNoise3d(aPositionTarget * 0.2);float nosie = mix(nosieOrigin,nosieTarget,uProgress);nosie = smoothstep(-1.0,1.0,nosie);// 设置噪音 0 - 1 float duration = 0.4; // 设置延长float delay = (1.0 - duration) * nosie; // 延长事件最大float end = delay + duration; // 结束float progress = smoothstep(delay, end, uProgress);vec3 mixedPosition = mix(position,aPositionTarget,progress);// Final positionvec4 modelPosition = modelMatrix * vec4(mixedPosition, 1.0);vec4 viewPosition = viewMatrix * modelPosition;vec4 projectedPosition = projectionMatrix * viewPosition;gl_Position = projectedPosition;// Point sizegl_PointSize = aSize * uSize * uResolution.y;gl_PointSize *= (1.0 / - viewPosition.z);// varyingvColor = mix(uColorA,uColorB,nosie);
}

index.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Particles Morphing</title><link rel="stylesheet" href="./style.css">
</head>
<body><canvas class="webgl"></canvas><script type="module" src="./script.js"></script>
</body>
</html>

style.css

*
{margin: 0;padding: 0;
}html,
body
{overflow: hidden;
}.webgl
{position: fixed;top: 0;left: 0;outline: none;
}

simplexNoise3d.glsl (噪音)

//	Simplex 3D Noise 
//	by Ian McEwan, Ashima Arts
//
vec4 permute(vec4 x){ return mod(((x*34.0)+1.0)*x, 289.0); }
vec4 taylorInvSqrt(vec4 r){ return 1.79284291400159 - 0.85373472095314 * r; }float simplexNoise3d(vec3 v)
{const vec2  C = vec2(1.0/6.0, 1.0/3.0) ;const vec4  D = vec4(0.0, 0.5, 1.0, 2.0);// First cornervec3 i  = floor(v + dot(v, C.yyy) );vec3 x0 =   v - i + dot(i, C.xxx) ;// Other cornersvec3 g = step(x0.yzx, x0.xyz);vec3 l = 1.0 - g;vec3 i1 = min( g.xyz, l.zxy );vec3 i2 = max( g.xyz, l.zxy );//  x0 = x0 - 0. + 0.0 * C vec3 x1 = x0 - i1 + 1.0 * C.xxx;vec3 x2 = x0 - i2 + 2.0 * C.xxx;vec3 x3 = x0 - 1. + 3.0 * C.xxx;// Permutationsi = mod(i, 289.0 ); vec4 p = permute( permute( permute( i.z + vec4(0.0, i1.z, i2.z, 1.0 )) + i.y + vec4(0.0, i1.y, i2.y, 1.0 ))  + i.x + vec4(0.0, i1.x, i2.x, 1.0 ));// Gradients// ( N*N points uniformly over a square, mapped onto an octahedron.)float n_ = 1.0/7.0; // N=7vec3  ns = n_ * D.wyz - D.xzx;vec4 j = p - 49.0 * floor(p * ns.z *ns.z);  //  mod(p,N*N)vec4 x_ = floor(j * ns.z);vec4 y_ = floor(j - 7.0 * x_ );    // mod(j,N)vec4 x = x_ *ns.x + ns.yyyy;vec4 y = y_ *ns.x + ns.yyyy;vec4 h = 1.0 - abs(x) - abs(y);vec4 b0 = vec4( x.xy, y.xy );vec4 b1 = vec4( x.zw, y.zw );vec4 s0 = floor(b0)*2.0 + 1.0;vec4 s1 = floor(b1)*2.0 + 1.0;vec4 sh = -step(h, vec4(0.0));vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;vec3 p0 = vec3(a0.xy,h.x);vec3 p1 = vec3(a0.zw,h.y);vec3 p2 = vec3(a1.xy,h.z);vec3 p3 = vec3(a1.zw,h.w);// Normalise gradientsvec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));p0 *= norm.x;p1 *= norm.y;p2 *= norm.z;p3 *= norm.w;// Mix final noise valuevec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);m = m * m;return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3) ) );
}

开始结束的线性运动

二,效果

three.js 关于着色器粒子应用


总结

属于着色器应该的一个小案例

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/80944.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【PostgreSQL数据分析实战:从数据清洗到可视化全流程】附录-D. 扩展插件列表(PostGIS/PostgREST等)

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 附录D. PostgreSQL扩展插件速查表一、插件分类速查表二、核心插件详解三、安装与配置指南四、应用场景模板五、版本兼容性说明六、维护与优化建议七、官方资源与工具八、附录…

【Linux】冯诺依曼体系结构和操作系统的理解

目录 冯诺依曼体系结构一个例子来深入理解 初识操作系统操作系统的作用设计操作系统的目的操作系统之上和之下分别有啥 管理的精髓&#xff0c;先描述&#xff0c;再组织 冯诺依曼体系结构 我们知道&#xff0c;计算机这个东西发明出来就是帮助人们快速解决问题的。那如果我们想…

kotlin @JvmStatic注解的作用和使用场景

1. JvmStatic 的作用 JvmStatic 是 Kotlin 提供的一个注解&#xff0c;用于在 JVM 上将伴生对象&#xff08;companion object&#xff09;中的方法或属性暴露为 Java 静态方法或字段。 作用对象&#xff1a;只能用在 companion object 中的函数或属性。效果&#xff1a; 在 …

Redis实现-优惠卷秒杀(基础版本)

(一)全局唯一ID 一、全局ID生成器 可以看到在优惠卷订单表中的主键id并没有设置Auto increment自增长 假如未来订单量达到数亿单&#xff0c;单表无法保存如此多数据&#xff0c;就需要对其进行分表存储(分布式)。假如每张表都采用自增长&#xff0c;各自从1开始自增&#xf…

c++STL——哈希表封装:实现高效unordered_map与unordered_set

文章目录 用哈希表封装unordered_map和unordered_set改进底层框架迭代器实现实现思路迭代器框架迭代器重载operator哈希表中获取迭代器位置 哈希表的默认成员函数修改后的哈希表的代码封装至上层容器 用哈希表封装unordered_map和unordered_set 在前面我们已经学过如何实现哈希…

虹科应用 | 探索PCAN卡与医疗机器人的革命性结合

随着医疗技术的不断进步&#xff0c;医疗机器人在提高手术精度、减少感染风险以及提升患者护理质量方面发挥着越来越重要的作用。医疗机器人的精确操作依赖于稳定且高效的数据通信系统&#xff0c;虹科提供的PCAN四通道mini PCIe转CAN FD卡&#xff0c;正是为了满足这一需求而设…

Yolov8的详解与实战-深度学习目标检测

Yolov8的详解与实战- 文章目录 摘要 模型详解 C2F模块 Loss head部分 模型实战 训练COCO数据集 下载数据集 COCO转yolo格式数据集&#xff08;适用V4&#xff0c;V5&#xff0c;V6&#xff0c;V7&#xff0c;V8&#xff09; 配置yolov8环境 训练 测试 训练自定义数据集 Labelme…

scons user 3.1.2

前言 感谢您抽出时间阅读有关 SCons 的内容。SCons 是一款下一代软件构建工具&#xff0c;或者称为 make 工具&#xff0c;即一种用于构建软件&#xff08;或其他文件&#xff09;并在底层输入文件发生更改时使已构建的软件保持最新状态的软件实用程序。 SCons 最显著的特点是…

Java的多线程笔记

创建一个线程的方法有多种&#xff0c;比如可以继承Thread类或者实现Runnable接口&#xff0c;结论是实现Runnable接口比前者更加优越。 二者代码对比 Java 不支持多继承&#xff0c;如果你继承了 Thread 类&#xff0c;就不能再继承其他类&#xff0c;实现 Runnable 接口后&am…

PDF Base64格式字符串转换为PDF文件临时文件

需求描述&#xff1a; 在对接电子病历系统与河北CA&#xff0c;进行免密文件签章的时候&#xff0c;两者系统入参不同&#xff0c;前者是pdf文件&#xff0c;base64格式&#xff1b;后者要求File类型的PDF文件。 在业务中间层开发时&#xff0c;则需要接收EMR侧提供的base64格式…

代码随想录训练营第二十三天| 572.另一颗树的子树 104.二叉树的最大深度 559.N叉树的最大深度 111.二叉树的最小深度

572.另一颗树的子树&#xff1a; 状态&#xff1a;已做出 思路&#xff1a; 这道题目当时第一时间不是想到利用100.相同的树思路来解决&#xff0c;而是先想到了使用kmp&#xff0c;不过这个题目官方题解确实是有kmp解法的&#xff0c;我使用的暴力解法&#xff0c;kmp的大致思…

【RabbitMq C++】消息队列组件

RabbitMq 消息队列组件 1. RabbitMq介绍2. 安装RabbitMQ3. 安装 RabbitMQ 的 C客户端库4. AMQP-CPP 库的简单使用4.1 使用4.1.1 TCP 模式4.1.2 扩展模式 4.2 常用类与接口介绍4.2.1 Channel4.3.2 ev 5. RabbitMQ样例编写5.1 发布消息5.2 订阅消息 1. RabbitMq介绍 RabbitMq - …

鸿蒙NEXT开发动画案例8

1.创建空白项目 2.Page文件夹下面新建Spin.ets文件&#xff0c;代码如下&#xff1a; /*** SpinKit动画组件 (重构版)* author: CSDN-鸿蒙布道师* since: 2025/05/14*/interface AnimationGroup {indexes: number[];delay: number; }ComponentV2 export struct SpinEight {Re…

MySQL全局优化

目录 1 硬件层面优化 1.1 CPU优化 1.2 内存优化 1.3 存储优化 1.4 网络优化 2 系统配置优化 2.1 操作系统配置 2.2 MySQL服务配置 3 库表结构优化 4 SQL及索引优化 mysql可以从四个层面考虑优化&#xff0c;分别是 硬件系统配置库表结构SQL及索引 从成本和优化效果来看&#xf…

vue和springboot交互数据,使用axios【跨域问题】

vue和springboot交互数据&#xff0c;使用axios【跨域问题】 提示&#xff1a;帮帮志会陆续更新非常多的IT技术知识&#xff0c;希望分享的内容对您有用。本章分享的是node.js和vue的使用。前后每一小节的内容是存在的有&#xff1a;学习and理解的关联性。【帮帮志系列文章】&…

FFMPEG 与 mp4

1. FFmpeg 中的 start_time 与 time_base start_time 流的起始时间戳&#xff08;单位&#xff1a;time_base&#xff09;&#xff0c;表示第一帧的呈现时间&#xff08;Presentation Time&#xff09;。通常用于同步多个流&#xff08;如音频和视频&#xff09;。 time_base …

AI世界的崩塌:当人类思考枯竭引发数据生态链断裂

AI世界的崩塌&#xff1a;当人类思考枯竭引发数据生态链断裂 ——论过度依赖AI创作对技术进化的反噬 一、数据生态的恶性循环&#xff1a;AI的“自噬危机” 当前AI模型的训练依赖于人类创造的原始数据——书籍、论文、艺术作品、社交媒体动态等。据统计&#xff0c;2025年全球…

C++【STL】(2)string

C【STL】string用法扩展 1. assign&#xff1a;为字符串赋新值 用于替换字符串内容&#xff0c;支持多种参数形式。 常用形式&#xff1a; // 用另一个字符串赋值 str.assign("Hello World");// 用另一个字符串的子串&#xff08;从第6个字符开始&#xff0c;取5…

树莓派4基于Debian GNU/Linux 12 (Bookworm)开启VNC,使用MobaXterm连接VNC出现黑屏/灰屏问题

1. 开启树莓派的VNC服务 启用VNC服务&#xff1a;通过raspi-config开启 # 1. 通过 raspi-config 工具开启 sudo raspi-config选择 Interface Options → VNC → Yes退出时会自动启动服务 检查服务状态&#xff1a; sudo systemctl status vncserver-x11-serviced正常输出应显示…

MongoDB使用x.509证书认证

文章目录 自定义证书生成CA证书生成服务器之间的证书生成集群证书生成用户证书 MongoDB配置java使用x.509证书连接MongoDBMongoShell使用证书连接 8.0版本的mongodb开启复制集&#xff0c;配置证书认证 自定义证书 生成CA证书 生成ca私钥&#xff1a; openssl genrsa -out ca…