react-three-fiber實現炫酷3D粒子效果首頁
背景
初學者怎麼用react-three-fiber實現一個炫酷粒子效果的首頁
Three.js工作原理
場景(Scene)、相機(Camera)和渲染器(Renderer)構成瞭web端展示3D模型的基本腳手架,HTML<canvas>
元素則可以讓我們在頁面中看到3D模型。
場景(Scene)
場景是3D模型的載體,可以將場景視為所有 3D 對象都存在於其中的“小宇宙”。
import { Scene } from 'three'; const scene = new Scene();
場景擁有一個3D 笛卡爾坐標系也是俗稱的右手坐標系,它是我們在three.js 中處理可見對象時的主要參考框架。
場景的中心是點(0,0,0),也稱為坐標原點。每當我們創建一個新對象並放入場景中時,默認是放置在原點。
相機(Camera)
在場景搭建好之後,我們需要將3D場景轉化為人眼2D視角可見的東西,就需要引入相機,進行這種轉換有很多種相機模式。對我們來說,最重要的相機是透視相機(PerspectiveCamera),它是模擬人眼視角,從一個點到物體的視角成像,遵循近大遠小的原理。
import { PerspectiveCamera } from 'three'; const fov = 75; // 攝像機視錐體垂直視野角度 const aspect = container.clientWidth / container.clientHeight; //攝像機視錐體長寬比 const near = 0.1; // 攝像機視錐體近端面 const far = 100; // 攝像機視錐體遠端面 const camera = new PerspectiveCamera(fov, aspect, near, far);
渲染器(renderer)
渲染器通過相機觀察3D場景,並將看到的東西繪制到<canvas>
上,我們把這個過程叫做渲染。
import { WebGLRenderer } from 'three'; const renderer = new WebGLRenderer();
雖然場景、相機和渲染器一起為我們提供瞭 three.js 的基本腳手架。但是我們無法在頁面上看到腳手架的存在。
網格對象(mesh)
網格是 3D 計算機圖形學中最常見的一種可見對象,用於顯示各種 3D 對象。還有其他種類的可見對象,例如線條、形狀、精靈和粒子等等。
網格一般包含幾何模型和材質,在創建網格之前,需要創建幾何模型和材質。
import { Mesh } from 'three'; const mesh = new Mesh(geometry, material);
幾何模型形狀定義瞭網格的形狀,而材質定義瞭網格的表面外觀。
基於以上介紹可以寫一個簡單的three.js demo。
// 場景 const scene = new THREE.Scene(); // 相機 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上聲明瞭三點前提:
- 在 Threejs 中可以實現的都可以在react-three-fiber實現。
- react-three-fiber沒有額外的開銷,並且由於 Reacts 調度能力,它在規模上優於 Threejs。
- react-three-fiber可以跟上three.js的頻繁功能更新,three.js版本添加、刪除或更改功能,無需依賴此庫的更新。
react-three-fiber的生態系統
畫佈(Canvas)
Canvas 組件在幕後做瞭一些重要的設置工作:
- 它設置瞭Scene和Camera,這是渲染所需的基本構建塊
- 它每幀渲染我們的場景,不需要傳統的渲染循環
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
響應適應父節點,因此可以通過更改父節點的寬度和高度來控制它的大小,在本例中為 #canvas-container
。
上面的three.js demo 代碼用react-three-fiber實現如下:
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粒子模型構建
首先看一段非常簡單的粒子模型的three.js實現
// 創建一個球體幾何對象 var geometry = new THREE.SphereGeometry(100, 25, 25); // 創建一個點材質對象 var material = new THREE.PointsMaterial({ color: 0x0000ff, //顏色 size: 3, //點渲染尺寸 }); // 點模型對象 參數:幾何體 點材質 let point = new THREE.Points(geometry, material); // 網格模型添加到場景中 scene.add(point);
THREE.Points
– 用來創造點的類,也用來批量管理粒子,這個類的構造函數可以接受兩個參數,一個幾何體和一個材質,幾何體參數用來定義粒子的位置坐標,而材質參數用來格式化粒子.
- 在threejs的粒子系統中,每個粒子其實是一張圖片或者一個canvas而不是3D的物體。
- 當粒子量級非常大時,可以用BufferGeometry來代替Geometry的頂點,因為它可以將數據存儲在緩沖區中,減少數據傳遞到GPU的成本,同時因為在緩沖區,所以更適合靜態的物體。
實現思路
-
定義buffer幾何體,並填充數據
創建buffer幾何體
<bufferGeometry attach="geometry"></bufferGeometry>
幾何體定義好之後需要往裡填充數據。假設我們的星系是由70000顆星星組成,星系的背景零零散散的有9000顆星星環繞。我們需要確定各個點的位置和顏色。背景的星星統一由藍色的點,我們在之後的材質裡定義顏色,星系的星星使用顏色矩陣。
//背景 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>
-
將buffer幾何體包裹在點模型中,並為每個點引入材質
引入點材質
const textureLoader = new THREE.TextureLoader(); const shape = textureLoader.load('1.png');
為每個點,加載材質。
// 背景 <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; });
小知識點:useFrame這個鉤子允許你在每個渲染的幀上執行代碼,比如運行效果、更新控件等等。你會收到狀態和時鐘增量。您的回調函數將在渲染幀之前被調用。當組件卸載時,它會自動從渲染循環中取消訂閱。
最後呈現結果
到此這篇關於react-three-fiber實現炫酷3D粒子效果首頁的文章就介紹到這瞭,更多相關react 3D粒子內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 基於Three.js實現3D玉兔效果的示例代碼
- three.js簡單實現類似七聖召喚的擲骰子
- vue3項目中使用three.js的操作步驟
- React + Threejs + Swiper 實現全景圖效果的完整代碼
- three.js實現3d全景看房示例