Saltar al contenido

TRELLIS 2 en 8 GB: generar modelos 3D localmente en una RTX 2080 SUPER

Oficialmente, TRELLIS 2 pide 24 GB de VRAM. Mi RTX 2080 SUPER tiene 8. Y salió en 2019. Evidentemente, había que probarlo.

Microsoft presenta TRELLIS.2 como un modelo image-to-3D de 4.000 millones de parámetros: le das una imagen y te devuelve un objeto 3D con geometría y materiales PBR, listo para exportar a GLB. En el repositorio oficial recomiendan una GPU NVIDIA de al menos 24 GB y publican sus tiempos de referencia medidos en una H100.

Este artículo no es una guía. Es un experimento con datos: meter el pipeline completo en una tarjeta de 8 GB de hace seis años y medir todo lo que se pueda medir. No «arrancó una vez»: monté un banco con 36 generaciones, 0 errores de memoria, y VRAM, RAM, tiempos por etapa, geometría y calidad de textura registrados en cada una.

TRELLIS 2 en 8 GB: generar modelos 3D localmente en una RTX 2080 SUPER (Turing). Un experimento con datos en una tarjeta de 2019.

Resumen de resultados

MétricaValor medido
Generaciones completadas36 / 36
Errores de memoria (OOM)0
Caída a ruta CPU (sm75_fallback)0
VRAM del proceso~5,3 GB (5.264–5.296 MB), plana en las 36
Pico total de la GPU7,28–7,38 GB de 8,19
RAM del proceso (RSS)14,5–17,8 GB
Tiempo por generación115–638 s (mediana highpoly Q8 ≈ 183 s; lowpoly 117–135 s)
Desplegado de UV (xatlas, CPU)0–364 s
Caras de la malla1.900 (lowpoly) a 668.000 (highpoly)
Mallas watertight0 / 36
Resumen del banco de pruebas de TRELLIS 2 en la RTX 2080 SUPER.

Sí: en esta implementación, TRELLIS 2 cabe en una GPU de 8 GB. Lo complicado fue explicárselo a TRELLIS 2.

Qué es TRELLIS 2 y qué genera

El flujo, en lenguaje humano: una imagen entra, sale un modelo 3D texturizado. Por dentro, en cascada:

  1. Estructura dispersa — un DiT de flow matching estima una rejilla gruesa de 64³.
  2. Geometría (SLAT) — dos DiT más refinan la forma a baja y alta resolución mediante la representación O-Voxel.
  3. Material (SLAT de textura) — un cuarto DiT genera base color, metallic, roughness y alpha.
  4. Malla + textura — dual-contouring vóxel→malla, remesh y simplificación (CuMesh), desplegado UV (xatlas), rasterizado y horneado (nvdiffrast).

Es un modelo de 4B parámetros; el oficial llega a resoluciones de 1536³. Microsoft habla de ~3 s a 512³, ~17 s a 1024³ y ~60 s a 1536³… en una H100.

Conviene decirlo pronto: no compares esos 17 segundos con los ~183 de este artículo como si fueran dos GPUs haciendo el mismo trabajo. Son hardware distinto, implementación distinta (aquí, GGUF cuantizado sobre ComfyUI) y un pipeline distinto. Lo que mide este banco no es «cuánto más lenta es la 2080», sino si funciona, con qué margen y qué configuración conviene.

Turing no estaba invitado

Viñeta: un portero fornido con la etiqueta NVIDIA Ampere bloquea la puerta de la «Fiesta de TRELLIS 2 — solo arquitectura Ampere o superior» y señala la salida a una pequeña RTX 2080 SUPER (Turing) cabizbaja, que piensa «¡Pero si soy sm_75! ¿Por qué no me han invitado?» mientras dentro bailan tarjetas RTX 3090 y A6000.
Las wheels precompiladas del nodo se construyen para Ampere en adelante; en Turing (sm_75) cada extensión CUDA aborta con el error 209.

El primer problema no fue la velocidad. Ni la VRAM. Fue que el software no traía instrucciones compiladas para la arquitectura de mi GPU.

Las wheels precompiladas del nodo de ComfyUI se construyen para tarjetas Ampere y posteriores. En una 2080 SUPER (Turing, sm_75), cada extensión CUDA aborta con:

CUDA error 209: cudaErrorNoKernelImageForDevice

Afecta a las cuatro piezas nativas del pipeline:

KernelFunción
o_voxeldual-contouring vóxel → malla
CuMeshremesh, simplificación, relleno de agujeros
FlexGEMMconvolución dispersa
nvdiffrastrasterizado + horneado de textura
Las cuatro extensiones CUDA del pipeline: sin sm_75, todas fallan en Turing.

Sin ellas solo queda una ruta por CPU tan lenta que un solo objeto tardaba 17 minutos únicamente en el desplegado de UV. Inservible para un banco.

Cuatro kernels y tres parches

Lo que hubo que tocar (el detalle y el script para reproducirlo van con el workflow, más abajo):

  1. Recompilar las cuatro extensiones para sm_75 desde sus fuentes, con Turing como arquitectura objetivo (TORCH_CUDA_ARCH_LIST="7.5;8.6"). Tras resolver varios choques de toolchain —CUDA 12.8 no acepta el libstdc++ de gcc 15, un LD_LIBRARY_PATH contaminado rompe ninja— la 2080 ejecuta el pipeline entero en GPU.
  2. Sustituir grid_sample_3d. En mi compilación para sm_75, la versión CUDA de grid_sample_3d dejaba un ribete de moteado negro en el borde del volumen disperso; sustituirla por una implementación en PyTorch lo eliminó. No he aislado la causa con un caso mínimo ni comprobado en otra máquina Turing, así que lo doy como observación de esta build, no como un fallo general del kernel.
  3. Cambiar el relleno del margen del atlas de textura. El método por defecto (cv2.inpaint TELEA) sobre el ~45 % vacío del atlas degenera en ruido de sal y pimienta; se sustituye por una extensión suave del color de borde (vecino válido más cercano + suavizado).
  4. Entender el GGUF al cargar. El nodo des-cuantiza cada tensor a BF16 en el momento de la carga. Ese detalle —que Q4_K_M y Q8_0 acaben igual en memoria— explica medio artículo.
Reemplazo de grid_sample_3dAntes (kernel CUDA)Después (torch)
Moteado negro en el borde del volumen12 %0 %
Desviación de color dentro de los charts UV6524
Efecto del reemplazo de grid_sample_3d sobre el moteado de la textura.

Metodología

Interior del PC de laboratorio de Acuántico Power con refrigeración líquida personalizada roja: AMD Ryzen 7 5800X y NVIDIA GeForce RTX 2080 SUPER de 8 GB.
El equipo del banco: RTX 2080 SUPER de 8 GB y Ryzen 7 5800X.

Hardware y entorno

GPUNVIDIA RTX 2080 SUPER · 8 GB · sm_75 (Turing)
CPU / RAMRyzen 7 5800X · 32 GiB
SoftwareComfyUI 0.26 + nodo GGUF de TRELLIS 2 · Python 3.12 · torch 2.9.1+cu128
ModeloTRELLIS.2-4B GGUF, formatos Q4_K_M y Q8_0
Hardware y software del banco.

Dataset

Tres imágenes «datasheet», producto sobre fondo oscuro, 1408×768. El banco parte de la JPG cruda con eliminación de fondo automática, que es como lo usaría un lector. Cada objeto estresa cosas distintas:

  • silla — patas tubulares finas de acero, chapa de madera, superficie brillante. El caso difícil: geometría delgada + materiales mixtos + reflejos.
  • taza — geometría sencilla pero con asa y agujero (topología con género 1). Cerámica mate casi lisa.
  • zapatilla — orgánica: cordones, malla, mediasuela, hueco de suela. Muchos detalles de mediana escala.
Las tres imágenes de partida del banco: la silla, la taza y la zapatilla, sobre fondo negro
Las tres imágenes de partida. De cada una TRELLIS 2 generó el modelo 3D.

Diseño del banco: 36 generaciones

Fueron 36 generaciones, no 36 imágenes: 3 objetos × 12 configuraciones. Las 12 configuraciones:

PistaCuantizaciónTexturaSamplerPasosMallaN.º
baseline (contraste)Q4_K_M1024euler + heun8/8/8 · 12/12/12 · 20/16/12highpoly6
highpolyQ8_02048euler8/8/8 · 12/12/12 · 20/16/12highpoly3
lowpolyQ8_02048euler12/12/12target_face_num 2k · 8k · 20k3
Las 12 configuraciones por objeto — 36 generaciones en total.

«Highpoly» significa sin tope efectivo de caras (target_face_num = 2 M, que nunca se alcanza). Los pasos se anotan como estructura/forma/textura.

Las 36 generaciones del banco de TRELLIS 2 en la RTX 2080 SUPER, renderizadas en Blender
Las 36 generaciones, renderizadas en Blender. Tres objetos × doce configuraciones.

Instrumentación

Un cliente HTTP lanza cada trabajo contra la API de ComfyUI. Un hilo muestrea cada 250 ms: VRAM del dispositivo y del proceso (NVML) y RSS del árbol de procesos (psutil). Se parsea el log para número de vóxeles, caras tras simplificar, agujeros, segundos de xatlas y marcas de OOM. Cada generación deja sus artefactos (malla GLB, traza de VRAM en CSV, recorte de log) y una fila en la tabla de resultados. El banco es reanudable: si se corta, se relanza y solo hace lo que falta.

Métricas de calidad

  • hf_std — desviación estándar del residuo de alta frecuencia del atlas base color (la imagen menos su media móvil 7×7), calculado sobre el atlas llevado a 1024 px para que mida la misma banda espacial en 1024 y en 2048. (La única excepción es la tabla exploratoria de diez diagnósticos, marcada como medida sobre atlas nativo.) Proxy de moteado; menor es mejor. Cautela: también baja cuando simplemente hay menos detalle que medir.
  • sil_IoU — solape de silueta contra el recorte de la datasheet. Se renderiza la malla desde 64 vistas (16 azimut × 4 elevación), se normaliza cada máscara a su bounding box y se toma la mejor IoU. Mide que exista un ángulo con esa silueta, no la fidelidad geométrica 3D ni la pose; mayor es mejor. Al quedarse con la mejor de 64 vistas es una medida deliberadamente optimista.
  • Geometría: vértices, caras, caras tras simplificar, vóxeles del dual-contouring, agujeros, watertight, tamaño del GLB.

Reproducibilidad

Semilla fija en las 36. Como comprobación, la pista highpoly Q4 se ejecutó dos veces: en esa repetición la geometría fue muy estable —las caras quedaron dentro del 0,4 % y los tiempos dentro del ~10 %. No hice una campaña multisemilla ni repetí las 36 condiciones, así que esta comprobación mide repetibilidad puntual, no variabilidad estadística del pipeline.

Las 36 filas de medidas (rendimiento, VRAM, geometría y las métricas de calidad), la traza de VRAM muestreada cada 250 ms de cada generación, el recorte de log correspondiente y los hashes y versiones fijadas están publicados para que los números de este artículo se puedan auditar. No incluye las mallas GLB (≈365 MB); sí todas sus métricas.

Elección de la configuración definitiva

Antes de la batería completa, diez diagnósticos sobre la misma generación (silla · 12/12/12 · euler), cambiando una sola palanca cada vez:

Variantehf_std ↓p99 ↓wall sVRAM dispOOM
Q4_K_M · tex 1024 (baseline)4,2161877212no
Q4_K_M · tex 1024 · relleno de gutter con mediana7,4271377283no
Q4_K_M · tex 1024 · tiled decoder OFF4,015,61817368no
Q4_K_M · tex 20482,17,61837375no
Q8_0 · tex 10244,015,51337386no
Q8_0 · tex 20482,27,71337394no
Q8_0 · tex 1024 · texture_steps 304,216,31417426no
Q4_K_M · reemplazo de grid_sample OFF6,021,31977421no
Q4_K_M · tex 2048 · grid_sample OFF4,115,81987403no
lowpoly · tex 2048 · 8.000 caras1,55,21397382no
Diez diagnósticos: una sola palanca cambiada cada vez (silla · 12/12/12 · euler). Tabla exploratoria previa a la batería: su hf_std/p99 se midió sobre el atlas nativo y no está normalizado (esas generaciones de diagnóstico no se archivaron). El resto del artículo usa hf_std normalizado a 1024 px, así que estas dos columnas no son comparables con las de más abajo; para 2048 sobrestiman la mejora frente a 1024.

Lecturas: Q8_0 no penaliza la VRAM (idéntica) ni el tiempo de forma consistente —en estos diagnósticos fue más rápido— → se elige Q8_0. tex 2048 reduce el moteado, aunque bastante menos de lo que dice el número crudo de la tabla (ver Limitaciones). El reemplazo de grid_sample reduce el ruido (6,0 → 4,0, ambas medidas a 1024). Más pasos de textura no ayudan; el tiled decoder es indiferente. El relleno de gutter con mediana empeora (preserva bordes → escalones): se usa gaussiano.

Configuración del banco: GGUF Q8_0 · pipeline 1024_cascade · texture_size 2048 · sampler euler · reemplazo de grid_sample activo · semilla fija.

Anatomía de una generación

Desglose del tiempo de una generación de la taza (12/12/12 · euler · Q8_0 · 2048, 181 s de reloj): muestreo de estructura dispersa ~59 s, decodificado a 64³ y cargas de modelo ~18 s, muestreo de forma SLAT baja resolución ~4 s y alta resolución ~20 s, dual-contouring de 929.939 vóxeles con remesh y simplificación ~15 s, desplegado de UV con xatlas en CPU (100 charts) 48 s, horneado de textura ~15 s.
Dónde se va el tiempo en una generación típica. El muestreo de la estructura dispersa y el desplegado de UV se reparten más de la mitad del tiempo; el segundo, además, no toca la GPU.

Resultados

Ejecución

36 de 36 completadas, 0 OOM, 0 caídas a CPU, en todo el rango: Q4 y Q8, textura 1024 y 2048, de 8/8/8 a 20/16/12 pasos, euler y heun, de 500.000 a 1.900 caras.

PistaGeneracionesOKOOMwatertight
baseline Q4 / 1024181800 / 18
highpoly Q8 / 20489900 / 9
lowpoly Q8 / 20489900 / 9
Ejecución: 36 de 36, sin OOM.

Memoria

PistaVRAM dispositivo (MB)VRAM proceso (MB)RSS (MB)
baseline Q4 / 10247.282 / 7.294 / 7.3365.264 / 5.264 / 5.29616.561 / 17.174 / 17.828
highpoly Q8 / 20487.335 / 7.350 / 7.3805.264 / 5.264 / 5.29614.752 / 15.158 / 15.877
lowpoly Q8 / 20487.335 / 7.335 / 7.3675.264 / 5.264 / 5.29614.553 / 15.286 / 15.533
Memoria — min / mediana / max por pista. Tope de VRAM del dispositivo: 8.192 MB.

min / mediana / max. Tope de VRAM del dispositivo: 8.192 MB.

Dos cosas. Primera: la VRAM del proceso es plana —5.264–5.296 MB— haga lo que haga con pasos, cuantización o textura. Segunda, y hay que no mezclarlas: la VRAM del proceso no es la VRAM total ocupada en la tarjeta. El proceso usó ~5,3 GB; el resto hasta los ~7,3–7,4 GB es el escritorio, el compositor y el contexto de CUDA. El modelo no estaba haciendo equilibrios al borde del abismo.

Velocidad

PistaReloj (s)xatlas (s)
baseline Q4 / 1024123 / 235 / 63810 / 60 / 364
highpoly Q8 / 2048115 / 183 / 23510 / 48 / 72
lowpoly Q8 / 2048117 / 123 / 1350 / 0 / 1
Velocidad — min / mediana / max por pista.

min / mediana / max.

  • El sampler Heun cuesta 1,5–3× que Euler para geometría y calidad casi idénticas. En ninguno de los tres objetos dio el mejor resultado visual; a menudo salió más sucio.
  • Subir de 8/8/8 a 20/16/12 pasos añade ~60–100 s. Mirando las 36 capturas, por encima de 12/12/12 no hubo una mejora visual consistente; con textura 2048, 12/12/12 + Euler fue el más equilibrado en los tres objetos. Es un resultado de este banco —tres objetos, una implementación GGUF—, no una conclusión general de TRELLIS 2.

Más pasos, más espera y una diferencia que muchas veces hay que buscar con lupa. La informática no siempre premia el entusiasmo.

El cuello de botella no estaba en la GPU

El desplegado de UV (xatlas) se ejecuta en CPU y su tiempo va de 10 s a 364 s en highpoly. Se dispara con las mallas de superficie curva y muchos charts —la taza a 20/16/12 heun: 311 s solo de unwrap—. En lowpoly baja a 0–1 s.

Mientras yo miraba una RTX de ocho gigas con cara de sospechosa, el que podía tirarse seis minutos pensando era xatlas, en la CPU.

Geometría

PistaVóxelesCaras (malla final)AgujerosGLB (MB)
baseline Q4 / 1024248k / 713k / 914k228k / 315k / 668k0 / 58 / 2.6727 / 10 / 18
highpoly Q8 / 2048391k / 612k / 930k245k / 319k / 510k0 / 11 / 96312 / 16 / 18
lowpoly Q8 / 2048400k / 585k / 930k1.896 / 7.988 / 20.4590 / 2 / 632 / 4 / 6
Geometría — min / mediana / max por pista.
  • Caras highpoly según complejidad del objeto: silla ~248k, zapatilla ~320k, taza ~510k. El target_face_num de 2 M nunca se alcanza: lo limita la resolución de dual-contouring.
  • target_face_num bajo se respeta con precisión: pides 2.000 / 8.000 / 20.000 y obtienes ~1.900 / ~7.900 / ~19.900.
  • Ninguna de las 36 mallas es watertight. Los agujeros van de 0 a unos miles y no correlacionan con la calidad visual. Para impresión 3D directa haría falta un paso de reparación.

Cuantización: Q4 no ahorra VRAM

La expectativa era usar la cuantización más agresiva (Q4_K_M) para que cupiera, y aceptar peor calidad. Comparando las dos configuraciones principales del banco, a igual objeto, pasos y sampler:

Objeto · pasosReloj Q4 → Q8hf_std Q4 → Q8VRAM proceso
silla 8/8/8123 → 115 s6,7 → 7,25.264 → 5.264
silla 12/12/12137 → 193 s7,4 → 6,15.264 → 5.264
silla 20/16/12182 → 175 s12,1 → 5,65.264 → 5.264
taza 8/8/8149 → 149 s16,0 → 10,25.296 → 5.296
taza 12/12/12189 → 183 s17,0 → 8,15.264 → 5.296
taza 20/16/12297 → 219 s18,3 → 4,55.264 → 5.296
zapatilla 8/8/8205 → 173 s25,1 → 12,85.264 → 5.264
zapatilla 12/12/12196 → 191 s16,4 → 5,95.264 → 5.264
zapatilla 20/16/12269 → 235 s10,9 → 12,35.264 → 5.264
Q4 / 1024 frente a Q8 / 2048, manteniendo objeto, pasos y sampler. Cambian dos variables a la vez.

El hf_std de esta tabla está normalizado a 1024 px, pero sigue mezclando cuantización y textura (Q4 va a 1024, Q8 a 2048): en 7 de los 9 casos el atlas de Q8 / 2048 tiene menos moteado que el de Q4 / 1024; en 2 (silla 8/8/8 y zapatilla 20/16/12), no. No es una comparación de un solo factor. Lo que sí es limpio: la VRAM del proceso es idéntica, y el tiempo no muestra penalización consistente para Q8. Como los tensores GGUF se des-cuantizan a BF16 al cargar, la cuantización solo cambia el tamaño en disco y el error de los pesos, no la memoria en ejecución. Si Q4 no ahorra memoria donde importa, me quedo con Q8, que tiene mejores pesos. El repositorio GGUF ofrece Q4_K_M, Q5_K_M, Q6_K y Q8_0 de los distintos componentes.

Textura 1024 → 2048

La primera medición, con un hf_std calculado píxel a píxel, sugería que pasar a 2048 reducía el moteado a la mitad. Era en parte un artefacto de medida: un filtro de tamaño fijo abarca una banda espacial más fina en un atlas de 2048 que en uno de 1024, así que la métrica bajaba también por resolución.

Atlas normalizado a 1024 pxhf_std ↓ (mediana)
baseline · Q4_K_M · textura 102416,0
highpoly · Q8_0 · textura 20487,2
lowpoly · Q8_0 · textura 20483,8
Moteado con los atlas llevados a una resolución común. El salto 1024→2048 compone también el cambio Q4→Q8; no es un solo factor.

Menos moteado, sin coste apreciable de tiempo y sin provocar ningún OOM: un factor ~2 en la mediana normalizada —no ~4 como decía el número crudo—, y mezclado con el cambio de cuantización. La evidencia más clara de que 2048 se ve mejor son las láminas. Aun así, girar el botón hacia la derecha sí hizo algo útil.

Highpoly frente a lowpoly

El otro gran resultado. La misma generación (Q8 · 2048 · 12/12/12 · euler), variando solo el tope de caras:

ConfigCarasGLBxatlashf_std ↓sil_IoU ↑
silla · highpoly249.90312,1 MB68 s6,100,596
silla · lowpoly 2k1.9343,3 MB0 s3,770,583
silla · lowpoly 8k7.9883,6 MB0 s3,970,581
silla · lowpoly 20k19.5224,4 MB1 s3,880,579
taza · highpoly510.30017,6 MB48 s8,100,949
taza · lowpoly 2k1.8962,5 MB0 s1,890,955
taza · lowpoly 8k7.4822,6 MB0 s2,000,950
taza · lowpoly 20k19.8603,0 MB0 s2,110,950
zapatilla · highpoly320.67715,2 MB62 s5,910,896
zapatilla · lowpoly 2k1.9024,4 MB0 s3,520,898
zapatilla · lowpoly 8k8.1565,0 MB0 s3,890,897
zapatilla · lowpoly 20k20.4595,6 MB1 s3,970,896
Highpoly frente a lowpoly: misma generación, solo cambia el tope de caras. hf_std con el atlas normalizado a 1024 px (todas estas variantes usan textura 2048, así que la comparación es homogénea).

A ~1.900 caras —en torno al 0,4–0,8 % de la versión completa— la silueta apenas cambia: la mayor diferencia absoluta de sil_IoU en los tres objetos es 0,013. El GLB queda aproximadamente 3,5–7 veces más pequeño y el desplegado de UV pasa de 48–68 s a prácticamente cero.

Con una cautela: el hf_std (aquí ya normalizado a 1024) también baja porque a pocas caras hay menos alta frecuencia que medir; aun así, el lowpoly a 2048 tiene consistentemente menos que el highpoly a 2048, lo que apoya el mecanismo que describo más abajo. No diré que lowpoly sea «mejor» a secas. Diré que para assets de escena donde importa el presupuesto de polígonos, fue claramente la opción más práctica en estas pruebas.

Comparativa de la silla generada con TRELLIS 2: lowpoly frente a highpoly con textura 1024 y 2048
Silla · lowpoly (2k/8k/20k) │ highpoly textura 1024 (las 6 del banco de contraste, Q4_K_M) │ highpoly textura 2048 (Q8_0).
Comparativa de la taza generada con TRELLIS 2: lowpoly frente a highpoly con textura 1024 y 2048
Taza · misma comparación. La textura 2048 da la superficie más limpia; a 2.000 caras el interior facetado delata el lowpoly.
Comparativa de la zapatilla generada con TRELLIS 2: lowpoly frente a highpoly con textura 1024 y 2048
Zapatilla · geometría compleja. Con textura 2048, 12/12/12 + Euler es el mejor equilibrio.

Las tres láminas cuentan la misma historia: las configuraciones Q8 / 2048 muestran mucha menos suciedad superficial que varias de las Q4 / 1024, y las variantes Heun salen peor. El lowpoly aguanta la silueta pero pierde nitidez en bordes e interiores.

El punto dulce del lowpoly

8.000 caras bastaron para geometría sencilla o media —silla y taza—: a partir de ahí la mejora es pequeña. La zapatilla, con cordones, paneles, mediasuela y hueco de suela, conservó mejor la forma a 20.000: los polígonos de más tienen dónde trabajar. Con 2.000 caras la degradación ya es clara —el objeto se vuelve una aproximación angular—, y en la zapatilla es especialmente evidente.

Discusión

Infografía que resume la discusión: por qué la VRAM del proceso es plana (el pico lo fija el DiT más grande ya des-cuantizado), por qué xatlas escala mal (las mallas densas generan cientos de charts en CPU), por qué el lowpoly limpia la textura (la interpolación actúa como filtro paso-bajo) y fidelidad de forma frente a fidelidad de textura (el IoU de silueta apenas se mueve con la textura o el número de polígonos).
Resumen visual de la discusión: los cuatro porqués del comportamiento del pipeline en 8 GB.

Por qué la VRAM del proceso es plana

El nodo GGUF de este pipeline des-cuantiza cada DiT a BF16 en el momento de la carga. Los pesos ocupan lo mismo en memoria vengan de Q4_K_M o de Q8_0; y como el pipeline carga un DiT cada vez y descarga el anterior, el pico del proceso lo fija el DiT más grande ya des-cuantizado, no el número de pasos ni la textura. De ahí los 5,3 GB constantes. Consecuencia práctica: en esta implementación, la cuantización agresiva no es una palanca de VRAM.

Por qué xatlas escala mal

El desplegado de UV parte la malla en charts (islas) y los empaqueta en el atlas. En una malla densa de superficie curva —la taza— salen cientos de charts con fronteras largas, y el «Computing charts» es CPU puro, un par de hilos. La malla lowpoly, con pocas caras, produce ~100 charts grandes y limpios: el paso se vuelve instantáneo. Es el mayor determinante del tiempo total en objetos complejos, y no se acelera con la GPU.

Por qué el lowpoly «limpia» la textura

El horneado muestrea el volumen de atributos (el SLAT de textura) en la posición 3D de cada téxel. Con una malla densa, cada cara abarca pocos téxeles y la variación de alta frecuencia presente en el volumen de atributos se reproduce téxel a téxel. Con una malla dispersa, cada cara abarca muchos téxeles y la interpolación de grid_sample sobre esa superficie puede actuar de forma parecida a un filtro paso-bajo. El experimento muestra la correlación —el lowpoly tiene menos hf_std—, no demuestra el mecanismo causal directamente; en parte será eso, en parte que hay menos detalle. El orden (lowpoly < highpoly, ambos a 2048) se mantiene tras normalizar la métrica a una resolución común.

Fidelidad de forma frente a fidelidad de textura

El sil_IoU es prácticamente constante en las 36 configuraciones: taza ~0,95, zapatilla ~0,90, silla ~0,58, sin moverse con pasos, cuantización, textura ni densidad de malla. La forma la fijan las etapas de estructura dispersa y SLAT; lo demás casi no toca la silueta. El 0,58 de la silla son sus patas cromadas finas: una IoU normalizada a bounding box penaliza mucho lo que sobresale y es delgado —es más artefacto de métrica que fallo de calidad—.

Limitaciones del método

  • hf_std es sensible a la resolución del atlas: un filtro de tamaño fijo mide una banda espacial distinta en 1024 y en 2048. Salvo la tabla exploratoria de diez diagnósticos —identificada explícitamente como medida sobre atlas nativo—, todos los hf_std comparativos del artículo se calculan sobre el atlas llevado a 1024 px; una primera versión sin normalizar sobrestimaba la ventaja de la textura 2048 (aproximadamente el doble). Aun normalizado, mezcla «más limpio» con «menos detalle»: el lowpoly puntúa bajo en parte por carecer de alta frecuencia. Hay que mirar también las láminas.
  • sil_IoU es la mejor de 64 vistas, normalizada a bounding box: mide que exista un ángulo con esa silueta, no la fidelidad geométrica 3D ni la pose, escala u orientación. Es deliberadamente optimista.
  • Los renders del banco eran turntable plano con albedo; las láminas de este artículo son Blender/Cycles con luz de estudio, que es más justo pero tampoco es un visor de motor real.
  • El margen de VRAM se midió con el escritorio usando ~0,8 GB. Un escritorio más cargado, o una segunda app GPU, se acerca al OOM. En headless el margen sería mayor.
  • La pista de contraste (Q4 / 1024) usó una versión anterior del relleno de gutter (mediana), algo peor que el gaussiano definitivo: sus hf_std son un techo, no el mejor Q4 posible.

Límites prácticos de TRELLIS 2

Para no vender la moto, lo que no salió bien:

  • La silla. Metal fino, madera brillante y fondo oscuro fue el escenario complicado. En textura 1024 —sobre todo con Heun— aparece un moteado en la madera: el modelo hornea iluminación e incertidumbre como si fueran color. La textura 2048 lo elimina casi por completo, pero conviene saber que está ahí.
  • Mallas abiertas. Ninguna de las 36 salió watertight. O-Voxel es una representación pensada para superficies abiertas y geometría non-manifold, así que esto es esperable, no un fallo: para render, videojuegos o VFX no molesta. Para impresión 3D o fabricación sí haría falta cerrar la malla.
  • Más pasos no arreglan una referencia difícil. Si la foto de partida es ambigua, subir la calidad del muestreo no la desambigua.

Microsoft habla de assets de alta fidelidad con materiales PBR, pero la propia ficha oficial advierte que el modelo base no está alineado con preferencias humanas y que los resultados pueden variar. No cualquier foto da un modelo listo para producción sin revisar.

Qué configuración usaría en 8 GB

ParámetroValor
CuantizaciónQ8_0
Pipeline1024_cascade
Textura2048
SamplerEuler
Pasos12/12/12 (mejor equilibrio en los tres objetos). 8/8/8 va casi igual y más rápido; por encima de 12/12/12 no hubo mejora consistente
Lowpoly8.000 caras para geometría sencilla o media; 20.000 para objetos complejos como la zapatilla
Configuración recomendada para una GPU de 8 GB.

El workflow

Comparto el flujo de ComfyUI que usé, ya con esta configuración: cargas tu imagen y sale el GLB. En Ampere o posteriores (RTX 30/40/50) no hace falta la recompilación específica para sm_75; el resto de la compatibilidad (drivers, PyTorch, wheels del nodo) es la del propio ComfyUI-Trellis2-GGUF, no la he probado en esas tarjetas.

En una GPU Turing (RTX 20xx, GTX 16xx) hace falta además recompilar los cuatro kernels para sm_75 y aplicar los parches. El descargable «Instalación en Turing» trae un script que descarga las fuentes y las compila en tu máquina, junto con los parches y las instrucciones. En Ampere o posterior no hace falta.

Conclusiones: cinco preguntas

¿Puede TRELLIS 2 funcionar en una GPU de 8 GB?
Sí, en esta RTX 2080 SUPER modificando el stack: 36/36 y 0 OOM.

¿Consume realmente solo 5 GB?
El proceso rondó los 5,3 GB; la tarjeta completa llegó a ~7,3–7,4 GB.

¿Q4 merece la pena frente a Q8?
No por VRAM de ejecución en esta implementación. Me quedo con Q8.

¿Highpoly es siempre mejor?
No. 8.000 caras bastaron para objetos sencillos; los más complejos, como la zapatilla, pidieron 20.000. En ambos casos, mejor relación entre tiempo, tamaño y forma que la malla completa.

¿Es rápido?
Depende más de lo que haga la malla y del desplegado de UV que de la VRAM. El rango completo fue 115–638 s.

TRELLIS 2 en 8 GB: lo que realmente puede hacer una RTX 2080 SUPER

Vídeo creado con uno de los objetos de la prueba de laboratorio + MCP + Blender + IA. Pulsa para apoyar el proyecto.

La RTX 2080 SUPER salió en 2019. El repositorio oficial de TRELLIS 2 pide al menos 24 GB de VRAM y Microsoft publica sus cifras sobre una H100. La mía tiene ocho.

Después de recompilar cuatro kernels, corregir un par de cosas que Turing decidió interpretar con creatividad y hacer 36 generaciones, sigue aquí: 36 terminadas, ningún OOM y modelos 3D texturizados saliendo en unos pocos minutos.

Eso sí, tampoco hay que emocionarse. La taza salió con un asa en la que caben las dos manos y la silla tiene cierto aire de mobiliario de guardería. Y no creo que GTA VI vaya a poner esas zapatillas a ninguno de sus personajes.

Pero TRELLIS 2, cuyo stack oficial parte de 24 GB, está generando objetos 3D completos en una RTX de 2019 con ocho. Para mí, ese era el experimento.