Three.js渲染模型卡頓問(wèn)題的一些優(yōu)化辦法
事先說(shuō)明
優(yōu)化方法是根據(jù)chatGPT的回答下,我這里記錄一下,有的方法進(jìn)行了嘗試,有的還沒(méi)有。
1、模型面數(shù)過(guò)多導(dǎo)致渲染卡頓
可以通過(guò)減少面數(shù)來(lái)優(yōu)化,也可以使用LOD技術(shù)(Level of Detail)在不同距離下使用不同的模型細(xì)節(jié)來(lái)優(yōu)化。
使用LOD技術(shù)可以在不同距離下使用不同的模型細(xì)節(jié)來(lái)優(yōu)化three.js渲染性能,下面是具體步驟:
創(chuàng)建多個(gè)模型,每個(gè)模型的面數(shù)和細(xì)節(jié)不同,這些模型應(yīng)該是同一個(gè)對(duì)象的不同版本。
將這些模型按照從低到高的細(xì)節(jié)順序添加到同一個(gè)
LOD
(Level of Detail)對(duì)象中,如下所示:
const lod = new THREE.LOD(); const lowDetailModel = ... // 低細(xì)節(jié)模型 const midDetailModel = ... // 中細(xì)節(jié)模型 const highDetailModel = ... // 高細(xì)節(jié)模型 lod.addLevel(lowDetailModel, 0); // 添加低細(xì)節(jié)模型,距離為0 lod.addLevel(midDetailModel, 100); // 添加中細(xì)節(jié)模型,距離為100 lod.addLevel(highDetailModel, 200); // 添加高細(xì)節(jié)模型,距離為200
- 將
LOD
對(duì)象添加到場(chǎng)景中。
scene.add(lod);
- 在渲染循環(huán)中,根據(jù)相機(jī)與LOD對(duì)象的距離,自動(dòng)選擇當(dāng)前需要顯示的模型細(xì)節(jié)等級(jí)。可以使用
THREE.LOD
對(duì)象的update
方法來(lái)實(shí)現(xiàn)。
function render() { requestAnimationFrame(render); lod.update(camera); renderer.render(scene, camera); }
2、材質(zhì)貼圖過(guò)大導(dǎo)致渲染卡頓
可以通過(guò)減小貼圖尺寸,壓縮貼圖格式,使用紋理集(Texture Atlas)等方式來(lái)優(yōu)化。
使用紋理集(Texture Atlas)可以將多張小紋理圖合并成一張大紋理圖,從而減少渲染時(shí)的紋理切換次數(shù),優(yōu)化three.js渲染性能,下面是具體步驟:
- 創(chuàng)建一張大紋理圖,并將多張小紋理圖拼接在一起,這些小紋理圖應(yīng)該是同一對(duì)象的不同部分,如下所示:
const texture = new THREE.TextureLoader().load('atlas.png'); const material = new THREE.MeshBasicMaterial({ map: texture });
- 將每個(gè)物體的UV坐標(biāo)映射到對(duì)應(yīng)的小紋理圖區(qū)域,需要根據(jù)小紋理圖在大紋理圖中的位置和大小計(jì)算出UV坐標(biāo),如下所示:
const geometry = new THREE.BoxGeometry(1, 1, 1); const uvAttribute = geometry.attributes.uv; for (let i = 0; i < uvAttribute.count; i++) { const u = uvAttribute.getX(i); const v = uvAttribute.getY(i); // 根據(jù)小紋理圖在大紋理圖中的位置和大小計(jì)算出UV坐標(biāo) uvAttribute.setXY(i, u * smallTextureWidth / bigTextureWidth + smallTextureX / bigTextureWidth, v * smallTextureHeight / bigTextureHeight + smallTextureY / bigTextureHeight); }
- 在渲染循環(huán)中,更新大紋理圖的偏移和縮放值。
function render() { requestAnimationFrame(render); const time = Date.now() * 0.001; texture.offset.x = time * 0.1; // x方向偏移量 texture.offset.y = time * 0.2; // y方向偏移量 texture.repeat.set(2, 2); // 橫向和縱向縮放值 renderer.render(scene, camera); }
3、著色器復(fù)雜度過(guò)高導(dǎo)致渲染卡頓
可以通過(guò)簡(jiǎn)化著色器,使用預(yù)編譯的著色器,使用Instancing
等方式來(lái)優(yōu)化。
使用Instancing(實(shí)例化)可以將多個(gè)相同的物體復(fù)用同一個(gè)幾何體和材質(zhì),并在渲染時(shí)進(jìn)行一次性繪制,從而減少渲染調(diào)用次數(shù),優(yōu)化three.js渲染性能,下面是具體步驟:
- 創(chuàng)建一個(gè)幾何體和材質(zhì),將它們分別作為多個(gè)物體的原型。
const geometry = new THREE.BoxGeometry(1, 1, 1); const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
- 創(chuàng)建一個(gè)
InstancedBufferGeometry
對(duì)象,并將原型幾何體的屬性復(fù)制到它的屬性中。
const instances = 10000; // 實(shí)例數(shù)量 const instancedGeometry = new THREE.InstancedBufferGeometry(); instancedGeometry.copy(geometry); // 復(fù)制幾何體屬性 const translations = new Float32Array(instances * 3); // 實(shí)例位置數(shù)組 for (let i = 0; i < instances; i++) { translations[i * 3] = Math.random() * 100 - 50; translations[i * 3 + 1] = Math.random() * 100 - 50; translations[i * 3 + 2] = Math.random() * 100 - 50; } instancedGeometry.setAttribute('translation', new THREE.InstancedBufferAttribute(translations, 3));
- 創(chuàng)建一個(gè)InstancedMesh對(duì)象,并將原型材質(zhì)和實(shí)例化幾何體作為它的參數(shù)。
const instancedMesh = new THREE.InstancedMesh(instancedGeometry, material, instances); scene.add(instancedMesh);
- 在渲染循環(huán)中,更新實(shí)例化幾何體的屬性,即實(shí)例的位置、旋轉(zhuǎn)和縮放等信息。
function render() { requestAnimationFrame(render); const time = Date.now() * 0.001; for (let i = 0; i < instances; i++) { const translation = instancedMesh.geometry.attributes.translation; translation.setXYZ(i, Math.sin(time + i * 0.5) * 5, Math.cos(time + i * 0.3) * 5, i * 0.1); } instancedMesh.geometry.attributes.translation.needsUpdate = true; // 更新實(shí)例位置屬性 renderer.render(scene, camera); }
4、不合理的渲染方式導(dǎo)致渲染卡頓
可以通過(guò)使用合適的渲染方式,如WebGL2渲染,使用Web Worker
等方式來(lái)優(yōu)化。
Ⅰ、使用WebGL2可以在現(xiàn)代瀏覽器中利用新的圖形處理能力,優(yōu)化three.js渲染性能,下面是具體步驟:
① 在渲染器中啟用WebGL2。
const renderer = new THREE.WebGLRenderer({ canvas: canvas, context: canvas.getContext('webgl2') });
② 使用WebGL2支持的新特性,如transform feedback
、instanced arrays
等。
例如,以下代碼演示了如何使用transform feedback來(lái)記錄頂點(diǎn)位置的變化:
const transformFeedback = new THREE.WebGL2TransformFeedback(); const bufferGeometry = new THREE.BufferGeometry(); const positions = new Float32Array([0, 0, 0]); bufferGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); const shader = new THREE.ShaderMaterial({ vertexShader: ` out vec3 transformedPosition; void main() { transformedPosition = position; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, fragmentShader: ` void main() { gl_FragColor = vec4(1.0); } `, transformFeedback: { // 將頂點(diǎn)位置記錄到transformedPosition變量中 varyings: ['transformedPosition'], // 開(kāi)啟transform feedback enabled: true, // 設(shè)置bufferGeometry的位置屬性為transform feedback的輸出屬性 bufferGeometry: bufferGeometry } }); const mesh = new THREE.Mesh(bufferGeometry, shader); scene.add(mesh); function render() { requestAnimationFrame(render); renderer.setRenderTarget(null); // 開(kāi)始transform feedback transformFeedback.begin(); renderer.render(scene, camera); // 結(jié)束transform feedback,并將變化后的頂點(diǎn)位置存儲(chǔ)到bufferGeometry中 transformFeedback.end(); // 更新頂點(diǎn)位置 positions.set(bufferGeometry.getAttribute('position').array); bufferGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); renderer.render(scene, camera); }
---------------------------------------------------------------分隔線-----------------------------------------------------------------
Ⅱ、使用Web Worker可以將計(jì)算密集型的任務(wù)分離到另一個(gè)線程中,從而避免主線程被阻塞,優(yōu)化three.js渲染性能,下面是具體步驟:
① 創(chuàng)建一個(gè)Web Worker,用于處理計(jì)算密集型的任務(wù)。
const worker = new Worker('worker.js');
② 在Web Worker中定義處理函數(shù)。
// worker.js function process(data) { // 計(jì)算密集型的任務(wù) return result; } onmessage = function(event) { const result = process(event.data); postMessage(result); };
③ 在主線程中將任務(wù)發(fā)送到Web Worker,并設(shè)置回調(diào)函數(shù)處理返回結(jié)果。
function render() { requestAnimationFrame(render); // 發(fā)送任務(wù)到Web Worker worker.postMessage(data); worker.onmessage = function(event) { const result = event.data; // 處理返回結(jié)果 }; renderer.render(scene, camera); }
通過(guò)以上步驟,就可以使用Web Worker來(lái)將計(jì)算密集型的任務(wù)分離到另一個(gè)線程中,從而避免主線程被阻塞,優(yōu)化three.js渲染性能。需要注意的是,Web Worker中無(wú)法直接訪問(wèn)主線程的DOM和three.js對(duì)象,需要通過(guò)消息傳遞來(lái)實(shí)現(xiàn)通信。
5、CPU和GPU資源不平衡導(dǎo)致渲染卡頓
可以通過(guò)分析性能監(jiān)控,優(yōu)化代碼邏輯,使用requestAnimationFrame
等方式來(lái)平衡CPU和GPU資源占用。
使用requestAnimationFrame可以讓瀏覽器根據(jù)自身的渲染節(jié)奏調(diào)整動(dòng)畫(huà)的幀率,從而避免過(guò)度渲染,優(yōu)化three.js渲染性能,下面是具體步驟:
- 將渲染函數(shù)作為requestAnimationFrame的回調(diào)函數(shù)。
function render() { // 渲染代碼 renderer.render(scene, camera); // 請(qǐng)求下一幀動(dòng)畫(huà) requestAnimationFrame(render); }
- 在初始化時(shí)調(diào)用一次requestAnimationFrame,啟動(dòng)動(dòng)畫(huà)。
var animationId = requestAnimationFrame(render);
- 在動(dòng)畫(huà)結(jié)束時(shí),記得停止requestAnimationFrame,以避免不必要的資源消耗。
function stop() { cancelAnimationFrame(animationId); }
需要注意的是,使用requestAnimationFrame時(shí)需要避免在渲染循環(huán)中進(jìn)行過(guò)多的計(jì)算,以免影響渲染性能。
總結(jié)
到此這篇關(guān)于Three.js渲染模型卡頓問(wèn)題的一些優(yōu)化辦法的文章就介紹到這了,更多相關(guān)Three.js渲染模型卡頓內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
產(chǎn)制造追溯系統(tǒng)之通過(guò)微信小程序?qū)崿F(xiàn)移動(dòng)端報(bào)表平臺(tái)
這篇文章主要介紹了產(chǎn)制造追溯系統(tǒng)-通過(guò)微信小程序?qū)崿F(xiàn)移動(dòng)端報(bào)表平臺(tái) ,本文圖文并茂給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-06-06如何在postman測(cè)試用例中實(shí)現(xiàn)斷言過(guò)程解析
這篇文章主要介紹了如何在postman測(cè)試用例中實(shí)現(xiàn)斷言過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07JavaScript 計(jì)算圖片加載數(shù)量的代碼
先定義一個(gè)圖片的數(shù)組,然后通過(guò)image的onload事件來(lái)計(jì)算,注意,onload在ie和火狐有所不同。2011-01-01利用JS實(shí)現(xiàn)scroll自定義滾動(dòng)效果詳解
這篇文章主要給大家介紹了關(guān)于利用JS如何實(shí)現(xiàn)scroll自定義滾動(dòng)效果的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-10-10使用JS獲取input file的路徑C:\fakepath\問(wèn)題及解決方法
這篇文章主要介紹了使用JS獲取input file的路徑C:\fakepath\問(wèn)題及解決方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2024-01-01詳解ES6中的 Set Map 數(shù)據(jù)結(jié)構(gòu)學(xué)習(xí)總結(jié)
這篇文章主要介紹了詳解ES6中的 Set Map 數(shù)據(jù)結(jié)構(gòu)學(xué)習(xí)總結(jié),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-11-11???????基于el-table和el-pagination實(shí)現(xiàn)數(shù)據(jù)的分頁(yè)效果流程詳解
本文主要介紹了???????基于el-table和el-pagination實(shí)現(xiàn)數(shù)據(jù)的分頁(yè)效果,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-11-11