看了餓了麼小小倩老師的canvas做品,心血來潮,學着作了個3D地球,也算是入坑WebGL了吧。以前有用過原生的canvas畫2D的圖形,此次則是用了Three.js和stats.js的3D框架,邊學邊練手,效果還算比較滿意...畢竟第一次接觸WebGLcss
Talk is cheap show the code!html
github項目源碼地址:github.com/FightingHao…
項目演示地址:fightinghao.github.io/codingDream…前端
代碼還有不少不足,求大神review..git
隨着近幾年前端的飛速發展,網頁的表現能力愈來愈強大,瀏覽器提供了WebGL(Web圖形處理庫)接口,能夠經過調用對應API進行3D圖形的繪製,Three.js
則是在此基礎接口之上又作了一層封裝。Three.js
是當下最流行的網頁3D渲染JS引擎。github
<!-- 做爲Three.js渲染器輸出3D圖形 -->
<canvas id="webglcanvas"></canvas>
複製代碼
<!-- Three.js庫 -->
<script src="./libs/three.min.js"></script>
複製代碼
<script src='http://cdnjs.cloudflare.com/ajax/libs/three.js/r70/three.min.js'></script>
複製代碼
let canvas, //畫布標籤
stats, //性能檢測器
camera, //相機
scene, //場景
renderer, //渲染器
group, //物體組
mouseX = 0, //鼠標橫向位置
mouseY = 0, //鼠標縱向位置
windowHalfX = window.innerWidth / 2, //視口大小的通常
windowHalfY = window.innerHeight / 2; //視口大小的一半
複製代碼
爲了讓three.js顯示,須要三件事情:場景、相機和渲染器web
THREE.Scene()
scene = new THREE.Scene() //建立場景
複製代碼
THREE.PerspectiveCamera(fov, aspect, near, far)
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 2000)
camera.position.z = 500 //相機的遠近
複製代碼
THREE.WebGLRenderer({})
renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true, // true/false表示是否開啓反鋸齒,
/*
alpha: false, // true/false 表示是否能夠設置背景色透明,
precision: 'highp', // highp/mediump/lowp 表示着色精度選擇,
premultipliedAlpha: false, // true/false 表示是否能夠設置像素深度(用來度量圖像的分辨率),
maxLights: 3, // 最大燈光數,
stencil: false // false/true 表示是否使用模板字體或圖案
*/
})
複製代碼
指定渲染器寬高 API:renderer.setSize(window.innerWidth, window.innerHeight)
參數分別爲寬和高ajax
咱們已經用相機在場景中拍攝出了素材,但這些素在畢竟還只是2D,如今咱們要將該素材由2D變爲3D,這時這就須要用一個3D圖形做爲2D素材的載體。用個吃貨的簡單理解,就是雞肉卷外面的卷,開始卷是2D的平面的,把雞肉到卷面上,用捲包裹起雞肉,則就由2D的卷變成了3D的雞肉卷對吧。emmm...解釋的好尬。
API:canvas
THREE.SphereGeometry(radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength)
THREE.MeshBasicMaterial({})
THREE.Mesh(geometry, material)
let geometry = new THREE.SphereGeometry(200, 20, 20) //形狀
let material = new THREE.MeshBasicMaterial({ map: texture }) //材質
let mesh = new THREE.Mesh(geometry, material) //物體
複製代碼
new THREE.TextureLoader().load(img, callback)
loader.load('./img/land_ocean_ice_cloud_2048.jpg', function (texture) {
let geometry = new THREE.SphereGeometry(200, 20, 20) //形狀
let material = new THREE.MeshBasicMaterial({ map: texture }) //材質
let mesh = new THREE.Mesh(geometry, material) //物體
})
複製代碼
咱們有了3D圖形,接下來就是將這些圖形組合在一塊兒,變成多樣的3D界面
建立組合 API:THREE.Group()
瀏覽器
//建立一個組合
group = new THREE.Group()
scene.add(group) //將組合添加進場景中渲染
複製代碼
API:group.add(mesh)
bash
3D圖形已經在場景中渲染出來了,如今,則須要讓它們動起來!
function animate() {
// 請求運動幀
requestAnimationFrame(animate)
render()
}
// 地球旋轉邏輯函數
function render() {
// 更新性能監視器
stats.update();
camera.position.x += (mouseX - camera.position.x) * 0.05
camera.position.y += (mouseX - camera.position.y) * 0.05
// 拍攝角度, 可改變地球視角
camera.lookAt(scene.position)
// 地球自轉速度
group.rotation.y -= 0.005
// 運動核心 遞歸調用
renderer.render(scene, camera)
}
複製代碼
如今已經基本完成了3D地球的自轉
說了Three.js,如今聊聊什麼是stats.js吧。stats.js 是一個 Three.js 開發的輔助庫,經過檢測動畫運行時的幀數,來測試WebGL代碼的運行性能
相似於Three.js,stats.js也須要引入框架庫,並由div來渲染顯示性能測試界面
<!-- 用於顯示和統計圖形的性能 -->
<div id="stats-output"></div>
<!-- 引入stats.js庫 -->
<script src="./libs/stats.min.js"></script>
複製代碼
首先須要初始化stats
// stats性能檢測器初始化
stats = initStats();
function initStats() {
stats = new Stats();
//設置統計模式
stats.setMode(0); // 0: fps, 1: ms
//統計信息顯示在左上角
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '10px';
stats.domElement.style.top = '10px';
//將統計對象添加到對應的<div>元素中
document.getElementById("stats-output").appendChild(stats.domElement);
return stats;
}
複製代碼
當場景變換時,也就是3D運動時,須要實時更新stats檢測器 API:stats.update();
用於地球旋轉時,動態更新檢測狀況
// 地球旋轉邏輯函數
function render() {
// 更新性能監視器
stats.update();
camera.position.x += (mouseX - camera.position.x) * 0.05
camera.position.y += (mouseX - camera.position.y) * 0.05
// 拍攝角度, 可改變地球視角
camera.lookAt(scene.position)
// 地球自轉速度
group.rotation.y -= 0.005
// 核心 遞歸調用
renderer.render(scene, camera)
}
複製代碼
能夠看到兩種檢測狀況 stats.setMode(0); // 0: fps, 1: ms
參數0 顯示FPS
// 綁定鼠標移動事件
document.addEventListener('mousemove', onDocumentMouseMove, false)
// 監聽鼠標移動方向, 從而肯定地球南北半球
function onDocumentMouseMove(ev) {
ev = ev || event
mouseX = ev.clientX - windowHalfX
mouseY = ev.clientY - windowHalfY
}
複製代碼
可根據窗口大小自動改變渲染圖形大小
// 窗口大小改變監聽
window.addEventListener('resize', onWindowResize, false)
// 監聽窗口大小, 從而根據窗口大小改變地球大小, 相似響應式
function onWindowResize() {
windowHalfX = window.innerWidth / 2
windowHalfY = window.innerHeight / 2
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth, window.innerHeight)
}
複製代碼
好了,如今3D地球就基本上完成了~
完整代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>WebGL之3D地球</title>
<style>
/* 禁止系統默認滾動條 */
html,
body {
height: 100%;
overflow: hidden;
}
</style>
</head>
<body>
<!-- 用於顯示和統計圖形的性能 -->
<div id="stats-output"></div>
<!-- 做爲Three.js渲染器輸出3D圖形 -->
<canvas id="webglcanvas"></canvas>
<!-- webgl庫 -->
<script src="./libs/three.min.js"></script>
<script src="./libs/stats.min.js"></script>
<script>
let canvas, //畫布標籤 繪圖API
stats, //性能檢測器
camera, //相機
scene, //場景
renderer, //渲染器
group, //物體組
mouseX = 0, //鼠標橫向位置
mouseY = 0, //鼠標縱向位置
windowHalfX = window.innerWidth / 2, //視口大小的通常
windowHalfY = window.innerHeight / 2; //視口大小的一半
init() //構建地球
animate() //使地球旋轉起來
function init() {
// 獲取canvas畫布
canvas = document.getElementById('webglcanvas')
// stats性能檢測器初始化
stats = initStats();
// 3D繪製
// 相機
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 2000)
camera.position.z = 500 //相機的遠近
// 場景
scene = new THREE.Scene()
// 建立一個組合
group = new THREE.Group()
scene.add(group) //將組合添加進場景中渲染
// 地球 數學形狀 貼圖
let loader = new THREE.TextureLoader()
loader.load('./img/land_ocean_ice_cloud_2048.jpg', function (texture) {
// console.log(texture)
let geometry = new THREE.SphereGeometry(200, 20, 20) //形狀
let material = new THREE.MeshBasicMaterial({ map: texture }) //材質
let mesh = new THREE.Mesh(geometry, material) //物體
group.add(mesh)
})
// 渲染器
renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true, // true/false表示是否開啓反鋸齒,
/*
alpha: false, // true/false 表示是否能夠設置背景色透明,
precision: 'highp', // highp/mediump/lowp 表示着色精度選擇,
premultipliedAlpha: false, // true/false 表示是否能夠設置像素深度(用來度量圖像的分辨率),
maxLights: 3, // 最大燈光數,
stencil: false // false/true 表示是否使用模板字體或圖案
*/
})
// 指定渲染器寬高
renderer.setSize(window.innerWidth, window.innerHeight)
// 綁定鼠標移動事件
document.addEventListener('mousemove', onDocumentMouseMove, false)
// 窗口大小改變監聽
window.addEventListener('resize', onWindowResize, false)
}
// 監聽鼠標移動方向, 從而肯定地球南北半球
function onDocumentMouseMove(ev) {
ev = ev || event
mouseX = ev.clientX - windowHalfX
mouseY = ev.clientY - windowHalfY
}
// 監聽窗口大小, 從而根據窗口大小改變地球大小, 相似響應式
function onWindowResize() {
windowHalfX = window.innerWidth / 2
windowHalfY = window.innerHeight / 2
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth, window.innerHeight)
}
function animate() {
// 請求運動幀
requestAnimationFrame(animate)
render()
}
// 地球旋轉邏輯函數
function render() {
// 更新性能監視器
stats.update();
camera.position.x += (mouseX - camera.position.x) * 0.05
camera.position.y += (mouseX - camera.position.y) * 0.05
// 拍攝角度, 可改變地球視角
camera.lookAt(scene.position)
// 地球自轉速度
group.rotation.y -= 0.005
// 核心 遞歸調用
renderer.render(scene, camera)
}
function initStats() {
stats = new Stats();
//設置統計模式
stats.setMode(0); // 0: fps, 1: ms
//統計信息顯示在左上角
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '10px';
stats.domElement.style.top = '10px';
//將統計對象添加到對應的<div>元素中
document.getElementById("stats-output").appendChild(stats.domElement);
return stats;
}
// ecchat 數據可視化
// 平面的世界是錯誤的, css perspective:1000px transform-style:perserve-3d
// Camera Scene render(渲染容器) Light -> canvas
</script>
</body>
</html>
複製代碼
圖片素材和js庫能夠到個人github上下載:
github.com/FightingHao…
第一次在掘金上發文章,但願你們能夠點點贊哈哈~