Guida allo sviluppo con WebGPU
Questa guida spiega come utilizzare WebGPU per eseguire il rendering di scene 3DGS nel tuo progetto e quali sono le differenze rispetto all'integrazione con WebGL.
Supporto motore
La pipeline WebGPU supporta solo il motore Three.js. CesiumJS non supporta il rendering WebGPU.
Requisito di versione
Versione dell'SDK: SDK v0.6.4+
Limitazioni sul formato dei dati
Attualmente la pipeline WebGPU supporta solo il formato .lcc2. Il formato .lcc non supporta ancora il rendering WebGPU; il supporto verrà aggiunto nelle prossime versioni.
Se viene fornito un percorso dati .lcc, è necessario ripiegare sul renderer WebGL. Il codice di esempio implementa questa logica tramite il parametro forceWebGL:
const forceWebGL = new URL(dataPath, location.origin).pathname.endsWith('.lcc');
const renderer = await createRenderer(canvas, forceWebGL);
Limitazioni di WebXR
Attualmente l'SDK supporta WebXR solo nella pipeline WebGL. La pipeline WebGPU non supporta WebXR.
Limitazioni sui dispositivi mobili
WebGPU non è consigliato sui dispositivi mobili. Attualmente la pipeline WebGL dell'SDK offre prestazioni migliori su mobile.
Percorsi di rendering di Three.js
Three.js dispone di tre percorsi di rendering. Comprenderne le differenze è fondamentale per integrare correttamente l'SDK.
Percorso 1: WebGLRenderer classico (ampiamente utilizzato)
WebGLRenderer è il renderer principale fin dalla nascita di Three.js e rimane la scelta più diffusa. È costruito direttamente sull'API WebGL 2 e il suo sistema di materiali utilizza shader GLSL.
Importazione:
import * as THREE from 'three';
const renderer = new THREE.WebGLRenderer();
L'SDK supporta questo percorso, con entrambi i formati .lcc e .lcc2.
Percorso 2: Nuova architettura WebGPURenderer + backend WebGPU
WebGPURenderer è il renderer di nuova generazione di Three.js e si basa su una nuova architettura di backend astratta. Introduce un sistema di materiali basato su nodi (Node Material) e TSL (Three.js Shading Language). Gli shader scritti in TSL vengono compilati automaticamente in WGSL o GLSL a seconda del backend. Quando il browser supporta WebGPU, WebGPURenderer utilizza il backend WebGPU nativo.
Importazione:
import * as THREE from 'three';
import * as THREE_WEBGPU from 'three/webgpu';
import * as THREE_TSL from 'three/tsl';
const renderer = new THREE_WEBGPU.WebGPURenderer({ canvas });
await renderer.init();
L'SDK supporta questo percorso, solo con il formato .lcc2.
Percorso 3: Nuova architettura WebGPURenderer + backend WebGL 2
WebGPURenderer include un backend WebGL 2 integrato come fallback. Quando il browser non supporta WebGPU, WebGPURenderer passa automaticamente al backend WebGL 2. Pur utilizzando ancora WebGL 2, il sistema di materiali e la pipeline degli shader sono gli stessi del percorso 2 (Node Material + TSL), con GLSL come destinazione finale della compilazione.
L'SDK non supporta questo percorso.
Confronto tra i percorsi
| Percorso | Renderer | API grafica | Sistema di materiali / shader | Supporto SDK | Formati supportati |
|---|---|---|---|---|---|
| 1. WebGL classico | WebGLRenderer | WebGL 2 | GLSL (ShaderMaterial) | ✅ | .lcc .lcc2 |
| 2. Nuova architettura WebGPU | WebGPURenderer | WebGPU | Node Material / TSL / WGSL | ✅ | .lcc2 |
| 3. Nuova architettura con fallback WebGL | WebGPURenderer | WebGL 2 (fallback del backend) | Node Material / TSL / GLSL | ❌ | - |
Differenze tra l'integrazione WebGPU e WebGL
Rispetto all'integrazione con WebGL, le modifiche per WebGPU si concentrano nei punti seguenti. Il resto del codice applicativo (controllo della fotocamera, parametri delle prestazioni, ciclo di rendering, ecc.) rimane identico.
1. Importazioni aggiuntive: three/webgpu e three/tsl
WebGL richiede solo three. WebGPU richiede due moduli aggiuntivi:
<!-- importmap -->
<script type="importmap">
{
"imports": {
"three": "./engine/three/three.module.js",
"three/webgpu": "./engine/three/three.webgpu.js",
"three/tsl": "./engine/three/three.tsl.js",
"three/addons/": "./engine/three/jsm/"
}
}
</script>
import * as THREE from 'three';
import * as THREE_WEBGPU from 'three/webgpu';
import * as THREE_TSL from 'three/tsl';
2. Creazione asincrona del renderer
Il renderer WebGL viene creato in modo sincrono. Il renderer WebGPU richiede un'inizializzazione asincrona:
// WebGL (sincrono)
const renderer = new THREE.WebGLRenderer({ canvas, antialias: false });
// WebGPU (asincrono)
const renderer = new THREE_WEBGPU.WebGPURenderer({ canvas, antialias: false });
await renderer.init();
Si consiglia di utilizzare la funzione di utilità createRenderer fornita negli esempi dell'SDK, che implementa una catena di fallback completa:
- Il browser non supporta
navigator.gpu→ fallback suWebGLRenderer - Impossibile ottenere l'adattatore GPU → fallback su
WebGLRenderer - Dopo l'inizializzazione
WebGPURendererripiega silenziosamente sul backend WebGL 2 (nessunGPUDevice) → distruggerlo e fare fallback suWebGLRenderer - Tutti i controlli superati → restituire un vero renderer WebGPU
import { createRenderer } from './engine/three/util.js';
const renderer = await createRenderer(canvas, forceWebGL);
const isWebGPU = renderer.isWebGPURenderer === true;
3. Disattivare l'output sRGB e il tone mapping
In modalità WebGPU è necessario disattivare la conversione di output sRGB predefinita e il tone mapping di Three.js. L'output sRGB predefinito aggiunge un passaggio di conversione lineare → sRGB aggiuntivo che entra in conflitto con il blending ad alpha premoltiplicato di 3DGS, causando colori non accurati:
if (isWebGPU) {
renderer.outputColorSpace = THREE.LinearSRGBColorSpace;
renderer.toneMapping = THREE.NoToneMapping;
}
In modalità WebGL non è necessario.
4. Parametri di LCCRender.load
WebGPU richiede i parametri aggiuntivi renderLibWebgpu e renderLibTSL:
// WebGL
LCCRender.load({
camera, scene, renderer,
dataPath,
renderLib: THREE,
canvas: renderer.domElement,
// ...
});
// WebGPU
LCCRender.load({
camera, scene, renderer,
dataPath,
renderLib: THREE,
renderLibWebgpu: THREE_WEBGPU, // nuovo
renderLibTSL: THREE_TSL, // nuovo
canvas: renderer.domElement,
// ...
});
Configurazione dei limiti del dispositivo GPU
Quando si crea un WebGPURenderer, si consiglia di interrogare i limiti effettivi dell'adattatore GPU e passarli in requiredLimits per sfruttare appieno le capacità del dispositivo:
const adapter = await navigator.gpu.requestAdapter();
const requiredLimits = {
maxBufferSize: adapter.limits.maxBufferSize,
maxStorageBufferBindingSize: adapter.limits.maxStorageBufferBindingSize,
maxTextureDimension2D: adapter.limits.maxTextureDimension2D
};
const renderer = new THREE_WEBGPU.WebGPURenderer({
canvas,
antialias: false,
requiredLimits
});
await renderer.init();
Esempio completo WebGPU
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>LCC Three.js WebGPU Demo</title>
<style>
html, body { margin: 0; padding: 0; height: 100%; overflow: hidden; }
canvas { display: block; }
</style>
</head>
<body>
<script type="importmap">
{
"imports": {
"three": "./engine/three/three.module.js",
"three/webgpu": "./engine/three/three.webgpu.js",
"three/tsl": "./engine/three/three.tsl.js",
"three/addons/": "./engine/three/jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import * as THREE_WEBGPU from 'three/webgpu';
import * as THREE_TSL from 'three/tsl';
import { createRenderer } from './engine/three/util.js';
import { LCCRender } from '../sdk/lcc-sdk-three.js';
const dataPath = 'https://your-server.com/assets/scene/meta.lcc2';
// WebGPU supporta solo .lcc2; per i dati .lcc forzare WebGL
const forceWebGL = new URL(dataPath, location.origin).pathname.endsWith('.lcc');
const scene = new THREE.Scene();
const canvas = document.createElement('canvas');
document.body.appendChild(canvas);
// Creare il renderer con fallback automatico WebGPU → WebGL
const renderer = await createRenderer(canvas, forceWebGL);
const isWebGPU = renderer.isWebGPURenderer === true;
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setClearColor(0x000000);
// Disattivare l'output sRGB in WebGPU per evitare conflitti di spazio colore con 3DGS
if (isWebGPU) {
renderer.outputColorSpace = THREE.LinearSRGBColorSpace;
renderer.toneMapping = THREE.NoToneMapping;
}
const camera = new THREE.PerspectiveCamera(
45, window.innerWidth / window.innerHeight, 0.1, 150000
);
camera.position.set(0, 2, 0);
// Caricare la scena 3DGS
const lccObj = LCCRender.load({
camera, scene, renderer,
dataPath,
renderLib: THREE,
renderLibWebgpu: THREE_WEBGPU,
renderLibTSL: THREE_TSL,
canvas: renderer.domElement,
useEnv: true,
useIndexDB: true,
useLoadingEffect: true,
appKey: null // sostituire con la propria chiave
},
(mesh) => { console.log('Loaded', mesh); },
(percent) => { console.log('Loading ' + (percent * 100).toFixed(1) + '%'); },
() => { console.log('Loading failed'); }
);
// Configurare i parametri delle prestazioni
lccObj.setStartLod(0);
lccObj.setMaxSplats(8000000);
lccObj.setMaxNodeSplats(4000000);
lccObj.setMaxDistance(200);
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
renderer.setAnimationLoop(() => {
LCCRender.update();
renderer.render(scene, camera);
});
</script>
</body>
</html>