Guía de desarrollo con WebGPU
Esta guía explica cómo usar WebGPU para renderizar escenas 3DGS en tu proyecto y en qué se diferencia de la integración con WebGL.
Compatibilidad de motor
La canalización WebGPU solo es compatible con el motor Three.js. CesiumJS no admite el renderizado con WebGPU.
Requisito de versión
Versión del SDK: SDK v0.6.4+
Limitaciones del formato de datos
Actualmente, la canalización WebGPU solo admite el formato .lcc2. El formato .lcc todavía no admite el renderizado con WebGPU; la compatibilidad se añadirá en próximas versiones.
Si se proporciona una ruta de datos .lcc, debes recurrir al renderizador WebGL. El código de ejemplo implementa esta lógica mediante el parámetro forceWebGL:
const forceWebGL = new URL(dataPath, location.origin).pathname.endsWith('.lcc');
const renderer = await createRenderer(canvas, forceWebGL);
Limitaciones de WebXR
Actualmente, el SDK solo admite WebXR en la canalización WebGL. La canalización WebGPU no admite WebXR.
Limitaciones en dispositivos móviles
No se recomienda usar WebGPU en dispositivos móviles. Actualmente, la canalización WebGL del SDK ofrece un mejor rendimiento en móviles.
Rutas de renderizado de Three.js
Three.js dispone de tres rutas de renderizado. Comprender sus diferencias es fundamental para integrar correctamente el SDK.
Ruta 1: WebGLRenderer clásico (ampliamente utilizado)
WebGLRenderer es el renderizador principal desde la creación de Three.js y sigue siendo la opción más utilizada. Se basa directamente en la API WebGL 2 y su sistema de materiales utiliza shaders GLSL.
Importación:
import * as THREE from 'three';
const renderer = new THREE.WebGLRenderer();
El SDK es compatible con esta ruta, con los formatos .lcc y .lcc2.
Ruta 2: Nueva arquitectura WebGPURenderer + backend WebGPU
WebGPURenderer es el renderizador de nueva generación de Three.js y utiliza una nueva arquitectura de backend abstracta. Introduce un sistema de materiales basado en nodos (Node Material) y TSL (Three.js Shading Language). Los shaders escritos en TSL se compilan automáticamente a WGSL o GLSL según el backend. Cuando el navegador admite WebGPU, WebGPURenderer utiliza el backend WebGPU nativo.
Importación:
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();
El SDK es compatible con esta ruta, solo con el formato .lcc2.
Ruta 3: Nueva arquitectura WebGPURenderer + backend WebGL 2
WebGPURenderer incluye un backend WebGL 2 integrado como alternativa. Cuando el navegador no admite WebGPU, WebGPURenderer cambia automáticamente al backend WebGL 2. Aunque sigue usando WebGL 2, el sistema de materiales y la canalización de shaders son los mismos que en la ruta 2 (Node Material + TSL), con GLSL como destino final de compilación.
El SDK no es compatible con esta ruta.
Comparación de rutas
| Ruta | Renderizador | API gráfica | Sistema de materiales / shaders | Compatibilidad del SDK | Formatos admitidos |
|---|---|---|---|---|---|
| 1. WebGL clásico | WebGLRenderer | WebGL 2 | GLSL (ShaderMaterial) | ✅ | .lcc .lcc2 |
| 2. Nueva arquitectura WebGPU | WebGPURenderer | WebGPU | Node Material / TSL / WGSL | ✅ | .lcc2 |
| 3. Nueva arquitectura con alternativa WebGL | WebGPURenderer | WebGL 2 (backend alternativo) | Node Material / TSL / GLSL | ❌ | - |
Diferencias entre la integración con WebGPU y con WebGL
En comparación con la integración con WebGL, los cambios para WebGPU se concentran en los siguientes aspectos. El resto del código de negocio (control de cámara, parámetros de rendimiento, bucle de renderizado, etc.) es idéntico.
1. Importaciones adicionales: three/webgpu y three/tsl
WebGL solo necesita three. WebGPU necesita dos módulos adicionales:
<!-- 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. Creación asíncrona del renderizador
El renderizador WebGL se crea de forma síncrona. El renderizador WebGPU requiere una inicialización asíncrona:
// WebGL (síncrono)
const renderer = new THREE.WebGLRenderer({ canvas, antialias: false });
// WebGPU (asíncrono)
const renderer = new THREE_WEBGPU.WebGPURenderer({ canvas, antialias: false });
await renderer.init();
Se recomienda usar la función auxiliar createRenderer incluida en los ejemplos del SDK, que implementa una cadena de alternativas completa:
- El navegador no admite
navigator.gpu→ recurrir aWebGLRenderer - No se puede obtener el adaptador de GPU → recurrir a
WebGLRenderer WebGPURendererrecurre de forma silenciosa al backend WebGL 2 tras la inicialización (sinGPUDevice) → destruirlo y recurrir aWebGLRenderer- Se superan todas las comprobaciones → devolver un renderizador WebGPU real
import { createRenderer } from './engine/three/util.js';
const renderer = await createRenderer(canvas, forceWebGL);
const isWebGPU = renderer.isWebGPURenderer === true;
3. Desactivar la salida sRGB y el mapeo de tonos
En el modo WebGPU debes desactivar la conversión de salida sRGB predeterminada y el mapeo de tonos de Three.js. La salida sRGB predeterminada añade un paso extra de conversión lineal → sRGB que entra en conflicto con la mezcla alfa premultiplicada de 3DGS y provoca desviaciones de color:
if (isWebGPU) {
renderer.outputColorSpace = THREE.LinearSRGBColorSpace;
renderer.toneMapping = THREE.NoToneMapping;
}
En el modo WebGL no es necesario.
4. Parámetros de LCCRender.load
WebGPU requiere los parámetros adicionales renderLibWebgpu y 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, // nuevo
renderLibTSL: THREE_TSL, // nuevo
canvas: renderer.domElement,
// ...
});
Configuración de los límites del dispositivo GPU
Al crear un WebGPURenderer, se recomienda consultar los límites reales del adaptador de GPU y pasarlos en requiredLimits para aprovechar al máximo las capacidades 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();
Ejemplo completo de 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 solo admite .lcc2; forzar WebGL para datos .lcc
const forceWebGL = new URL(dataPath, location.origin).pathname.endsWith('.lcc');
const scene = new THREE.Scene();
const canvas = document.createElement('canvas');
document.body.appendChild(canvas);
// Crear el renderizador con alternativa automática 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);
// Desactivar la salida sRGB en WebGPU para evitar conflictos de espacio de color 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);
// Cargar la escena 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 // sustitúyelo por tu clave
},
(mesh) => { console.log('Loaded', mesh); },
(percent) => { console.log('Loading ' + (percent * 100).toFixed(1) + '%'); },
() => { console.log('Loading failed'); }
);
// Configurar los parámetros de rendimiento
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>