Three.js初學(3)
- 動畫渲染循環
- 1. 請求動畫幀
- 2. 旋轉動畫
- Canvas畫布布局和全屏
- 常見幾何體
- 渲染器設置
- GUI.js庫
- 1. 庫的引入
- 2. 如何使用
- 初步調試
- 進階調試
- 界面分組
動畫渲染循環
1. 請求動畫幀
requestAnimationFrame
實現周期性循環執行
requestAnimationFrame
默認每秒鐘執行60次,但不一定能做到,要看代碼的性能,對于部分高刷新率的電腦硬件設備,也是有可能超過60次的。
let i = 0;
function render() {i+=1;console.log('執行次數'+i);requestAnimationFrame(render);//請求再次執行函數render
}
render();
2. 旋轉動畫
動畫說白了就是一張張照片,連起來依次展示,這樣就形成一個動畫效果,只要幀率高,人的眼睛就感覺不到卡頓,是連續的視頻效果。以下案例代碼會將幾何體沿著y軸旋轉。rotateY
會影響幾何體旋轉的速度,你也寫成rotateX/Z
。
// 渲染函數
function render() {renderer.render(scene, camera); //執行渲染操作mesh.rotateY(0.01);//每次繞y軸旋轉0.01弧度requestAnimationFrame(render);//請求再次執行渲染函數render,渲染下一幀
}
render();
設置了渲染循環,相機控件OrbitControls
就不用再通過事件change
執行renderer.render(scene, camera);
,畢竟渲染循環一直在執行。
Canvas畫布布局和全屏
threejs
渲染輸出的結果就是一個Cavnas畫布,canvas畫布也是HTML的元素之一,這意味著three.js
渲染結果的布局和普通web前端習慣是一樣的。
全屏布局
const width = window.innerWidth; //窗口文檔顯示區的寬度作為畫布寬度
const height = window.innerHeight; //窗口文檔顯示區的高度作為畫布高度
document.body.appendChild(renderer.domElement);
同時要注意全局的css樣式設置
<style>body{overflow: hidden;margin: 0px;}
</style>
常見幾何體
three.js
的材質默認正面可見,反面不可見,對于矩形平面PlaneGeometry、圓形平面如果你想看到兩面,可以設置side: THREE.DoubleSide
。
new THREE.MeshBasicMaterial({side: THREE.DoubleSide, //兩面可見
});
渲染器設置
渲染器鋸齒屬性
可以使得渲染的幾何體質量更好更清晰一點。
const renderer = new THREE.WebGLRenderer({antialias:true,
});
設置設備像素比
如果你在渲染的過程中需要畫布顯示不清晰或者模糊的問題
// 獲取你屏幕對應的設備像素比.devicePixelRatio告訴threejs,以免渲染模糊問題
renderer.setPixelRatio(window.devicePixelRatio);
設置背景顏色
renderer.setClearColor(0x444444, 1); //設置背景顏色
效果如下圖所示:
GUI.js庫
它是一個前端庫,對HTML、CSS和JavaScript進行了封裝,可以借助dat.gui.js
快速創建控制三維場景的UI交互界面。
1. 庫的引入
github
地址:https://github.com/dataarts/dat.gui
npm
地址:https://www.npmjs.com/package/dat.gui
當然threejs
官方案例擴展庫中也提供了gui.js
。
// 引入dat.gui.js的一個類GUI
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
2. 如何使用
初步調試
創建GUI對象
創建完成之后運行,就會發現右上角多了一個交互界面。
// 實例化一個gui對象
const gui = new GUI();
改變GUI界面默認的style屬性
//改變交互界面style屬性
gui.domElement.style.right = '0px';
gui.domElement.style.width = '300px';
.add()方法
執行gui
的.add()
方法可以快速創建一個UI交互界面,可以用來改變一個JavaScript對象屬性的屬性值。
格式:.add(控制對象,對象具體屬性,其他參數)
//創建一個對象,對象屬性的值可以被GUI庫創建的交互界面改變
const obj = {x: 30,y: 60,z: 30,
};
// gui界面上增加交互界面,改變obj對應屬性
gui.add(obj, 'x', 0, 100);
gui.add(obj, 'y', 0, 100);
gui.add(obj, 'z', 0, 100);
后面的兩個參數,代表著這個拖動條的區間范圍數值。但這個時候拖動只有數值變化,幾何體位置依然不變,如果想要變化,就將obj
對象換成mesh.position
或者可以使用onchange
方法。
gui.add(mesh.position, 'x', 0, 100);
gui.add(obj, 'x', 0, 100).onChange(function(value){mesh.position.x = value;// 你可以寫任何你想跟著obj.x同步變化的代碼
});
光照強度的調試
在調試場景渲染效果的時候,比如光照的強度,人大腦的CPU是沒有能力通過光照參數算出來模型渲染效果的,一般來說你先大概給一個經驗值,然后通過gui
在這個大概值的基礎上下浮動可視化調試。
// 光照強度屬性.intensity
console.log('ambient.intensity',ambient.intensity);
// 通過GUI改變mesh.position對象的xyz屬性
gui.add(ambient, 'intensity', 0, 2.0);
ambient
是我們之前所設置的環境光,詳情可看我的 另一篇博客
進階調試
.name()方法
在創建的交互界面之后,會默認顯示所改變屬性的名字,為了通過交互界面更好理解你改變的某個對象屬性,可以通過.name()
方法改變gui生成交互界面顯示的內容。
gui.add(ambient, 'intensity', 0, 100.0).name('環境光強度');
gui.add(pointLight, 'intensity', 0, 10.0).name('點光源強度');
gui.add(directionalLight, 'intensity', 0, 50.0).name('平行光強度');
步長.step()方法
可以設置交互界面每次改變屬性值間隔是多少。
gui.add(mesh.position, 'x', 0, 100).name('X軸').step(1.0);
gui.add(mesh.position, 'y', 0, 100).name('Y軸').step(1.0);
gui.add(mesh.position, 'z', 0, 100).name('Z軸').step(1.0);
.addColor()顏色值改變
生成幾何體顏色值改變的交互界面。
const obj = {color:0xff0000,
};
// .addColor()生成顏色值改變的交互界面
gui.addColor(obj, 'color').onChange(function(value){mesh.material.color.set(value);
});
.add()方法進階
之前我們所提到的.add()
方法,后面的參數不僅僅可以是數字,還可以是數組,布爾值甚至是對象。
- 數組:
const obj = {scale: 0,
};
// 參數3數據類型:數組(下拉菜單)
gui.add(obj, 'scale', [-100, 0, 100]).name('y坐標').onChange(function (value) {mesh.position.y = value;
});
- 布爾值
const obj = {bool: false,
};
gui.add(obj, 'bool').name('旋轉動畫');// 渲染循環
function render() {// 當gui界面設置obj.bool為true,mesh執行旋轉動畫if (obj.bool) mesh.rotateY(0.01);renderer.render(scene, camera);requestAnimationFrame(render);
}
render();
- 對象
const obj = {scale: 0,
};
// 參數3數據類型:對象(下拉菜單)
gui.add(obj, 'scale', {left: -100,center: 0,right: 100// 左: -100,//可以用中文// 中: 0,// 右: 100
}).name('位置選擇').onChange(function (value) {mesh.position.x = value;
});
界面分組
當GUI交互界面需要控制的屬性比較多的時候,為了避免混合,可以適當分組管理,這樣更清晰。
通過gui對象的.addFolder()
方法可以創建一個子菜單,當你通過GUI控制的屬性比較多的時候,可以使用.addFolder()
進行分組。
const pos = gui.addFolder("位置");
pos.add(mesh.position, 'x', 0, 100).name('X軸').step(1.0);
pos.add(mesh.position, 'y', 0, 100).name('Y軸').step(1.0);
pos.add(mesh.position, 'z', 0, 100).name('Z軸').step(1.0);const lightFolder = gui.addFolder('光源');
lightFolder.add(ambient, 'intensity', 0, 100.0).name('環境光強度').step(1.0);
lightFolder.add(pointLight, 'intensity', 0, 10.0).name('點光源強度').step(1.0);
lightFolder.add(directionalLight, 'intensity', 0, 50.0).name('平行光強度').step(1.0);
子菜單都可以用代碼控制交互界面關閉或開展狀態。
gui.close();//關閉菜單
gui.open(); //打開菜單