使用IndexedDB快取給WebGL三維程式加速
- 2022 年 2 月 9 日
- 筆記
前言
使用webgl開發三維應用的時候,經常會發現三維場景載入比較慢,往往需要等待挺長時間,這樣用戶的體驗就很不友好。 造成載入慢的原因,主要是三維應用涉及到的資源文件會特別多,這些資源文件主要是模型及其圖片,往往這些模型和圖片都會比較大。
為了加快三維場景的加快速度,可以使用IndexedDB在客戶端進行資源快取。IndexedDB,即客戶端持久化資料庫!使用本快取技術,在初次訪問後,3D場景中的文件級別數據將寫入訪問設備本地快取資料庫,在客戶端實現永久的生命周期,清除瀏覽器快取也不影響已快取的3D模型文件。
IndexedDB介紹
IndexedDB
是一個前端數據持久化解決方案(即前端快取),由瀏覽器實現。
IndexedDB又如下特點
- 基於文件存儲。意味著其容量可達到硬碟可用空間上限
- 非關係型資料庫。意味著擴展或收縮欄位一般無須修改資料庫和表結構(除非新增欄位用做索引)
- 鍵值對存儲。意味著存取無須字元串轉換過程
- 存儲類型豐富。意味著瀏覽器快取中不再是只能存字元串了
- 非同步: 意味著所有操作都要在回調中進行
本地瀏覽器擁有三種永久存儲數據技術,分別為Web Storage、IndexedDB、Web SQL。IndexedDB具備查詢高效、存儲空間大和非同步操作等技術特徵,有巨大的優勢。
存儲空間大。IndexedDB 的儲存空間比 LocalStorage 大得多,一般來說不少於 250MB,甚至沒有上限。在HTML5本地存儲中,IndexedDB存儲的數據則是最多的。
查詢高效。IndexedDB是一種輕量級NOSQL資料庫,是由瀏覽器自帶。相比Web Sql更加高效,包括索引、事務處理和查詢功能。
非同步操作。 IndexedDB 操作時不會鎖死瀏覽器,用戶依然可以進行其他操作,這與 LocalStorage 形成對比,後者的操作是同步的。非同步設計是為了防止大量數據的讀寫,拖慢網頁的表現。
與此同時,IndexedDB 內部採用對象倉庫存放數據。所有類型的數據都可以直接存入,包括 JavaScript 對象,滿足了三維場景的存儲需要。
因此 使用IndexedDB快取是一種最為優異的前端快取方案。像Babylon.js,其引擎層面已經支援了IndexedDB快取。可以參考如下文檔:
//doc.babylonjs.com/divingDeeper/scene/optimizeCached。
three.js使用IndexedDB的思路
有關具體如何使用IndexedDB,有很多資料進行介紹,此文不在贅述。
使用IndexedDB快取模型資源,首先需要獲取模型相關的資源,這些模型資源包括模型文件以及相關的圖片文件。 比如對於GLTF模型而言,其資源包括.gltf的模型主文件,.bin格式的文件,紋理貼圖文件等等。 首次載入加一個模型的時候,肯定是載入網路上的資源文件,通過threejs的LoadingMananger可以收集一個gltf模型的各種資源文件。 程式碼如下:
const resourceCollector = [];
const loadingManager = new LoadingManager();
loadingManager.setURLModifier( (url,path) => {
console.log(url);
if(url.startsWith("data:") || url.startsWith("blob:")) {
return url;
}
resourceCollector.push(url);
return url;
});
上述程式碼resourceCollector收集了載入模型過程中所有的模型資源的地址。 收集之後把所有資源存儲到IndexedDB中:
saveGltfModel:async function(options,resourceCollector){
const gltfUrl = options.gltfPath;
const blobs = {};
for(let i = 0;i < resourceCollector.length;i ++) {
let url = resourceCollector[i];
let blob = await loadAsBlob(url);
blobs[url] = blob;
await addToDatabase("model",{key:url,blob})
}
await addToDatabase("model_info",{key:gltfUrl,content:resourceCollector});
},
其中loadAsBlob是把一個資源載入成為blob對象,程式碼如下:
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.responseType = "blob";
xhr.onerror = function() {reject("Network error.")};
xhr.onload = function() {
if (xhr.status === 200) {resolve(xhr.response)}
else {reject("Loading error:" + xhr.statusText)}
};
xhr.send();
而addToDatabase方法把資源添加到IndexedDB資料庫。
function addToDatabase(storename, data) {
const promise = new Promise( (resolve,reject) => {
let store = database.transaction(storename, 'readwrite').objectStore(storename);
let countReq = store.count(data.key);
countReq.onsuccess = function(event) {
console.log("count:",event.target.result);
let count = event.target.result;
if(count == 0) {
let request = store.add(data);
request.onerror = function (event) {
console.error('add添加資料庫中已有該數據')
reject(event);
};
request.onsuccess = function (event) {
console.log('add添加數據已存入資料庫')
resolve(event);
};
}
};
});
}
下一次獲取模型的時候,可以先判斷是否以及本地存儲,如果已經本地存儲,就可以直接從本地獲取模型資源:
if(this.indexDbCache && indexedDB) {
if(database == null) {
database = await initialDB();
}
const storeObject = await findInDatabase("model_info",key);
if(storeObject) {
return this.loadGltfInDb(options);
}
}
快取效果測評
通過測試可以發現對於比較大的場景,模型載入的速度可以提高几倍,十幾倍甚至幾十倍。 由此可見,IndexedDB快取效果很明顯。
如果對可視化感興趣,可以和我交流,微信541002349。
關注公號「ITMan彪叔」 可以及時收到更多有價值的文章。