【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.pswp.cn/bicheng/80944.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/80944.shtml
英文地址,請注明出處:http://en.pswp.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…