DS-SDK封裝ThreeJS的三維場(chǎng)景核心庫(kù)Viewer
正文
viewer核心庫(kù)的封裝主要是針對(duì)threejs場(chǎng)景進(jìn)行初始封裝,以便多項(xiàng)目復(fù)用。具體細(xì)節(jié)我就不多寫了,網(wǎng)上一大堆,但是我發(fā)現(xiàn)網(wǎng)上的例子都比較雷同,所以我的這一篇文檔會(huì)著重從我做過(guò)的項(xiàng)目上遇到的一些問(wèn)題來(lái)具體描寫,詳細(xì)請(qǐng)看第七、第八小節(jié),主要是第一我們真實(shí)項(xiàng)目中,其實(shí)你的渲染頁(yè)面不是整個(gè)頁(yè)面,而且其中的一小塊div,所以你的寬高是div而不是windws的(如下圖);第二對(duì)于頁(yè)面大小變化的監(jiān)視,以我的知識(shí)結(jié)構(gòu)來(lái)說(shuō),還沒(méi)有很好的解決方案來(lái)監(jiān)視div的大小變化。
基礎(chǔ)封裝初始化包括以下:
一、ThreeJS-ES6庫(kù)引入
引入部分除了ThreeJS核心庫(kù)的東西外,還有標(biāo)簽渲染器,用于后期在場(chǎng)景中添加標(biāo)簽,還有控制器何和幀率顯示器。
import { Cache, WebGLRenderer, ACESFilmicToneMapping, PCFSoftShadowMap, sRGBEncoding, PerspectiveCamera, Scene, Color } from 'three' // 二維標(biāo)簽渲染器 import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer' import { CSS3DRenderer } from 'three/examples/jsm/renderers/CSS3DRenderer' import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js' import Stats from 'three/examples/jsm/libs/stats.module.js'
二、初始化渲染器
初始化渲染器部分主要是獲取渲染畫布的dom,然后初始化場(chǎng)景渲染器,初始化二維標(biāo)簽渲染器,和三維標(biāo)簽渲染器。 這一部分代碼我們?cè)O(shè)置渲染器的大小,這個(gè)放到了后面會(huì)講。
_initRenderer () { // 獲取畫布dom this.viewerDom = document.getElementById(this.id) // 初始化渲染器 this.renderer = new WebGLRenderer({ logarithmicDepthBuffer: true, antialias: true, // true/false表示是否開(kāi)啟反鋸齒 alpha: true, // true/false 表示是否可以設(shè)置背景色透明 precision: 'highp', // highp/mediump/lowp 表示著色精度選擇 premultipliedAlpha: true, // true/false 表示是否可以設(shè)置像素深度(用來(lái)度量圖像的分辨率) preserveDrawingBuffer: true // true/false 表示是否保存繪圖緩沖 }) this.renderer.domElement.style.zIndex = 1 // 默認(rèn)情況下,js的光強(qiáng)數(shù)值不真實(shí)。為了使得光強(qiáng)更趨于真實(shí)值,應(yīng)該把渲染器的physicallyCorrectLights屬性設(shè)為true this.renderer.physicallyCorrectLights = true // 盡管我們的貼圖不是HDR,但使用tone mapping可以塑造更真實(shí)的效果。 this.renderer.toneMapping = ACESFilmicToneMapping // 場(chǎng)景中的陰影自動(dòng)更新 this.renderer.shadowMap.enabled = true // 設(shè)置渲染器開(kāi)啟陰影貼圖,并將類型設(shè)為PCFSoftShadowMap this.renderer.shadowMap.type = PCFSoftShadowMap // 這下我們可以看到更亮的材質(zhì),同時(shí)這也影響到環(huán)境貼圖。 this.renderer.outputEncoding = sRGBEncoding // 一個(gè)canvas,渲染器在其上繪制輸出。 this.viewerDom.appendChild(this.renderer.domElement) // 網(wǎng)頁(yè)標(biāo)簽 this.labelRenderer = new CSS2DRenderer() this.labelRenderer.domElement.style.zIndex = 2 this.labelRenderer.domElement.style.position = 'absolute' this.labelRenderer.domElement.style.top = '0px' this.labelRenderer.domElement.style.left = '0px' // 避免HTML標(biāo)簽遮擋三維場(chǎng)景的鼠標(biāo)事件 this.labelRenderer.domElement.style.pointerEvents = 'none' this.viewerDom.appendChild(this.labelRenderer.domElement) // 三維標(biāo)簽 this.css3DRenderer = new CSS3DRenderer() this.css3DRenderer.domElement.style.zIndex = 0 this.css3DRenderer.domElement.style.position = 'absolute' this.css3DRenderer.domElement.style.top = '0px' this.css3DRenderer.domElement.style.left = '0px' // 避免HTML標(biāo)簽遮擋三維場(chǎng)景的鼠標(biāo)事件 this.css3DRenderer.domElement.style.pointerEvents = 'none' this.viewerDom.appendChild(this.css3DRenderer.domElement) }
三、初始化相機(jī)
相機(jī)參數(shù)里面的aspect(攝像機(jī)視錐體長(zhǎng)寬比),其實(shí)應(yīng)該是畫布dom的長(zhǎng)寬比,而不是我們?yōu)g覽器windows的長(zhǎng)寬比,請(qǐng)你仔細(xì)品品這句話。
參數(shù) 構(gòu)造器 PerspectiveCamera( fov : Number, aspect : Number, near : Number,> far : Number ) fov — 攝像機(jī)視錐體垂直視野角度 aspect — 攝像機(jī)視錐體長(zhǎng)寬比 near — 攝像機(jī)視錐體近端面 far — 攝像機(jī)視錐體遠(yuǎn)端面
_initCamera () { // 渲染相機(jī) this.camera = new PerspectiveCamera(55, window.innerWidth / window.innerHeight, 0.1, 5000) this.camera.position.set(50, 0, 50) this.camera.lookAt(0, 0, 0) }
四、初始化場(chǎng)景
_initScene () { // 渲染場(chǎng)景 this.scene = new Scene() this.scene.background = new Color('rgb(5,24,38)') }
五、初始化鼠標(biāo)控制器
控制器主要是用來(lái)控制通過(guò)鼠標(biāo)漫游場(chǎng)景的。
參數(shù) OrbitControls(object:Camera,domElement:HTMLDOMElement) object:(必需)要控制的攝像機(jī)。相機(jī)不得是其他對(duì)象的子對(duì)象,除非該對(duì)象是場(chǎng)景本身。 domElement:用于事件偵聽(tīng)器的HTML元素。
_initControl (option) { this.controls = new OrbitControls(this.camera, this.renderer.domElement) this.controls.enableDamping = false this.controls.maxPolarAngle = Math.PI * 0.46// 垂直軌道多遠(yuǎn) 上限 this.controls.minPolarAngle = Math.PI * 0.3// 你可以垂直軌道多遠(yuǎn),下限 this.controls.screenSpacePanning = false // 定義平移時(shí)如何平移相機(jī)的位置 控制不上下移動(dòng) }
六、燈光初始化
沒(méi)有燈光的話,就會(huì)一片漆黑,所以需要添加燈光。
// 全局光不需要 const ambientLight = new DS.THREE.AmbientLight(0xffffff, 0.2) viewer.scene.add(ambientLight)
七、全局渲染的函數(shù)-逐幀獲取頁(yè)面大小
這里是全局渲染的函數(shù),通過(guò)requestAnimationFrame函數(shù),可以逐幀去渲染場(chǎng)景。 攝像機(jī)視錐體的長(zhǎng)寬比,設(shè)置渲染器的長(zhǎng)寬比,都是這里一直去獲取傳入的div,而不是window的大小,設(shè)置參數(shù)(雖然這樣寫會(huì)很耗費(fèi)性能,但是我也沒(méi)有找到更好的方法去監(jiān)聽(tīng)頁(yè)面的大小變化)。
function animate () { requestAnimationFrame(animate) that._undateDom() that._readerDom() // 全局的公共動(dòng)畫函數(shù),添加函數(shù)可同步執(zhí)行 that.animateEventList.forEach(event => { event.fun && event.content && event.fun(event.content) }) } animate() // 更新dom大小 _undateDom () { const that = this that.controls.update() // 攝像機(jī)視錐體的長(zhǎng)寬比,通常是使用畫布的寬/畫布的高,所以這里的畫布指的是dom,就是一個(gè)div,而不是window that.camera.aspect = that.viewerDom.clientWidth / that.viewerDom.clientHeight // 更新攝像機(jī)投影矩陣。在任何參數(shù)被改變以后必須被調(diào)用,來(lái)使得這些改變生效 that.camera.updateProjectionMatrix() //設(shè)置渲染器的長(zhǎng)寬比,所以就不需要在代碼里面去寫window的頁(yè)面監(jiān)聽(tīng),來(lái)改變頁(yè)面大小【】 that.renderer.setSize(that.viewerDom.clientWidth, that.viewerDom.clientHeight) //that.renderer.setPixelRatio(window.devicePixelRatio) // 設(shè)置設(shè)備像素比 that.labelRenderer.setSize(that.viewerDom.clientWidth, that.viewerDom.clientHeight) that.css3DRenderer.setSize(that.viewerDom.clientWidth, that.viewerDom.clientHeight) } // 渲染dom _readerDom () { this.renderer.render(this.scene, this.camera) this.labelRenderer.render(this.scene, this.camera) this.css3DRenderer.render(this.css3dScene, this.camera) }
八、全局動(dòng)畫函數(shù)
這里我做了一個(gè)全局所有動(dòng)畫的管理器,頁(yè)面上所有的需要?jiǎng)赢嫷暮瘮?shù)都可以傳入運(yùn)行,包括模型的動(dòng)畫、加載水面的運(yùn)動(dòng)、貼圖的UV動(dòng)畫。
動(dòng)畫函數(shù)數(shù)組用來(lái)存儲(chǔ)所有的動(dòng)畫函數(shù)
//動(dòng)畫函數(shù)數(shù)組 this.animateEventList = []
對(duì)動(dòng)畫函數(shù)的添加
/** * 添加全局的動(dòng)畫事件 * @param animate 函數(shù)加參數(shù)對(duì)象 * 傳入對(duì)象 = { fun: 函數(shù)名稱, content: 函數(shù)參數(shù) } */ addAnimate (animate) { this.animateEventList.push(animate) }
這里以狀態(tài)監(jiān)視器為例,創(chuàng)建函數(shù),并且添加到全局的動(dòng)畫函數(shù)數(shù)組里面去
/** * 狀態(tài)更新 * @param statsControls */ _statsUpdate (statsControls) { statsControls.update() }
/** * 添加狀態(tài)監(jiān)測(cè) */ addStats () { if (!this.statsControls) this.statsControls = new Stats() this.statsControls.dom.style.position = 'absolute' this.viewerDom.appendChild(this.statsControls.dom) // 添加到動(dòng)畫 this.statsUpdateObject = { fun: this._statsUpdate, // 函數(shù)名稱,函數(shù)在上面 content: this.statsControls // 綁定傳入的參數(shù) } this.addAnimate(this.statsUpdateObject) }
對(duì)于函數(shù)的移除
/** * 移除全局的動(dòng)畫事件 * @param animate 函數(shù)加參數(shù)對(duì)象 * 傳入對(duì)象 = { fun: 函數(shù)名稱, content: 函數(shù)參數(shù) } */ removeAnimate (animate) { this.animateEventList.map((val, i) => { if (val === animate) this.animateEventList.splice(i, 1) }) }
以移除狀態(tài)監(jiān)視器為例
/** * 移除狀態(tài)檢測(cè) */ removeStats () { if (this.statsControls) this.viewerDom.removeChild(this.statsControls.dom) // 添加到動(dòng)畫 this.statsUpdateObject = { fun: this._statsUpdate, content: this.statsControls } this.removeAnimate(this.statsUpdateObject) }
九、銷毀頁(yè)面
beforeDestroy () { this.scene.traverse((child) => { if (child.material) { child.material.dispose() } if (child.geometry) { child.geometry.dispose() } child = null }) this.renderer.forceContextLoss() this.renderer.dispose() this.scene.clear() }
以上就是針對(duì)核心Viewer庫(kù)封裝的一些記錄,也不知道寫得行不行,但是里面寫得一些也是在工作應(yīng)用當(dāng)中遇到的一些真實(shí)問(wèn)題,更多關(guān)于ThreeJS核心庫(kù)Viewer封裝DS-SDK的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
TypeScript十大排序算法插入排序?qū)崿F(xiàn)示例詳解
這篇文章主要為大家介紹了TypeScript十大排序算法插入排序?qū)崿F(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02TypeScript快速學(xué)習(xí)入門基礎(chǔ)語(yǔ)法
TypeScript的基礎(chǔ)語(yǔ)法,包括變量聲明、復(fù)合類型(數(shù)組和對(duì)象)、條件控制(if-else和switch)、循環(huán)(for和while)、函數(shù)(基礎(chǔ)和箭頭函數(shù),以及可選參數(shù))、面向?qū)ο筇匦裕杜e、接口、繼承)以及模塊開(kāi)發(fā)中的導(dǎo)出和導(dǎo)入2024-07-07typescript快速上手的基礎(chǔ)知識(shí)篇
靜態(tài)類型的typescript與傳統(tǒng)動(dòng)態(tài)弱類型語(yǔ)言javascript不同,在執(zhí)行前會(huì)先編譯成javascript,因?yàn)樗鼜?qiáng)大的type類型系統(tǒng)加持,能讓我們?cè)诰帉懘a時(shí)增加更多嚴(yán)謹(jǐn)?shù)南拗啤W⒁?,它并不是一門全新的語(yǔ)言,所以并沒(méi)有增加額外的學(xué)習(xí)成本2022-12-12TypeScript開(kāi)發(fā)HapiJS應(yīng)用詳解
這篇文章主要為大家介紹了TypeScript開(kāi)發(fā)HapiJS應(yīng)用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08TypeScript實(shí)現(xiàn)十大排序算法之歸并排序示例詳解
這篇文章主要為大家介紹了TypeScript實(shí)現(xiàn)十大排序算法之歸并排序示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02TypeScript實(shí)現(xiàn)類型安全的EventEmitter
這篇文章主要為大家介紹了TypeScript實(shí)現(xiàn)類型安全的EventEmitter示例詳解有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03Typescript是必須要學(xué)習(xí)嗎?如何學(xué)習(xí)TS全棧開(kāi)發(fā)
Typescript目前在前端,網(wǎng)站,小程序中的位置基本無(wú)可替代,同時(shí)也可以構(gòu)建完美的CLI應(yīng)用。在移動(dòng),桌面,后端方面,性能不是要求很高的情況下完全可以勝任,并且在區(qū)塊鏈,嵌入式,人工智能方面也開(kāi)始茁壯成長(zhǎng)。2022-12-12