Kris

KTX2 en la práctica: la forma correcta de comprimir texturas

Compresión 3DCompresión de texturasKTX2Basis UniversalglTF

En el artículo anterior aclaramos la relación entre los formatos de textura de GPU, Basis Universal y KTX2. Ahora que entendemos la teoría, este artículo es puramente práctico: cómo elegir entre ETC1S y UASTC, qué herramientas usar, qué comandos ejecutar, y cómo cargar las texturas en los motores.

Puedes seguir los pasos y hacerlo mientras lees.

Primero, la decisión más importante: ¿ETC1S o UASTC?

Basis ofrece dos codificaciones intermedias. Elegir mal no es solo cuestión de «no es tan bueno», sino que los mapas de normales se arruinarán directamente. Memoriza esta tabla:

ETC1SUASTC
Tasa de compresiónMuy alta (similar a JPG)Media (similar a PNG de alta calidad)
Calidad de imagenSuficiente para texturas de colorCercana a la calidad original
Memoria de video (post-transcodificación)Generalmente 4bpp (~1/8 del original)Generalmente 8bpp (~1/4 del original)
Velocidad de codificaciónLenta (nivel ajustable)Rápida
AplicaciónAlbedo/difusa, emisivaNormal, metalness-roughness, mapas de datos
No aplicableNormal, mapas que requieren valores precisosMapas de color (matar moscas a cañonazos, tamaño grande)

¿Por qué no se puede usar ETC1S para mapas de normales? Porque los mapas de normales almacenan vectores de dirección, y los tres canales RGB de cada píxel se restringen mutuamente (longitud del vector ≈ 1). ETC1S es una compresión por bloques diseñada para que «los colores se vean bien». No es sensible a la precisión de un solo canal, y después de la compresión, la dirección del vector se desvía, haciendo que la iluminación se vea incorrecta de inmediato, especialmente en los reflejos especulares y los detalles de alta frecuencia. UASTC conserva mejor los valores numéricos y puede soportar este requisito de precisión.

Regla práctica:

  • Mapas de color (albedo, emisiva) → ETC1S
  • Mapas de datos (normal, roughness, metallic, AO, thickness) → UASTC
  • Si tienes dudas y quieres ahorrar tamaño → prueba primero con ETC1S; si se ve borroso al hacer zoom, cambia a UASTC.

Comparación de cuatro formatos con la misma imagen

Usando una textura de albedo de 2048×2048 como referencia (datos típicos de la comunidad, solo como referencia):

FormatoTamaño en discoMemoria de video (con mipmap)Subida a GPUMultiplataforma
PNG~5MB~22MBLenta
WebP~1MB~22MBLenta
KTX2 (ETC1S)~0.5-0.8MB~2.8MBRápida
KTX2 (UASTC)~3-4MB~5.6MBRápida

Observa que WebP ocupa la misma memoria de video que PNG: solo es más pequeño en disco, pero al cargarlo en la GPU se descomprime a píxeles originales. KTX2 con ETC1S reduce tanto el disco como la memoria de video al mínimo, y ahí radica su valor.

Herramientas: tres caminos que funcionan

No hay una sola herramienta para comprimir KTX2. Las ordenamos por «comodidad de uso»:

1. toktx (oficial, la más potente)

Herramienta oficial de Khronos, con la mayor cantidad de parámetros, ideal para procesar texturas individuales.

# Convertir PNG a KTX2 con codificación ETC1S
toktx --bcmp --uastc 0 albedo.ktx2 albedo.png

# Convertir PNG a KTX2 con codificación UASTC
toktx --uastc 1 normal.ktx2 normal.png

Parámetros comunes:

# ETC1S + nivel de calidad (1-255, por defecto 128, a mayor valor mayor calidad y tamaño)
toktx --bcmp --uastc 0 --qlevel 200 albedo.ktx2 albedo.png

# UASTC + supercompresión (Zstandard, reduce aún más el tamaño en disco)
toktx --uastc 1 --zcmp 19 normal.ktx2 normal.png

# Generar mipmaps automáticamente (muy recomendado)
toktx --bcmp --genmipmap albedo.ktx2 albedo.png

# Especificar espacio de color sRGB (obligatorio para mapas de color)
toktx --bcmp --srgb albedo.ktx2 albedo.png

--bcmp activa el modo ETC1S (modo base de Basis Universal), --uastc 1 activa el modo UASTC. Son mutuamente excluyentes.

2. gltf-transform (el más sencillo, muy recomendado)

Si tienes un modelo glTF/GLB completo, usa gltf-transform con un solo comando para convertir todas las texturas a KTX2, eligiendo automáticamente ETC1S/UASTC según el uso.

# Instalación
npm install -g @gltf-transform/cli

# Comprimir todo el modelo con un solo comando
gltf-transform optimize model.glb model-optimized.glb \
  --texture-compress basisu

Internamente, determina el uso de la textura: color → ETC1S, datos → UASTC, y escribe automáticamente la extensión KHR_texture_basisu. Esta es la mejor opción en el 90% de los casos, sin necesidad de ejecutar toktx manualmente para cada textura.

3. Herramientas en línea (la más rápida para empezar)

Cuando no quieras instalar un entorno, usa herramientas en el navegador: gltf.report (versión en línea de gltf-transform), KTX2 Converter, etc. Subes, descargas, y listo. Ideal para pruebas rápidas o tareas puntuales.

Pipeline completo: desde el archivo fuente hasta la publicación

Resumamos el flujo estándar (diagrama de flujo):

Archivo fuente (PNG/JPG/PSD/TGA)
        │
        ├── [Modelo completo] gltf-transform optimize model.glb → identifica automáticamente el tipo de textura
        │            └─ Mapas de color → ETC1S
        │            └─ Mapas de datos → UASTC
        │            └─ Escribe la extensión KHR_texture_basisu
        │
        └── [Textura individual] toktx → especificar manualmente ETC1S/UASTC + espacio de color + mipmaps
        │
        ▼
KTX2 / GLB comprimidos
        │
        ▼
Carga en el motor (Three.js / Babylon.js) → transcodificación en tiempo de ejecución → formato nativo de GPU

Cargar KTX2 en Three.js

Three.js soporta KTX2 de forma nativa desde r129: asocia KTX2Loader (especificando la ruta del transcoder wasm, detectando la capacidad de la GPU) a GLTFLoader. Luego, al cargar un glTF con texturas KTX2, se transcodifican automáticamente.

const ktx2Loader = new KTX2Loader().setTranscoderPath("/basis/").detectSupport(renderer);
gltfLoader.setKTX2Loader(ktx2Loader); // Si el modelo también usa Draco/MeshOpt, recuerda cargar DRACOLoader / MeshoptDecoder

detectSupport(renderer) es obligatorio: determina a qué formato nativo se transcodificará en tiempo de ejecución. Además, las texturas de color deben tener texture.colorSpace = THREE.SRGBColorSpace. Omitir esto es el clásico error de «la imagen se ve grisácea» (los mapas de datos como normal/roughness deben permanecer lineales).

Cargar KTX2 en Babylon.js

Babylon es aún más sencillo: GLTFFileLoader tiene habilitada por defecto la extensión KHR_texture_basisu y descarga automáticamente el transcoder de Basis desde la CDN. Cargar un glTF con KTX2 funciona sin configuración adicional. En entornos sin conexión/intranet, hay que configurar manualmente BASISFileLoader.TranscoderModule.

Ajuste de parámetros de compresión: equilibrio entre calidad y tamaño

El parámetro clave de ETC1S es --qlevel (1-255). ¿Cómo afecta al resultado?

qlevelTamañoCalidadTiempo de codificaciónAplicación
128 (por defecto)PequeñoSuficienteMedioLa mayoría de los casos
200-255MayorCasi sin pérdidaLargo (varias veces)Requisitos de alta calidad
60-100Muy pequeñoBloques visiblesRápidoTexturas lejanas/pequeñas

El tamaño de UASTC es relativamente fijo, principalmente se ajusta con --zcmp (supercompresión Zstandard) para reducir el tamaño en disco, sin afectar la memoria de video (después de descomprimir sigue siendo 8bpp).

Orden sugerido para ajustar:

  1. Primero, comprime con parámetros por defecto y evalúa tamaño y calidad
  2. Si no es suficiente → ajusta --qlevel (ETC1S) o añade --zcmp (UASTC)
  3. Si el mapa de normales se ve borroso → confirma que estás usando UASTC, no ETC1S
  4. Si los colores se ven oscuros → verifica la configuración del espacio de color (bandera sRGB, colorSpace en el motor)

Solución de problemas comunes

Error de transcodificación / error al cargar

  • Verifica que la ruta del transcoder wasm sea correcta (en Three.js, setTranscoderPath)
  • Verifica que la versión del motor sea compatible con la versión actual de KTX2 (las codificaciones antiguas de Basis no son compatibles con transcodificadores modernos)
  • La consola suele mostrar un error específico; busca por palabras clave

Colores más oscuros / más claros

  • La textura de color (albedo) no tiene configurado sRGB, o está configurado al revés
  • Faltó --srgb al ejecutar toktx (debe añadirse para texturas de color)
  • Se añadió sRGB por error a un mapa de datos (normal)

Faltan mipmaps, parpadeo en la distancia

  • No se añadió --genmipmap durante la compresión
  • En el motor, texture.generateMipmaps no está activado (en Three.js, KTX2 lo trae por defecto, pero el material debe tener un minFilter que use mipmaps)

La dirección de las normales es incorrecta al hacer zoom

  • El mapa de normales se comprimió con ETC1S; cámbialo a UASTC
  • Confirma que el mapa de normales es de estilo OpenGL (canal verde hacia arriba); en algunos motores, los mapas de estilo DirectX requieren invertir el canal G

El archivo resultante es más grande

  • Las texturas pequeñas (< 128×128) no se benefician de KTX2; la compresión por bloques tiene un costo fijo que llena todo el bloque
  • Las texturas de un solo color no se deben comprimir a KTX2; es mejor usar directamente el valor de color del material

Siguiente paso

Con esto, la compresión de texturas está prácticamente dominada. Pero «saber usar la herramienta» no es lo mismo que «usar la herramienta correcta». En el próximo artículo, resumiremos todo el conocimiento de los 4 artículos anteriores en un marco de selección: escritorio, móvil, VR, mini programas web; para cada escenario, qué combinación usar.

Apóyanos