react-three-fiber實(shí)現(xiàn)炫酷3D粒子效果首頁(yè)
背景
初學(xué)者怎么用react-three-fiber實(shí)現(xiàn)一個(gè)炫酷粒子效果的首頁(yè)
Three.js工作原理
場(chǎng)景(Scene)、相機(jī)(Camera)和渲染器(Renderer)構(gòu)成了web端展示3D模型的基本腳手架,HTML<canvas>
元素則可以讓我們?cè)陧?yè)面中看到3D模型。
場(chǎng)景(Scene)
場(chǎng)景是3D模型的載體,可以將場(chǎng)景視為所有 3D 對(duì)象都存在于其中的“小宇宙”。
import { Scene } from 'three'; const scene = new Scene();
場(chǎng)景擁有一個(gè)3D 笛卡爾坐標(biāo)系也是俗稱的右手坐標(biāo)系,它是我們?cè)趖hree.js 中處理可見對(duì)象時(shí)的主要參考框架。
場(chǎng)景的中心是點(diǎn)(0,0,0),也稱為坐標(biāo)原點(diǎn)。每當(dāng)我們創(chuàng)建一個(gè)新對(duì)象并放入場(chǎng)景中時(shí),默認(rèn)是放置在原點(diǎn)。
相機(jī)(Camera)
在場(chǎng)景搭建好之后,我們需要將3D場(chǎng)景轉(zhuǎn)化為人眼2D視角可見的東西,就需要引入相機(jī),進(jìn)行這種轉(zhuǎn)換有很多種相機(jī)模式。對(duì)我們來說,最重要的相機(jī)是透視相機(jī)(PerspectiveCamera),它是模擬人眼視角,從一個(gè)點(diǎn)到物體的視角成像,遵循近大遠(yuǎn)小的原理。
import { PerspectiveCamera } from 'three'; const fov = 75; // 攝像機(jī)視錐體垂直視野角度 const aspect = container.clientWidth / container.clientHeight; //攝像機(jī)視錐體長(zhǎng)寬比 const near = 0.1; // 攝像機(jī)視錐體近端面 const far = 100; // 攝像機(jī)視錐體遠(yuǎn)端面 const camera = new PerspectiveCamera(fov, aspect, near, far);
渲染器(renderer)
渲染器通過相機(jī)觀察3D場(chǎng)景,并將看到的東西繪制到<canvas>
上,我們把這個(gè)過程叫做渲染。
import { WebGLRenderer } from 'three'; const renderer = new WebGLRenderer();
雖然場(chǎng)景、相機(jī)和渲染器一起為我們提供了 three.js 的基本腳手架。但是我們無法在頁(yè)面上看到腳手架的存在。
網(wǎng)格對(duì)象(mesh)
網(wǎng)格是 3D 計(jì)算機(jī)圖形學(xué)中最常見的一種可見對(duì)象,用于顯示各種 3D 對(duì)象。還有其他種類的可見對(duì)象,例如線條、形狀、精靈和粒子等等。
網(wǎng)格一般包含幾何模型和材質(zhì),在創(chuàng)建網(wǎng)格之前,需要?jiǎng)?chuàng)建幾何模型和材質(zhì)。
import { Mesh } from 'three'; const mesh = new Mesh(geometry, material);
幾何模型形狀定義了網(wǎng)格的形狀,而材質(zhì)定義了網(wǎng)格的表面外觀。
基于以上介紹可以寫一個(gè)簡(jiǎn)單的three.js demo。
// 場(chǎng)景 const scene = new THREE.Scene(); // 相機(jī) const camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 10 ); camera.position.z = 1; // 模型 const geometry = new THREE.BoxGeometry( 0.2, 0.2, 0.2 ); const material = new THREE.MeshNormalMaterial(); const mesh = new THREE.Mesh( geometry, material ); scene.add( mesh ); const renderer = new THREE.WebGLRenderer( { antialias: true } ); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.setAnimationLoop( animation ); document.body.appendChild( renderer.domElement ); function animation( time ) { mesh.rotation.x = time / 2000; mesh.rotation.y = time / 1000; renderer.render( scene, camera ); }
react-three-fiber
react-three-fiber在它的github的readme.md上聲明了三點(diǎn)前提:
- 在 Threejs 中可以實(shí)現(xiàn)的都可以在react-three-fiber實(shí)現(xiàn)。
- react-three-fiber沒有額外的開銷,并且由于 Reacts 調(diào)度能力,它在規(guī)模上優(yōu)于 Threejs。
- react-three-fiber可以跟上three.js的頻繁功能更新,three.js版本添加、刪除或更改功能,無需依賴此庫(kù)的更新。
react-three-fiber的生態(tài)系統(tǒng)
畫布(Canvas)
Canvas 組件在幕后做了一些重要的設(shè)置工作:
- 它設(shè)置了Scene和Camera,這是渲染所需的基本構(gòu)建塊
- 它每幀渲染我們的場(chǎng)景,不需要傳統(tǒng)的渲染循環(huán)
import ReactDOM from 'react-dom' import { Canvas } from '@react-three/fiber' function App() { return ( <div id="canvas-container"> <Canvas /> </div>) } ReactDOM.render(<App />, document.getElementById('root'))
Canvas
響應(yīng)適應(yīng)父節(jié)點(diǎn),因此可以通過更改父節(jié)點(diǎn)的寬度和高度來控制它的大小,在本例中為 #canvas-container
。
上面的three.js demo 代碼用react-three-fiber實(shí)現(xiàn)如下:
import React from "react"; import { Canvas } from "react-three-fiber"; import "./styles.css"; export default function App() { return ( <div className="App"> <Canvas> <mesh> <boxBufferGeometry /> <meshPhongMaterial /> </mesh> <ambientLight args={[0xff0000]} intensity={0.1} /> <directionalLight position={[0, 0, 5]} intensity={0.5} /> </Canvas> </div> ); }
3D粒子模型構(gòu)建
首先看一段非常簡(jiǎn)單的粒子模型的three.js實(shí)現(xiàn)
// 創(chuàng)建一個(gè)球體幾何對(duì)象 var geometry = new THREE.SphereGeometry(100, 25, 25); // 創(chuàng)建一個(gè)點(diǎn)材質(zhì)對(duì)象 var material = new THREE.PointsMaterial({ color: 0x0000ff, //顏色 size: 3, //點(diǎn)渲染尺寸 }); // 點(diǎn)模型對(duì)象 參數(shù):幾何體 點(diǎn)材質(zhì) let point = new THREE.Points(geometry, material); // 網(wǎng)格模型添加到場(chǎng)景中 scene.add(point);
THREE.Points
- 用來創(chuàng)造點(diǎn)的類,也用來批量管理粒子,這個(gè)類的構(gòu)造函數(shù)可以接受兩個(gè)參數(shù),一個(gè)幾何體和一個(gè)材質(zhì),幾何體參數(shù)用來定義粒子的位置坐標(biāo),而材質(zhì)參數(shù)用來格式化粒子.
- 在threejs的粒子系統(tǒng)中,每個(gè)粒子其實(shí)是一張圖片或者一個(gè)canvas而不是3D的物體。
- 當(dāng)粒子量級(jí)非常大時(shí),可以用BufferGeometry來代替Geometry的頂點(diǎn),因?yàn)樗梢詫?shù)據(jù)存儲(chǔ)在緩沖區(qū)中,減少數(shù)據(jù)傳遞到GPU的成本,同時(shí)因?yàn)樵诰彌_區(qū),所以更適合靜態(tài)的物體。
實(shí)現(xiàn)思路
創(chuàng)建buffer幾何體
<bufferGeometry attach="geometry"></bufferGeometry>
幾何體定義好之后需要往里填充數(shù)據(jù)。假設(shè)我們的星系是由70000顆星星組成,星系的背景零零散散的有9000顆星星環(huán)繞。我們需要確定各個(gè)點(diǎn)的位置和顏色。背景的星星統(tǒng)一由藍(lán)色的點(diǎn),我們?cè)谥蟮牟馁|(zhì)里定義顏色,星系的星星使用顏色矩陣。
//背景 const bgStarsPositions = useMemo(() => { const bgStarsPositions = new Float32Array(parameters.stars * 3); // 背景星星的位置 for (let j = 0; j < parameters.stars; j++) { bgStarsPositions[j * 3 + 0] = (Math.random() - 0.5) * 20; bgStarsPositions[j * 3 + 1] = (Math.random() - 0.5) * 20; bgStarsPositions[j * 3 + 2] = (Math.random() - 0.5) * 20; } return bgStarsPositions; }, []);
// 星系 const [positions, colors] = useMemo(() => { const positions = new Float32Array(parameters.count * 3); const colors = new Float32Array(parameters.count * 3); const colorInside = new THREE.Color(parameters.insideColor); const colorOutside = new THREE.Color(parameters.outsideColor); for (let i = 0; i < parameters.count; i++) { // 位置 const x = Math.random() * parameters.radius; const branchAngle = ((i % parameters.branches) / parameters.branches) * 2 * Math.PI; const spinAngle = x * parameters.spin; const randomX = Math.pow(Math.random(), parameters.randomnessPower) * (Math.random() < 0.5 ? 1 : -1); const randomY = Math.pow(Math.random(), parameters.randomnessPower) * (Math.random() < 0.5 ? 1 : -1); const randomZ = Math.pow(Math.random(), parameters.randomnessPower) * (Math.random() < 0.5 ? 1 : -1); positions[i * 3] = Math.sin(branchAngle + spinAngle) * x + randomX; positions[i * 3 + 1] = randomY; positions[i * 3 + 2] = Math.cos(branchAngle + spinAngle) * x + randomZ; //顏色 const mixedColor = colorInside.clone(); mixedColor.lerp(colorOutside, x / parameters.radius); colors[i * 3 + 0] = mixedColor.r; colors[i * 3 + 1] = mixedColor.g; colors[i * 3 + 2] = mixedColor.b; } return [positions, colors]; }, []);
將位置和顏色賦值到buffer幾何體的attribute里
//星系 <bufferGeometry attach="geometry"> <bufferAttribute attachObject={['attributes', 'position']} count={70000} array={positions} itemSize={3}></bufferAttribute> <bufferAttribute attachObject={['attributes', 'color']} count={70000} array={colors} itemSize={3}></bufferAttribute> </bufferGeometry> //背景 <bufferGeometry attach="geometry"> <bufferAttribute attachObject={['attributes', 'position']} count={parameters.stars} array={bgStarsPositions} itemSize={3}></bufferAttribute> </bufferGeometry>
引入點(diǎn)材質(zhì)
const textureLoader = new THREE.TextureLoader(); const shape = textureLoader.load('1.png');
為每個(gè)點(diǎn),加載材質(zhì)。
// 背景 <points ref={bgstart}> <bufferGeometry attach="geometry"> <bufferAttribute attachObject={['attributes', 'position']} count={parameters.stars} array={bgStarsPositions} itemSize={3}></bufferAttribute> </bufferGeometry> <pointsMaterial attach="material" size={0.01} depthWrite={false} sizeAttenuation={true} blending={AdditiveBlending} color={'#1b3984'} transparent={true} alphaMap={shape}></pointsMaterial> </points>
// 星系 <points ref={points}> <bufferGeometry attach="geometry"> <bufferAttribute attachObject={['attributes', 'position']} count={parameters.count} array={positions} itemSize={3}></bufferAttribute> <bufferAttribute attachObject={['attributes', 'color']} count={parameters.count} array={colors} itemSize={3}></bufferAttribute> </bufferGeometry> <pointsMaterial attach="material" size={0.01} depthWrite={false} sizeAttenuation={true} blending={AdditiveBlending} vertexColors={true} transparent={true} alphaMap={shape}></pointsMaterial> </points>
//背景 const bgstart = useRef<any>(); useFrame(state => { const elapsedTime = state.clock.elapsedTime; bgstart.current.rotation.y = -elapsedTime * 0.05; });
//星系 const points = useRef<any>(); useFrame(state => { const elapsedTime = state.clock.elapsedTime; points.current.rotation.y = elapsedTime * 0.3; });
小知識(shí)點(diǎn):useFrame這個(gè)鉤子允許你在每個(gè)渲染的幀上執(zhí)行代碼,比如運(yùn)行效果、更新控件等等。你會(huì)收到狀態(tài)和時(shí)鐘增量。您的回調(diào)函數(shù)將在渲染幀之前被調(diào)用。當(dāng)組件卸載時(shí),它會(huì)自動(dòng)從渲染循環(huán)中取消訂閱。
最后呈現(xiàn)結(jié)果
到此這篇關(guān)于react-three-fiber實(shí)現(xiàn)炫酷3D粒子效果首頁(yè)的文章就介紹到這了,更多相關(guān)react 3D粒子內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
React useMemo與useCallabck有什么區(qū)別
useCallback和useMemo是一樣的東西,只是入?yún)⒂兴煌?,useCallback緩存的是回調(diào)函數(shù),如果依賴項(xiàng)沒有更新,就會(huì)使用緩存的回調(diào)函數(shù);useMemo緩存的是回調(diào)函數(shù)的return,如果依賴項(xiàng)沒有更新,就會(huì)使用緩存的return2022-12-12react項(xiàng)目中@路徑簡(jiǎn)單配置指南
這篇文章主要給大家介紹了關(guān)于react項(xiàng)目中@路徑簡(jiǎn)單配置的相關(guān)資料,文中還介紹了React配置@路徑別名的方法,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09使用React Native創(chuàng)建以太坊錢包實(shí)現(xiàn)轉(zhuǎn)賬等功能
這篇文章主要介紹了使用React Native創(chuàng)建以太坊錢包,實(shí)現(xiàn)轉(zhuǎn)賬等功能,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-07-07React組件中監(jiān)聽函數(shù)獲取不到最新的state問題
這篇文章主要介紹了React組件中監(jiān)聽函數(shù)獲取不到最新的state問題問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01React實(shí)現(xiàn)基于Antd密碼強(qiáng)度校驗(yàn)組件示例詳解
這篇文章主要為大家介紹了React實(shí)現(xiàn)基于Antd密碼強(qiáng)度校驗(yàn)組件示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01React中實(shí)現(xiàn)編輯框自動(dòng)獲取焦點(diǎn)與失焦更新功能
在React應(yīng)用中,編輯框的焦點(diǎn)控制和數(shù)據(jù)回填是一個(gè)常見需求,本文將介紹如何使用useRef和useEffect鉤子,在組件中實(shí)現(xiàn)輸入框自動(dòng)獲取焦點(diǎn)及失焦后更新數(shù)據(jù)的功能,文中通過代碼示例給大家講解的非常詳細(xì),需要的朋友可以參考下2024-01-01react中useState使用:如何實(shí)現(xiàn)在當(dāng)前表格直接更改數(shù)據(jù)
這篇文章主要介紹了react中useState的使用:如何實(shí)現(xiàn)在當(dāng)前表格直接更改數(shù)據(jù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08解決React報(bào)錯(cuò)Property?'value'?does?not?exist?on?
這篇文章主要為大家介紹了React報(bào)錯(cuò)Property?'value'?does?not?exist?on?type?EventTarget的解決方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12