基于 Three.js 的 3D 模型快照生成方案
此方案通過 Three.js 渲染場景并異步生成圖像數據,同時支持分辨率縮放和 Blob 格式輸出,為模型預覽、截圖保存等需求提供完整解決方案。
問題分析:
使用html2canvas 生成的快照畫布顯示為空,通常是因為 html2canvas 在渲染時無法正確捕獲 Three.js 生成的 WebGL 內容。因為 html2canvas 主要設計用于捕獲普通 DOM 元素,而 Three.js 使用 WebGL 上下文渲染 3D 內容,這部分內容不會被直接包含在 DOM 中。
推薦的方案:
使用 Three.js 自身的渲染功能導出圖像
直接使用 Three.js 的渲染器來捕獲圖像,而不是依賴外部庫,這種方法直接從WebGL上下文獲取像素數據,能夠可靠地獲取3D模型的渲染結果。
下面介紹具體實現方法:(默認已經使用threejs搭建完成一個三維場景)
步驟1: 場景渲染與尺寸獲取
renderer.render(scene, camera); // 渲染當前場景
const { width, height } = renderer.getSize(new THREE.Vector2()); // 獲取渲染窗口原始尺寸
步驟2: 臨時畫布創建與縮放處理
const canvasWidth = Math.floor(width * resolutionScale);
const canvasHeight = Math.floor(height * resolutionScale);
const tempCanvas = document.createElement("canvas");
tempCanvas.width = canvasWidth;
tempCanvas.height = canvasHeight;
- 根據resolutionScale計算目標畫布尺寸,使用Math.floor避免浮點尺寸導致的渲染模糊。
- 創建 HTMLCanvasElement 作為臨時畫布,用于后續圖像繪制和數據導出
步驟3: WebGL 內容轉繪至 2D 畫布
const webglCanvas = renderer.domElement; // 獲取WebGL渲染的畫布元素
const destCtx = tempCanvas.getContext("2d");
if (destCtx) {destCtx.drawImage(webglCanvas, 0, 0, canvasWidth, canvasHeight); // 繪制WebGL內容到臨時畫布
}
- 通過renderer.domElement獲取 Three.js 內部使用的 WebGL 畫布(通常為元素)
- 使用 2D 畫布上下文的drawImage方法,將 WebGL 畫布內容繪制到臨時畫布中,并按目標尺寸縮放
步驟4: 異步導出 Blob 數據
tempCanvas.toBlob((blob) => {if (blob) {resolve(blob); // 成功時返回Blob對象} else {reject(new Error("生成Blob失敗")); // 失敗時拋出錯誤}},"image/png", // 輸出格式為png(可改為image/jpeg等)1 // 質量系數(1為最高,僅適用于支持的格式)
);
- 使用畫布的toBlob方法異步生成圖像數據,該方法支持指定格式(如 WebP、PNG)和質量參數
- 通過 Promise 機制處理異步操作結果,成功時解析 Blob,失敗時通過reject傳遞錯誤信息
調用示例
scene.generateSnapshot(1).then((blob) => {if (blob) {//根據需求處理blob對象,以下是本地下載此圖片示例const url = URL.createObjectURL(blob)const a = document.createElement("a")a.href = urla.download = "snapshot.png"a.click()URL.revokeObjectURL(url) // 釋放內存}
})
完整代碼:
function generateSnapshot(resolutionScale: number = 1): Promise<Blob | null> {return new Promise((resolve, reject) => {// 渲染當前場景renderer.render(scene, camera);try {const { width, height } = renderer.getSize(new THREE.Vector2());const canvasWidth = Math.floor(width * resolutionScale);const canvasHeight = Math.floor(height * resolutionScale);let tempCanvas: HTMLCanvasElement = document.createElement("canvas");tempCanvas.width = canvasWidth;tempCanvas.height = canvasHeight;const tempContext = tempCanvas.getContext("2d");if (!tempContext) {throw new Error("無法獲取canvas上下文");}// 將WebGL內容繪制到臨時canvas中const webglCanvas = renderer.domElement;if (tempCanvas instanceof HTMLCanvasElement) {const destCtx = tempCanvas.getContext("2d");if (destCtx) {destCtx.drawImage(webglCanvas, 0, 0, canvasWidth, canvasHeight);}}// 使用 toBlob 異步導出圖片if (tempCanvas instanceof HTMLCanvasElement) {tempCanvas.toBlob((blob) => {if (blob) {resolve(blob); // 返回Blob對象} else {reject(new Error("生成Blob失敗"));}},"image/png",1);} else {reject(new Error("不支持的canvas類型"));}} catch (error) {console.error("生成快照失敗:", error);reject(error);}});}
總結:
實現了 Three.js 場景的異步快照生成,支持分辨率縮放和 Blob 數據輸出,適用于模型預覽截圖、數據存檔等場景。