De Blender a producción: un walkthrough de compresión end-to-end
Llegados a este punto de la serie, herramientas, principios y selección están cubiertos. En este último artículo atamos todo en un pipeline que corre: partiendo de un modelo real de Blender, comprimimos paso a paso, registrando tamaño de archivo, VRAM y tiempo de carga en cada paso, y al final vemos si puede encoger de un pesado de 50 MB a un modelo de 5 MB que abre al instante en el móvil.
Lector objetivo: quien ha leído los cinco anteriores y está listo para hacer de verdad. Aquí no hay conceptos nuevos — solo flujos, comandos y scripts copiables.
Punto de partida: un modelo PBR real
Usamos como muestra un modelo de exhibición e-commerce muy típico: un modelo de producto de alta precisión con maps PBR completos.
| Métrica inicial | Valor |
|---|---|
| Archivo fuente de Blender | ~120 MB (incl. alta poligonal no exportada) |
| GLB exportado (float32 + PNG) | ~50 MB |
| Número de vértices | ~180k |
| Maps | 6 × 4096×4096 (albedo, normal, roughness, metallic, AO, emissive) |
| Uso de VRAM (6 descomprimidas) | ~520 MB |
| Objetivo | Archivo ≤ 5 MB, VRAM controlable, apertura instantánea en móvil |
Un archivo de 50 MB y 520 MB de VRAM — este modelo crashea seguro en móvil. Vamos paso a paso.
Paso 0: exportar correctamente desde Blender
La primera puerta de la compresión es en realidad el export; muchos sangran aquí.
Ajustes clave al exportar glTF desde Blender:
- Formato:
glTF Binary (.glb)(archivo único, fácil de transferir) - Geometría: marcar
Normals,Tangents(los normal maps PBR necesitan tangentes) - UV: asegúrate de exportar (activado por defecto)
- Texturas:
AutomaticoJPEG(el formato de textura aquí no importa, recompimiremos después — pero que estén exportadas) - Compresión: no marcar la compresión de malla integrada de Blender; usaremos herramientas más especializadas
- Transformación:
+Y Up(estándar glTF) - Datos: marca solo lo necesario (animación, cámaras, luces si no se necesitan, no exportar, para reducir tamaño)
Tras el export, model.glb: 50 MB, 6 maps PNG, vértices float32. Esa es nuestra base.
La primera trampa común está justo aquí: Blender exporta por defecto también mallas no usadas y objetos auxiliares ocultos. Antes de exportar, ejecuta
File > Clean Up > Purge Orphansy en el outliner selecciona solo los objetos a exportar.
El pipeline end-to-end, vista general
Dibuja la línea entera para tener un mapa mental:
Archivo fuente de Blender
│ exportar .glb (float32 + PNG) 50 MB
▼
[1] Desdup + soldar vértices duplicados (gltf-transform) ~45 MB
│
[2] Compresión de vértices: MeshOpt (gltfpack / gltf-transform) ~30 MB
│
[3] Compresión de texturas: PNG → KTX2 (ETC1S/UASTC) ~6 MB
│
[4] (opcional) Simplificación de geometría LOD (simplify) ~4-5 MB
▼
Final model-final.glb ~5 MB
│
Carga en motor (Three.js / Babylon.js) → transcode en runtime → producción
Los números de cada paso se siguen en vivo en las tablas de abajo.
Toolchain: cuál elegir
Hay varias herramientas de compresión; aquí una comparación para no elegir mal:
| Herramienta | Fortaleza | Debilidad | Buena para |
|---|---|---|---|
| gltf-transform | Todoterreno, texturas + vértices de golpe; con API, scriptable | Razón máxima por debajo de herramientas dedicadas | Herramienta principal recomendada para la mayoría de escenarios |
| gltfpack | Pro de compresión de vértices, MeshOpt nativo | Compresión de texturas floja | Denso en vértices, control fino de MeshOpt |
| toktx | Herramienta de texturas más profesional, más parámetros | Solo texturas, no modelos enteros | Afinar una textura individual |
| gltf-pipeline | Veterano, soporta Draco | Mantenimiento inactivo, pocas funciones | Proyectos legacy con Draco |
| Herramientas online (gltf.report) | Cero instalación | No para automatización/masivo | Experimentos, tareas de una sola vez |
Recomendación principal: recorre todo el flujo con gltf-transform; usa gltfpack para detalle de vértices y toktx para afinar texturas individuales según haga falta. Todos los pasos de abajo se basan en gltf-transform.
Paso 1: desdup + soldar
Los modelos suelen tener vértices duplicados, nodos y materiales no usados. Limpia primero.
gltf-transform optimize model.glb step1.glb --weld --prune
| Etapa | Tamaño archivo | VRAM | Cambio |
|---|---|---|---|
| Base | 50 MB | ~520 MB | — |
| Step 1 desdup | 45 MB | ~520 MB | -5 MB (VRAM sin cambio, porque las texturas siguen) |
El VRAM casi no se movió — es lo esperado. El desdup ahorra principalmente vértices y estructura; las texturas son el peso pesado del VRAM.
Paso 2: compresión de vértices MeshOpt
gltf-transform optimize step1.glb step2.glb --meshopt --weld --prune
--meshopt cuantiza los vértices a 16 bits y aplica codificación sin pérdida MeshOpt, añadiendo automáticamente la extensión EXT_meshopt_compression.
| Etapa | Tamaño archivo | VRAM | Cambio |
|---|---|---|---|
| Step 1 | 45 MB | ~520 MB | — |
| Step 2 + MeshOpt | 30 MB | ~520 MB | -15 MB (parte de vértices) |
¿El VRAM sigue ~520 MB? Correcto — porque los vértices son una parte pequeña del VRAM (10-20 %); cortarlos tiene un impacto limitado. El verdadero monstruo del VRAM son las texturas, tratadas a continuación.
Paso 3: compresión de texturas PNG → KTX2
Este paso es el rey del coste-beneficio.
gltf-transform optimize step2.glb step3.glb \
--texture-compress basisu \
--meshopt --weld --prune
--texture-compress basisu detecta cada map automáticamente: color maps (albedo, emissive) → ETC1S; data maps (normal, roughness, metallic, AO) → UASTC.
| Etapa | Tamaño archivo | VRAM | Cambio |
|---|---|---|---|
| Step 2 | 30 MB | ~520 MB | — |
| Step 3 + KTX2 | 6 MB | ~70 MB | -24 MB archivo / -450 MB VRAM |
Este paso es el punto de inflexión de todo el pipeline:
- El archivo cae de 30 MB a 6 MB
- El VRAM cae de 520 MB a ~70 MB — porque los seis maps 4096 pasan de «píxeles crudos descomprimidos» a «compresión por bloque», cada uno de ~87 MB a ~11-14 MB
El VRAM cae un orden de magnitud — esa es la clave de si corre en móvil.
Paso 4: (opcional) simplificación de geometría
Si quieres aún más pequeño y la escena permite bajar la precisión de vértices, añade simplificación de geometría.
gltf-transform optimize step3.glb final.glb \
--texture-compress basisu \
--meshopt \
--simplify --simplify-ratio 0.5 \
--weld --prune
--simplify-ratio 0.5 significa conservar aproximadamente el 50 % de los vértices.
| Etapa | Tamaño archivo | VRAM | Cambio |
|---|---|---|---|
| Step 3 | 6 MB | ~70 MB | — |
| Step 4 + simplificar 0.5 | 4.5 MB | ~70 MB | -1.5 MB (VRAM casi sin cambio) |
La simplificación ahorra principalmente tamaño de archivo; el impacto en VRAM es pequeño. El coste es detalle del modelo reducido — visible de cerca. Las páginas de producto e-commerce normalmente no deberían simplificar en exceso; edificios/escenas grandes encajan muy bien.
Tabla total de seguimiento de efectos
Apila los cuatro pasos para el cuadro completo (basado en la muestra de arriba; los números ilustran el orden de magnitud):
| Paso | Tamaño archivo | VRAM | Reducción acumulada |
|---|---|---|---|
| Base (float32 + PNG) | 50 MB | ~520 MB | — |
| + desdup y soldar | 45 MB | ~520 MB | -10 % |
| + vértices MeshOpt | 30 MB | ~520 MB | -40 % |
| + texturas KTX2 | 6 MB | ~70 MB | -88 % archivo / -87 % VRAM |
| + simplif. geometría (0.5) | 4.5 MB | ~70 MB | -91 % archivo |
Conclusión: la compresión de texturas aporta la inmensa mayoría de las ganancias de tamaño y VRAM. La compresión de vértices es la guinda; la de texturas es el salvavidas. Esto encaja totalmente con la tesis del primer artículo — las texturas son el 80 % del tamaño, optimizarlas es lo más rentable.
Versión de un comando: compresión perezosa de golpe
Si no quieres ir paso a paso, haz todas las optimizaciones a la vez:
gltf-transform optimize model.glb model-final.glb \
--texture-compress basisu \
--meshopt \
--simplify --simplify-ratio 0.5 \
--weld --prune
Este un comando = desdup + soldar + vértices MeshOpt + texturas KTX2 + simplificación de geometría. Basta para el 90 % de los escenarios; el paso a paso es sobre todo para entender y afinar.
Script de automatización: reutilizable
Encapsula el flujo en un script que puedas meter en el build. Este script saca una versión distinta por plataforma objetivo e imprime el efecto de cada paso.
// scripts/compress-model.mjs
import { optimize } from "@gltf-transform/functions";
import { NodeIO } from "@gltf-transform/core";
import { KHRONOS_EXTENSIONS } from "@gltf-transform/extensions";
import { filesize } from "filesize";
const io = new NodeIO().registerExtensions(KHRONOS_EXTENSIONS);
// Estrategias de compresión por plataforma
const PROFILES = {
mobile: {
textureCompression: "basisu",
meshCompression: "meshopt",
simplify: { ratio: 0.5 },
weld: true,
prune: true,
},
vr: {
textureCompression: "basisu",
meshCompression: "meshopt",
// Visión cercana en VR, no simplificar
simplify: null,
weld: true,
prune: true,
},
desktop: {
textureCompression: "webp",
meshCompression: "meshopt",
simplify: null,
weld: true,
prune: true,
},
};
async function compress(inputPath, profileName) {
const cfg = PROFILES[profileName];
const doc = await io.read(inputPath);
const before = Buffer.byteLength(await io.writeBinary(doc), "utf8");
await optimize(doc, {
textureCompression: cfg.textureCompression,
meshCompression: cfg.meshCompression,
simplify: cfg.simplify ?? undefined,
weld: cfg.weld,
prune: cfg.prune,
});
const bytes = await io.writeBinary(doc);
const outPath = inputPath.replace(/\.glb$/, `-${profileName}.glb`);
await io.write(outPath, doc);
const after = bytes.byteLength;
console.log(
`${profileName.padEnd(8)} ${filesize(before)} → ${filesize(after)} ` +
`(${Math.round((1 - after / before) * 100)}% smaller) → ${outPath}`
);
}
// Uso: node scripts/compress-model.mjs path/to/model.glb
const input = process.argv[2];
for (const profile of Object.keys(PROFILES)) {
await compress(input, profile);
}
Meterlo en el proyecto:
node scripts/compress-model.mjs public/models/model.glb
# mobile 50 MB → 4.5 MB (91 % más pequeño) → model-mobile.glb
# vr 50 MB → 6.2 MB (87 % más pequeño) → model-vr.glb
# desktop 50 MB → 11 MB (78 % más pequeño) → model-desktop.glb
El frontend solo carga la versión correspondiente por dispositivo en runtime.
¿No quieres montar este pipeline tú mismo? La herramienta online de Any3D hace todo lo anterior de golpe — sube un GLB y automáticamente corre textura KTX2 + vértice MeshOpt y saca versiones comprimidas por plataforma, ahorrándate el lío de instalar una toolchain local.
FAQ de trampas comunes
El modelo se vuelve negro / las texturas no se muestran tras comprimir
- 99 % es el espacio de color: a un color map le falta sRGB. En Three.js
texture.colorSpace = THREE.SRGBColorSpace. - En toktx olvidaste
--srgben color maps.
La iluminación se ve mal tras comprimir normal maps
- El normal map usó ETC1S; cambia a UASTC.
- El normal map es estilo DirectX (canal verde abajo); el motor quiere estilo OpenGL, hay que invertir el canal G.
La carga móvil se atasca en el first paint
- Comprueba si estás cargando el wasm del decoder de Draco (petición extra). En móvil prefiere MeshOpt.
- La ruta del transcoder KTX2 mal configurada; el transcode falla y cae a decode en CPU.
El archivo comprimido se hizo más grande
- El map es demasiado pequeño (< 128 px); KTX2 no compensa — la compresión por bloque tiene un coste fijo.
- El modelo ya estaba comprimido una vez; una segunda pasada no da ganancia (incluso negativa).
El modelo se rompe tras simplificar
--simplify-ratiodemasiado bajo; sube a 0.7-0.8.- La simplificación es amable con superficies duras (maquinaria, arquitectura) y propensa a romper curvas orgánicas (personajes).
KTX2 falla al cargar en algunos navegadores
- Safari viejo / WebView viejo no lo soportan. Prepara un fallback PNG/WebP, o usa el campo
fallbackdeKHR_texture_basisupara una red de seguridad.
Chuleta de la serie
La esencia de los seis artículos condensada en una tabla —bookmark.
Composición del tamaño
| Componente | Proporción | Herramienta de optimización |
|---|---|---|
| Maps de textura | 70-85 % | KTX2 (ganancia mayor) |
| Datos de vértices | 10-20 % | MeshOpt / cuantización / Draco |
| Datos de animación | 0-15 % | Reducir keyframes / comprimir |
| Otros | < 2 % | Desdup |
Fórmula de VRAM
VRAM de formato tradicional = ancho * alto * 4 bytes * 1.333 (con mipmaps)
VRAM con compresión por bloque KTX2 ≈ lo de arriba / 4 (ETC1S) o / 2 (UASTC)
Selección de compresión de vértices
| Escenario | Recomendación |
|---|---|
| Cero dependencias, lo más simple | Cuantización pura (KHR_mesh_quantization) |
| Equilibrado, primer favorito web | MeshOpt |
| Razón máxima, se puede esperar el decode | Draco |
| Mini Program / sensible al paquete | Cuant. pura / MeshOpt; evitar Draco |
Selección de compresión de texturas
| Tipo de map | Codificación recomendada |
|---|---|
| albedo / emissive (color) | KTX2 ETC1S |
| normal / roughness / metallic / AO (datos) | KTX2 UASTC |
| Web escritorio, perseguir velocidad de descarga | WebP / AVIF |
| Maps pequeños (< 128 px) | Mantener PNG, no KTX2 |
Comando de un golpe
# Optimización completa (textura + vértice + simplificar)
gltf-transform optimize model.glb model-final.glb \
--texture-compress basisu --meshopt \
--simplify --simplify-ratio 0.5 --weld --prune
Chuleta de plataforma
| Plataforma | Textura | Vértice |
|---|---|---|
| Web escritorio | WebP / KTX2 | MeshOpt |
| Web móvil | KTX2 obligatorio | MeshOpt |
| VR | KTX2 obligatorio | MeshOpt + LOD |
| Mini Program | KTX2 / WebP | MeshOpt / cuantización |
| Escena grande | KTX2 obligatorio | MeshOpt + Draco + LOD |
Repaso de la serie
Seis artículos recorridos; juntos forman una cadena completa:
- Por qué tan pesado: fijar la composición del tamaño y la verdad del VRAM
- Tres armas de la compresión de vértices: principios y elección de cuantización, MeshOpt, Draco
- El problema del VRAM de texturas: por qué PNG/JPG son culpables a los ojos de la GPU
- KTX2 en la práctica: selección ETC1S/UASTC, toolchain, carga en motor
- Guía de selección: un marco de decisión por plataforma/escenario
- Este artículo: pipeline end-to-end, de Blender a producción
El núcleo es una frase: piensa primero el cuello de botella (descarga/VRAM/framerate), luego elige herramientas; la compresión de texturas tiene el mayor retorno, la de vértices es la guinda; plataformas distintas, destinos distintos — no apliques una regla universal.
Siguiendo el script y la chuleta de este artículo, llevar tu modelo de 50 MB a 5 MB y el VRAM de 520 MB a 70 MB debería ser un camino reproducible. Lo demás es solo ponerse a ello.