WebGL之3D地球

看了餓了麼小小倩老師的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

什麼是Three.js

隨着近幾年前端的飛速發展,網頁的表現能力愈來愈強大,瀏覽器提供了WebGL(Web圖形處理庫)接口,能夠經過調用對應API進行3D圖形的繪製,Three.js則是在此基礎接口之上又作了一層封裝。Three.js是當下最流行的網頁3D渲染JS引擎。github

Three.js使用方法

準備階段

  1. 頁面添加canvas元素
<!-- 做爲Three.js渲染器輸出3D圖形 -->
  <canvas id="webglcanvas"></canvas>
複製代碼
  1. 引入Three.js庫文件
  • 本地引入
<!-- Three.js庫 -->
  <script src="./libs/three.min.js"></script>
複製代碼
  • CDN遠程引入
<script src='http://cdnjs.cloudflare.com/ajax/libs/three.js/r70/three.min.js'></script>
複製代碼
  1. 項目中用到的變量
let canvas,  //畫布標籤
  stats,     //性能檢測器
  camera,    //相機
  scene,     //場景
  renderer,  //渲染器
  group,     //物體組
  mouseX = 0,  //鼠標橫向位置
  mouseY = 0,  //鼠標縱向位置
  windowHalfX = window.innerWidth / 2,  //視口大小的通常
  windowHalfY = window.innerHeight / 2; //視口大小的一半
複製代碼

建立場景素材

爲了讓three.js顯示,須要三件事情:場景、相機和渲染器web

  1. 場景,能夠理解爲舞臺。由於要繪製3D效果,必需要有一個舞臺來演示效果
    建立場景 API:THREE.Scene()
scene = new THREE.Scene() //建立場景
複製代碼
  1. 有了場景,如今須要相機來拍攝出素材出來,因此第二步則是須要建立一個"相機"
    建立相機 API:THREE.PerspectiveCamera(fov, aspect, near, far)
    • fov 可視角度,可理解爲視野,是在顯示器上看到的場景的範圍,以度爲單位。
    • aspect 爲width/height,一般設置爲canvas元素的高寬比。
    • near近端距離
    • far遠端距離
    • 只有離相機的距離大於near值,小於far值,且在相機的可視角度以內,才能被相機投影到。
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 2000)
camera.position.z = 500  //相機的遠近
複製代碼
  1. 場景和相機都有了,也就是可以拍攝出素材了,但素材須要通過一些PS、美顏等才能變得好看。這時候就須要渲染器。
    建立渲染器 API: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

建立3D圖形

咱們已經用相機在場景中拍攝出了素材,但這些素在畢竟還只是2D,如今咱們要將該素材由2D變爲3D,這時這就須要用一個3D圖形做爲2D素材的載體。用個吃貨的簡單理解,就是雞肉卷外面的卷,開始卷是2D的平面的,把雞肉到卷面上,用捲包裹起雞肉,則就由2D的卷變成了3D的雞肉卷對吧。emmm...解釋的好尬。
API:canvas

  • 圖形形狀THREE.SphereGeometry(radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength)
    • radius:球體半徑
    • widthSegments, heightSegments:水平方向和垂直方向上分段數。widthSegments最小值爲3,默認值爲8。heightSegments最小值爲2,默認值爲6。
    • phiStart:水平方向上的起始角,默認值0
    • phiLenght:水平方向上球體曲面覆蓋的弧度,默認Math.PI * 2
    • thetaStart : 垂直方向上的起始角, 默認0
    • thetaLength: 垂直方向是球體曲面覆蓋的弧度,默認值爲Math.PI
  • 圖形材質THREE.MeshBasicMaterial({})
    圖形材質有不少種,網上查了查資料,以爲這個總結的挺好,你們能夠看一看 blog.csdn.net/qq_30100043…
  • 圖形構成THREE.Mesh(geometry, material)
    • 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)  //將組合添加進場景中渲染
複製代碼

將建立好的3D圖形添加進組合中

API:group.add(mesh)bash

  • mesh 3D物體

運動起來

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地球的自轉

什麼是stats.js

說了Three.js,如今聊聊什麼是stats.js吧。stats.js 是一個 Three.js 開發的輔助庫,經過檢測動畫運行時的幀數,來測試WebGL代碼的運行性能

stats.js使用

準備階段

相似於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

stats.setMode(1); // 0: fps, 1: ms
參數1 顯示MS

最後優化

1.經過鼠標可控制地球角度

// 綁定鼠標移動事件
document.addEventListener('mousemove', onDocumentMouseMove, false)

// 監聽鼠標移動方向, 從而肯定地球南北半球
function onDocumentMouseMove(ev) {
  ev = ev || event
  mouseX = ev.clientX - windowHalfX
  mouseY = ev.clientY - windowHalfY
}
複製代碼

2.響應式

可根據窗口大小自動改變渲染圖形大小

// 窗口大小改變監聽
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…

第一次在掘金上發文章,但願你們能夠點點贊哈哈~

相關文章
相關標籤/搜索