記錄--教你用three.js寫一個炫酷的3D登陸頁面

来源:https://www.cnblogs.com/smileZAZ/archive/2022/12/19/16992775.html
-Advertisement-
Play Games

這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 前言: 該篇文章用到的主要技術:vue3、three.js 我們先看看成品效果: 高清大圖預覽(會有些慢): 座機小圖預覽: 廢話不多說,直接進入正題 Three.js的基礎知識 想象一下,在一個虛擬的3D世界中都需要什麼?首先,要有一個 ...


這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助

前言:

該篇文章用到的主要技術:vue3、three.js

我們先看看成品效果:

高清大圖預覽(會有些慢):

座機小圖預覽:

login2.gif

廢話不多說,直接進入正題

Three.js的基礎知識

想象一下,在一個虛擬的3D世界中都需要什麼?首先,要有一個立體的空間,其次是有光源,最重要的是要有一雙眼睛。下麵我們就看看在three.js中如何創建一個3D世界吧!

  1. 創建一個場景
  2. 設置光源
  3. 創建相機,設置相機位置和相機鏡頭的朝向
  4. 創建3D渲染器,使用渲染器把創建的場景渲染出來

此時,你就通過three.js創建出了一個可視化的3D頁面,很簡單是吧!

關於場景

你可以為場景添加背景顏色,或創建一個盒模型(球體、立方體),給盒模型的內部貼上圖片,再把相機放在這個盒模型內部以達到模擬場景的效果。盒模型的方式多用於360度全景,比如房屋vr展示

【登陸頁面】創建場景的例子:

const scene = new THREE.Scene()
// 在場景中添加霧的效果,Fog參數分別代表‘霧的顏色’、‘開始霧化的視線距離’、剛好霧化至看不見的視線距離’
scene.fog = new THREE.Fog(0x000000, 0, 10000)
// 盒模型的深度
const depth = 1400
// 在場景中添加一個圓球盒模型
// 1.創建一個立方體
const geometry = new THREE.BoxGeometry(1000, 800, depth)
// 2.載入紋理
const texture = new THREE.TextureLoader().load('bg.png')
// 3.創建網格材質(原料)
const material = new THREE.MeshBasicMaterial({map: texture, side: THREE.BackSide})
// 4.生成網格
const mesh = new THREE.Mesh(geometry, material)
// 5.把網格放入場景中
scene.add(mesh)

關於光源

為場景設置光源的顏色、強度,同時還可以設置光源的類型(環境光、點光源、平行光等)、光源所在的位置

【登陸頁面】創建光源的例子:

// 1.創建環境光
const ambientLight = new THREE.AmbientLight(0xffffff, 1)
// 2.創建點光源,位於場景右下角
const light_rightBottom = new THREE.PointLight(0x0655fd, 5, 0)
light_rightBottom.position.set(0, 100, -200)
// 3.把光源放入場景中
scene.add(light_rightBottom)
scene.add(ambientLight)

關於相機(重要)

很重要的一步,相機就是你的眼睛。這裡還會著重說明一下使用透視相機時可能會遇到的問題,我們最常用到的相機就是正交相機和透視相機了。

正交相機:無論物體距離相機距離遠或者近,在最終渲染的圖片中物體的大小都保持不變。用於渲染2D場景或者UI元素是非常有用的。如圖:

圖註解:

  1. 圖中紅色三角錐體是視野的大小
  2. 紅色錐體連著的第一個面是攝像機能看到的最近位置
  3. 從該面通過白色輔助線延伸過去的面是攝像機能看到的最遠的位置

透視相機:被用來模擬人眼所看到的景象。它是3D場景的渲染中使用得最普遍的投影模式。如圖:

我們在使用透視相機時,可能會遇到這種情況:邊緣處的物體會產生一定程度上的形變,原因是:透視相機是魚眼效果,如果視域越大,邊緣變形越大。為了避免邊緣變形,可以將fov角度設置小一些,距離拉遠一些

關於透視相機的幾個參數,new THREE.PerspectiveCamera(fov, width / height, near, far)

  • fov(field of view) — 攝像機視錐體垂直視野角度
  • aspect(width / height) — 攝像機視錐體長寬比
  • near — 攝像機視錐體近端面
  • far — 攝像機視錐體遠端面
/**
 * 為了避免邊緣變形,這裡將fov角度設置小一些,距離拉遠一些
 * 固定視域角度,求需要多少距離才能滿足完整的視野畫面
 * 15度等於(Math.PI / 12)
 */
const container = document.getElementById('login-three-container')
const width = container.clientWidth
const height = container.clientHeight
const fov = 15
const distance = width / 2 / Math.tan(Math.PI / 12)
const zAxisNumber = Math.floor(distance - depth / 2)
const camera = new THREE.PerspectiveCamera(fov, width / height, 1, 30000)
camera.position.set(0, 0, zAxisNumber)
const cameraTarget = new THREE.Vector3(0, 0, 0)
camera.lookAt(cameraTarget)

關於渲染器

WebGL渲染出你精心製作的場景。它會創建一個canvas進行渲染

【登陸頁面】創建渲染器的例子:

// 獲取容器dom
const container = document.getElementById('login-three-container')
// 創建webgl渲染器實例
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })
// 設置渲染器畫布的大小
renderer.setSize(width, height)
// 把畫布實例(canvas)放入容器中
container.appendChild(renderer.domElement)
// 渲染器渲染場景
renderer.render(scene, camera)
需要註意,這樣創建出來的場景並沒有動效,原因是這次渲染的僅僅只是這一幀的畫面。為了讓場景中的物體能動起來,我們需要使用requestAnimationFrame,所以我們可以寫一個loop函數
//動畫刷新
const loopAnimate = () => {
    requestAnimationFrame(loopAnimate)
    scene.rotateY(0.001)
    renderer.render(scene, camera)
}
loopAnimate()

完善效果

創建一個左上角的地球

// 載入紋理
const texture = THREE.TextureLoader().load('earth_bg.png')
// 創建網格材質
const material = new THREE.MeshPhongMaterial({map: texture, blendDstAlpha: 1})
// 創建幾何球體
const sphereGeometry = new THREE.SphereGeometry(50, 64, 32)
// 生成網格
const sphere = new THREE.Mesh(sphereGeometry, material)
// 為了單獨操作球體的運動效果,我們把球體放到一個組中
const Sphere_Group = new THREE.Group()
const Sphere_Group.add(sphere)
// 設置該組(球體)在空間坐標中的位置
const Sphere_Group.position.x = -400
const Sphere_Group.position.y = 200
const Sphere_Group.position.z = -200
// 加入場景
scene.add(Sphere_Group)
// 使球能夠自轉,需要在loopAnimate中加上
Sphere_Group.rotateY(0.001)

使地球自轉

// 渲染星球的自轉
const renderSphereRotate = () => {
    if (sphere) {
      Sphere_Group.rotateY(0.001)
    }
}
// 使球能夠自轉,需要在loopAnimate中加上
const loopAnimate = () => {
    requestAnimationFrame(loopAnimate)
    renderSphereRotate()
    renderer.render(scene, camera)
}

創建星星

// 初始化星星
const initSceneStar = (initZposition: number): any => {
    const geometry = new THREE.BufferGeometry()
    const vertices: number[] = []
    const pointsGeometry: any[] = []
    const textureLoader = new THREE.TextureLoader()
    const sprite1 = textureLoader.load('starflake1.png')
    const sprite2 = textureLoader.load('starflake2.png')
    parameters = [
      [[0.6, 100, 0.75], sprite1, 50],
      [[0, 0, 1], sprite2, 20]
    ]
    // 初始化500個節點
    for (let i = 0; i < 500; i++) {
      /**
       * const x: number = Math.random() * 2 * width - width
       * 等價
       * THREE.MathUtils.randFloatSpread(width)
       * _.random使用的是lodash庫中的生成隨機數
       */
      const x: number = THREE.MathUtils.randFloatSpread(width)
      const y: number = _.random(0, height / 2)
      const z: number = _.random(-depth / 2, zAxisNumber)
      vertices.push(x, y, z)
    }

    geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3))

    // 創建2種不同的材質的節點(500 * 2)
    for (let i = 0; i < parameters.length; i++) {
      const color = parameters[i][0]
      const sprite = parameters[i][1]
      const size = parameters[i][2]

      materials[i] = new THREE.PointsMaterial({
        size,
        map: sprite,
        blending: THREE.AdditiveBlending,
        depthTest: true,
        transparent: true
      })
      materials[i].color.setHSL(color[0], color[1], color[2])
      const particles = new THREE.Points(geometry, materials[i])
      particles.rotation.x = Math.random() * 0.2 - 0.15
      particles.rotation.z = Math.random() * 0.2 - 0.15
      particles.rotation.y = Math.random() * 0.2 - 0.15
      particles.position.setZ(initZposition)
      pointsGeometry.push(particles)
      scene.add(particles)
    }
    return pointsGeometry
}
const particles_init_position = -zAxisNumber - depth / 2
let zprogress = particles_init_position
let zprogress_second = particles_init_position * 2
const particles_first = initSceneStar(particles_init_position)
const particles_second = initSceneStar(zprogress_second)

使星星運動

// 渲染星星的運動
const renderStarMove = () => {
    const time = Date.now() * 0.00005
    zprogress += 1
    zprogress_second += 1

    if (zprogress >= zAxisNumber + depth / 2) {
      zprogress = particles_init_position
    } else {
      particles_first.forEach((item) => {
        item.position.setZ(zprogress)
      })
    }
    if (zprogress_second >= zAxisNumber + depth / 2) {
      zprogress_second = particles_init_position
    } else {
      particles_second.forEach((item) => {
        item.position.setZ(zprogress_second)
      })
    }

    for (let i = 0; i < materials.length; i++) {
      const color = parameters[i][0]

      const h = ((360 * (color[0] + time)) % 360) / 360
      materials[i].color.setHSL(color[0], color[1], parseFloat(h.toFixed(2)))
    }
}

星星的運動效果,實際就是沿著z軸從遠處不斷朝著相機位置移動,直到移出相機的位置時回到起點,不斷重覆這個操作。我們使用上帝視角,從x軸的左側看去,效果如下:

創建雲以及運動軌跡

// 創建曲線路徑
const route = [
    new THREE.Vector3(-width / 10, 0, -depth / 2),
    new THREE.Vector3(-width / 4, height / 8, 0),
    new THREE.Vector3(-width / 4, 0, zAxisNumber)
]
const curve = new THREE.CatmullRomCurve3(route, false)
const tubeGeometry = new THREE.TubeGeometry(curve, 100, 2, 50, false)
const tubeMaterial = new THREE.MeshBasicMaterial({
  opacity: 0,
  transparent: true
})
const tube = new THREE.Mesh(tubeGeometry, tubeMaterial)
// 把創建好的路徑加入場景中
scene.add(tube)
// 創建平面幾何
const clondGeometry = new THREE.PlaneGeometry(500, 200)
const textureLoader = new THREE.TextureLoader()
const cloudTexture = textureLoader.load('cloud.png')
const clondMaterial = new THREE.MeshBasicMaterial({
  map: cloudTexture,
  blending: THREE.AdditiveBlending,
  depthTest: false,
  transparent: true
})
const cloud = new THREE.Mesh(clondGeometry, clondMaterial)
// 將雲加入場景中
scene.add(cloud)

現在有了雲和曲線路徑,我們需要將二者結合,讓雲按著路徑進行運動

使雲運動

let cloudProgress = 0
let scaleSpeed = 0.0006
let maxScale = 1
let startScale = 0
// 初始化雲的運動函數
const cloudMove = () => {
  if (startScale < maxScale) {
    startScale += scaleSpeed
    cloud.scale.setScalar(startScale)
  }
  if (cloudProgress > 1) {
    cloudProgress = 0
    startScale = 0
  } else {
    cloudProgress += speed
    if (cloudParameter.curve) {
      const point = curve.getPoint(cloudProgress)
      if (point && point.x) {
        cloud.position.set(point.x, point.y, point.z)
      }
    }
  }

}

完成three.js有關效果

最後,把cloudMove函數放入loopAnimate函數中即可實現雲的運動。至此,該登錄頁所有與three.js有關的部分都介紹完了。剩下的月球地面、登錄框、宇航員都是通過定位和層級設置以及css3動畫實現的,這裡就不進行深入的討論了。

上面的每個部分的代碼在連貫性並不完整,並且同登錄頁的完整代碼也有些許出入。上面更多是為了介紹每個部分的實現方式。完整代碼,我放在github上了,每行註釋幾乎都打上了,希望能給你入坑three.js帶來一些幫助,地址:github.com/Yanzengyong…

結語

最後,我認為3D可視化的精髓其實在於設計,有好的素材、好的建模,能讓你的頁面效果瞬間提升N倍

three.js官網

本文轉載於:

https://juejin.cn/post/7020571868314730532

如果對您有所幫助,歡迎您點個關註,我會定時更新技術文檔,大家一起討論學習,一起進步。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 摘要:GaussDB(DWS)提供了資源管理功能,用戶可以根據自身業務情況對資源進行劃分,將資源按需劃分成不同的資源池,不同資源池之間資源互相隔離。 本文分享自華為雲社區《GaussDB(DWS)資源管理排隊原理與問題定位》,作者: 門前一棵葡萄樹 。 一、記憶體管控原理 GaussDB(DWS)提供 ...
  • Redis——02 前面瞭解了 Redis 以及在 Linux 的安裝,下麵瞭解一些 Redis 常用的命令。 Redis 常用命令: Redis 是 Key-Value 形式,Key 為字元串類型,而 Value 的取值類型如下: String 字元串 Hash 哈希表 List 列表 Set 集 ...
  • 摘要:以下提供的都是各個資料庫較為官方的jar包獲取方式。 本文分享自華為雲社區《JDBC連接相關jar包獲取及上傳管理中心白名單處理》,作者:HuaWei XYe。 jar包獲取 以下提供的都是各個資料庫較為官方的jar包獲取方式 1、Mysql https://dev.mysql.com/dow ...
  • GreatSQL社區原創內容未經授權不得隨意使用,轉載請聯繫小編並註明來源。 GreatSQL是MySQL的國產分支版本,使用上與MySQL一致。 作者:葉金榮 文章來源:社區原創 可能會執行非常慢,線上生產環境千萬別寫出這種SQL ... 背景交代 用 tpcc-mysql 工具生成 50個倉庫 ...
  • 1. 判斷本地是否已經安裝MySQL ① 在運行界面輸入services.msc進入服務界面,查看是否有MySQL服務 ② 進入任務管理器,點擊服務看是否有MySQL服務 2. 安裝MySQL(壓縮包版) 1. 下載MySQL社區伺服器(ZIP): MySQL zip下載 點擊No thanks,j ...
  • 華為運動健康服務(HUAWEI Health Kit)提供原子化數據開放,用戶數據被授權獲取後,應用可通過介面訪問運動健康數據,對相關數據進行增、刪、改、查等操作。這篇文章彙總了申請開通Health Kit測試許可權的常見問題,並給出了詳細解答,希望為開發者提供相關參考。 (1) 申請Health K ...
  • 本文簡介 點贊 + 關註 + 收藏 = 學會了 作為一隻前端,只懂 Vue、React 感覺已經和大家拉不開距離了。 可視化、機器學習等領域 JS 都有涉及到,而可視化方面已經被很多領域用到,比如大屏項目。 可視化領域相關的技術有 canvas 和 SVG ,而這兩個東東是遲早要接觸的了。 在我接觸 ...
  • 本章將繼續和大家分享Vue的一些基礎知識。話不多說,下麵我們直接上代碼: 本文內容大部分摘自Vue的官網:https://v2.cn.vuejs.org/v2/guide/ 一、計算屬性 示例如下: <!DOCTYPE html> <html lang="en"> <head> <meta char ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...