使用three.js实现炫酷的酸性风格3D页面
背景
近期學習了?WebGL?和?Three.js?的一些基礎知識,于是想結合最近流行的酸性設計風格,裝飾一下個人主頁,同時總結一些學到的知識。本文內容主要介紹,通過使用?React + three.js?技術棧,加載?3D模型、添加?3D文字、增加動畫、點擊交互等,配合樣式設計,實現充滿設計感的?🤢?酸性風格頁面。
基礎知識
Three.js
Three.js?是一款基于原生?WebGL封裝運行在瀏覽器中的?3D引擎,可以用它創建各種三維場景,包括了攝影機、光影、材質等各種對象。是一款使用非常廣泛的三維引擎。可以在?three.js官方中文文檔?進一步深入學習。
酸性設計
酸性設計?一詞翻譯自?Acid Graphics,起源于?上世紀90年代?的酸浩室音樂、電子舞曲以及嬉皮士文化。在設計領域,這種酸性美學承載一種?自由的主張,怪誕的圖形,大膽鮮明的配色,特殊的材料質感,搭配多種字體,組成了獨特的酸性設計風格。
總之,鮮艷高飽和度?的色彩組合;黑灰色打底高飽和?熒光色?點綴畫面的?五彩斑斕的黑;充滿未來感、炫酷、充滿科技感的液態金屬、玻璃、鋁箔塑料等材質;隨機?的元素、圖形的布局;不斷?重復、裁切、組合?幾何圖形?等都是酸性設計風格。酸性風格在音樂專輯封面、視覺海報、書籍電影封面、網頁設計中也逐漸開始流行。
實現效果
在線預覽:https://tricell.fun
實現
3D模型
場景初始化
🌏?創建場景
scene = new THREE.Scene();📷?初始化相機
透視相機 PerspectiveCamera?的?4個?參數分別是指:視場、長寬比、近面、遠面。
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 1000); // 設置相機位置 camera.position.set(600, 20, -200); // 相機聚焦到屏幕中央 camera.lookAt(new THREE.Vector3(0, 0, 0));💡?初始化光源
添加?半球光源 HemisphereLight:創建室外效果更加自然的光源
light = new THREE.HemisphereLight(0xffffff, 0x444444); light.position.set(0, 20, 0); scene.add(light); light = new THREE.DirectionalLight(0xffffff); light.position.set(0, 20, 10); light.castShadow = true; scene.add(light);添加?環境光 AmbientLight:
var ambiColor = '#0C0C0C'; var ambientLight = new THREE.AmbientLight(ambiColor); scene.add(ambientLight);添加輔助工具(可選)
📦?添加輔助網格
GridHelper?可用于添加網格輔助線,也可用于裝飾,通過?GridHelper(size, divisions, colorCenterLine, colorGrid)?實現。
- size:網格寬度,默認值為?10。
- divisions:等分數,默認值為?10。
- colorCenterLine:中心線顏色,默認值為?0x444444。
- colorGrid: 網格線顏色,默認值為?0x888888。
📦?添加相機控件
通過相機控件?OrbitControls?可以對三維場景進行縮放、平移、旋轉操作,本質上改變的并不是場景,而是相機的參數。開發時?OrbitControls.js?需要單獨引入。
controls = new THREE.OrbitControls(camera, renderer.domElement); controls.target.set(0, 0, 0); controls.update();📦?添加性能查看插件
stats?是一個?Three.js?開發的輔助庫,主要用于檢測動畫運行時的幀數。stats.js?也需要單獨引入。
stats = new Stats(); container.appendChild(stats.dom);加載模型
本文示例用到的?扔鐵餅的人?雕像?3D?模型來源于?threedscans.com,可?免費😄?下載使用,本文末尾提供了多個免費模型下載網站,有?200多頁?免費模型,大家感興趣的話可以挑選自己喜歡的模型下載使用。當然,有建模能力的同學,也可以使用?blender、3dmax?等專業建模軟件生成自己喜歡的模型。
加載?obj?或?fbx?模型
需要單獨引入?FBXLoader.js?或?OBJLoader.js,.fbx?和?.obj?格式的模型加載方法是一樣的。
// var loader = new THREE.FBXLoader(); var loader = new THREE.OBJLoader(); loader.load(model, function (object) {object.traverse(function (child) {if (child.isMesh) {child.castShadow = true;child.receiveShadow = true;}});object.rotation.y = Math.PI / 2;object.position.set(0, -200, 0);object.scale.set(0.32, 0.32, 0.32);model = object;scene.add(object); });加載?gltf?模型
需要單獨引入?GLTFLoader.js,加載?.gltf?格式模型方法稍有不同,需要注意的是模型的遍歷對象和最終添加到場景中的是?object.scene?而不是?object。
var loader = new THREE.GLTFLoader(); loader.load(model, function (object) {object.scene.traverse(function (child) {if (child.isMesh) {child.castShadow = true;child.receiveShadow = true;}});object.scene.rotation.y = Math.PI / 2;object.scene.position.set(0, -240, 0);object.scene.scale.set(0.33, 0.33, 0.33);model = object.scene;scene.add(object.scene); });添加網格、加載完成模型之后的效果如下圖所示。
添加轉盤動畫
通過?requestAnimationFrame?刷新頁面的方法添加轉盤動畫效果。window.requestAnimationFrame()?告訴瀏覽器希望執行一個動畫,并且要求瀏覽器在下次重繪之前調用指定的回調函數更新動畫。該方法需要傳入一個回調函數作為參數,該回調函數會在瀏覽器下一次重繪之前執行。
function animate () {requestAnimationFrame(animate);// 隨著頁面重繪不斷改變場景的rotation.y來實現旋轉scene.rotation.y -= 0.015;renderer.render(scene, camera); }添加點擊交互
在?Three.js?場景中我們要點擊某個模型獲取它的信息、或者做一些其他操作,要用到?Raycaster(光線投射),原理就是在你鼠標點擊的位置發射一束射線,被射線中的物體都被記錄下來。基本語法是?Raycaster(origin, direction, near, far),其中:
- origin:射線的起點向量。
- direction:射線的方向向量。
- near:所有返回的結果應該比?near?遠。值不能為負,默認值為?0。
- far:所有返回的結果應該比?far?近。不能小于?near,默認值為?無窮大。
代碼實現的基本步驟是:獲取鼠標在屏幕的坐標?→?屏幕坐標轉標準設備坐標?→?標準設備坐標轉世界坐標?→?拿到鼠標在場景的世界坐標?→?根據世界坐標和相機生成射線投射方向單位向量?→?根據射線投射方向單位向量創建射線投射器對象。
//聲明raycaster和mouse變量 var raycaster = new THREE.Raycaster(); var mouse = new THREE.Vector2(); onMouseClick = event => {// 將鼠標點擊位置的屏幕坐標轉成threejs中的標準坐標,以屏幕中心為原點,值的范圍為-1到1.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;// 通過鼠標點的位置和當前相機的矩陣計算出raycasterraycaster.setFromCamera(mouse, camera);// 獲取raycaster直線和所有模型相交的數組集合let intersects = raycaster.intersectObjects(scene.children);if (intersects.length > 0) {alert('HELLO WORLD')// 可以通過遍歷實現點擊不同mesh觸發不同交互,如:let selectedObj = intersects[0].object;if (selectedObj.name === 'car') {alert('汽車🚗')}} } window.addEventListener('click', onMouseClick, false);添加3D文字
使用?TextGeometry(text : String, parameters : Object)?添加?3D文字,以下是可設置屬性的說明:
- size:字號大小,一般為大寫字母的高度。
- height:文字的厚度。
- weight:值為?normal?或?bold,表示是否加粗。
- font:字體,默認是?helvetiker,需對應引用的字體文件。
- style:值為?normal?或?italics,表示是否斜體
- bevelThickness:倒角厚度。
- bevelSize:倒角寬度。
- curveSegments:弧線分段數,使得文字的曲線更加光滑。
- bevelEnabled:布爾值,是否使用倒角,意為在邊緣處斜切。
優化
現在模型加載已經基本完成了,但是?3D?模型的體積一般比較大,部署之后我發現網頁加載非常慢,影響用戶體驗,減小模型體積是十分必要的,在網上找了很久壓縮工具,發現在不需要安裝大型?3D建模軟件?的情況下,使用?obj2gltf可以將體積較大的?OBJ?格式模型轉化為?gltf?模型,有效優化模型體積,提升網頁加載速度。
安裝
npm install obj2gltf --save將obj模型復制到以下目錄中
node_modules\obj2gltf\bin執行轉碼指令
node obj2gltf.js -i demo.obj -o demo.gltf如圖出現類似上述內容,轉碼完成,對比轉化前后的文件體積,本例中?kas.obj?初始文件大小為?9.7M?轉化后的文件?kas.gltf?只有?4.6M,體積縮小一半,此時將轉化后的模型加載到頁面上,肉眼幾乎看不出模型效果的變化,同時頁面加載速度得到明顯提升。
obj2gltf?也可以作為庫使用,通過?node服務?實時轉化模型,感興趣的同學可以通過文章末尾鏈接深入學習。
也可以是使用?3D?建模軟件如?blender?等手動通過減少模型?面數?和?縮小體積?等途徑對模型壓縮優化,這種優化效果更明顯。
完整代碼
var model = require('@/assets/models/kas.gltf'); var container, stats, controls; var camera, scene, renderer, light, model; class Kas extends React.Component {render () {return (<div id="kas"></div>)}componentDidMount () {this.initThree();}initThree () {init();animate();function init () {container = document.getElementById('kas');scene = new THREE.Scene();scene.fog = new THREE.Fog(0xa0a0a0, 200, 1000);// 透視相機:視場、長寬比、近面、遠面camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 1000);camera.position.set(600, 20, -200);camera.lookAt(new THREE.Vector3(0, 0, 0));// 半球光源:創建室外效果更加自然的光源light = new THREE.HemisphereLight(0xffffff, 0x444444);light.position.set(0, 20, 0);scene.add(light);light = new THREE.DirectionalLight(0xffffff);light.position.set(0, 20, 10);light.castShadow = true;scene.add(light);// 環境光var ambiColor = '#0C0C0C';var ambientLight = new THREE.AmbientLight(ambiColor);scene.add(ambientLight);// 網格var grid = new THREE.GridHelper(1000, 100, 0x000000, 0x000000);grid.material.opacity = 0.1;grid.material.transparent = true;grid.position.set(0, -240, 0);scene.add(grid);// 加載gltf模型var loader = new THREE.GLTFLoader();loader.load(model, function (object) {object.scene.traverse(function (child) {if (child.isMesh) {child.castShadow = true;child.receiveShadow = true;}});object.scene.rotation.y = Math.PI / 2;object.scene.position.set(0, -240, 0);object.scene.scale.set(0.33, 0.33, 0.33);model = object.scene;scene.add(object.scene);});renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });renderer.setPixelRatio(window.devicePixelRatio);renderer.setSize(window.innerWidth, window.innerHeight);renderer.setClearAlpha(0);renderer.shadowMap.enabled = true;container.appendChild(renderer.domElement);window.addEventListener('resize', () => {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);}, false);stats = new Stats();container.appendChild(stats.dom);}function animate () {var clock = new THREE.Clock()requestAnimationFrame(animate);var delta = clock.getDelta();scene.rotation.y -= 0.015;renderer.render(scene, camera);stats.update();}// 增加點擊事件//聲明raycaster和mouse變量var raycaster = new THREE.Raycaster();var mouse = new THREE.Vector2();function onMouseClick(event) {// 通過鼠標點擊位置計算出raycaster所需要點的位置,以屏幕中心為原點,值的范圍為-1到1.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;// 通過鼠標點的位置和當前相機的矩陣計算出raycasterraycaster.setFromCamera(mouse, camera);// 獲取raycaster直線和所有模型相交的數組集合var intersects = raycaster.intersectObjects(scene.children);if (intersects.length > 0) {alert('HELLO WORLD')}}window.addEventListener('click', onMouseClick, false);} }其他設計元素
本文主要介紹?3D元素?的加載,由于文章篇幅以及時間有限(博主太懶😂)其他元素的實現不做詳細講解(可能后續有時間會總結整理?maybe)感興趣的同學可以擴展閱讀以下其他大神優秀的文章。
流體背景
靜態?液態背景圖可以通過?SVG filter?實現,可以閱讀《Creating Patterns With SVG Filters》,實現?動態?流體背景,可以使用Three.js 結合原生GLSL實現,可參考《CodePen Shader Template》示例來實現。
金屬、霓虹、故障效果等酸性效果字體可以閱讀我的另一篇文章《僅用CSS幾步實現賽博朋克2077風格視覺效果》,也可以使用設計生成,由于時間關系,本文項目中的金屬效果文字以及本文banner頭圖中的文字都是使用在線藝術字體生成網站生成的,感興趣的同學可以自行嘗試設計。
未來進一步優化
- #todo?酸性風格液態背景實現。
- #todo?3D模型液態金屬效果。
three.js?優秀案例推薦
最后給大家推薦幾個非常驚艷的?three.js?項目來一起體驗和學習,無論是頁面交互、視覺設計還是性能優化都做到了極致,可以從中學到很多。
- github首頁:3D地球?實時顯示全球熱門倉庫。
- kodeclubs:低面數?3D城市?第三人稱小游戲。
- 球鞋展示:720度?球鞋動態展示。
- 沙雕dance:沙雕動物舞者。
- Zenly軟件:Zenly App?中文主頁。
總結
以上是生活随笔為你收集整理的使用three.js实现炫酷的酸性风格3D页面的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ThreadLocal基本使用和内存泄漏
- 下一篇: 利用注解 + 反射消除重复代码,妙!