Any3DAny3D
·Any3D Team

De Blender a producción: un walkthrough de compresión end-to-end

3d-compressionpipelinetexture-compressionvertex-compressiongltf

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 inicialValor
Archivo fuente de Blender~120 MB (incl. alta poligonal no exportada)
GLB exportado (float32 + PNG)~50 MB
Número de vértices~180k
Maps6 × 4096×4096 (albedo, normal, roughness, metallic, AO, emissive)
Uso de VRAM (6 descomprimidas)~520 MB
ObjetivoArchivo ≤ 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: Automatic o JPEG (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 Orphans y 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:

HerramientaFortalezaDebilidadBuena para
gltf-transformTodoterreno, texturas + vértices de golpe; con API, scriptableRazón máxima por debajo de herramientas dedicadasHerramienta principal recomendada para la mayoría de escenarios
gltfpackPro de compresión de vértices, MeshOpt nativoCompresión de texturas flojaDenso en vértices, control fino de MeshOpt
toktxHerramienta de texturas más profesional, más parámetrosSolo texturas, no modelos enterosAfinar una textura individual
gltf-pipelineVeterano, soporta DracoMantenimiento inactivo, pocas funcionesProyectos legacy con Draco
Herramientas online (gltf.report)Cero instalaciónNo para automatización/masivoExperimentos, 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
EtapaTamaño archivoVRAMCambio
Base50 MB~520 MB
Step 1 desdup45 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.

EtapaTamaño archivoVRAMCambio
Step 145 MB~520 MB
Step 2 + MeshOpt30 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.

EtapaTamaño archivoVRAMCambio
Step 230 MB~520 MB
Step 3 + KTX26 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.

EtapaTamaño archivoVRAMCambio
Step 36 MB~70 MB
Step 4 + simplificar 0.54.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):

PasoTamaño archivoVRAMReducción acumulada
Base (float32 + PNG)50 MB~520 MB
+ desdup y soldar45 MB~520 MB-10 %
+ vértices MeshOpt30 MB~520 MB-40 %
+ texturas KTX26 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 --srgb en 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-ratio demasiado 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 fallback de KHR_texture_basisu para 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

ComponenteProporciónHerramienta de optimización
Maps de textura70-85 %KTX2 (ganancia mayor)
Datos de vértices10-20 %MeshOpt / cuantización / Draco
Datos de animación0-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

EscenarioRecomendación
Cero dependencias, lo más simpleCuantización pura (KHR_mesh_quantization)
Equilibrado, primer favorito webMeshOpt
Razón máxima, se puede esperar el decodeDraco
Mini Program / sensible al paqueteCuant. pura / MeshOpt; evitar Draco

Selección de compresión de texturas

Tipo de mapCodificación recomendada
albedo / emissive (color)KTX2 ETC1S
normal / roughness / metallic / AO (datos)KTX2 UASTC
Web escritorio, perseguir velocidad de descargaWebP / 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

PlataformaTexturaVértice
Web escritorioWebP / KTX2MeshOpt
Web móvilKTX2 obligatorioMeshOpt
VRKTX2 obligatorioMeshOpt + LOD
Mini ProgramKTX2 / WebPMeshOpt / cuantización
Escena grandeKTX2 obligatorioMeshOpt + Draco + LOD

Repaso de la serie

Seis artículos recorridos; juntos forman una cadena completa:

  1. Por qué tan pesado: fijar la composición del tamaño y la verdad del VRAM
  2. Tres armas de la compresión de vértices: principios y elección de cuantización, MeshOpt, Draco
  3. El problema del VRAM de texturas: por qué PNG/JPG son culpables a los ojos de la GPU
  4. KTX2 en la práctica: selección ETC1S/UASTC, toolchain, carga en motor
  5. Guía de selección: un marco de decisión por plataforma/escenario
  6. 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.

Apóyanos