Iconos de objetos renderizados desde modelos 3D
Convierte los prefabs del juego en sprites de iconos consistentes desde código: una escena de previsualización aislada, una cámara ortográfica con tres luces direccionales, una pose elegida por modelo, un renderizado con supersample 2×, downsampling premultiplicado y un contorno oscuro para que los iconos se vean sobre cualquier fondo. Re-ejecutable: regenera cuando el arte o las formas de los objetos cambien.
Flujo de trabajo
- Verifica el pipeline. La plantilla usa URP (
UniversalAdditionalCameraData). Para Built-in, elimina ese bloque; para HDRP, usaHDAdditionalCameraDatay desactiva el post-procesado de la misma forma. - Copia
templates/Editor/ModelIconRenderer.csen una carpeta/asmdef solo para editor (en un asmdef hace referencia a URP runtime) y renombra el namespace. - Define el mapeo de datos del juego a trabajos: qué prefab representa cada objeto, su tamaño de celda (1×1 para iconos simples; la forma de cuadrícula del objeto para inventarios de cuadrícula) y la ruta de salida. Mantén ese mapeo en el código de contenido del proyecto, no en el renderizador.
- Llama a
ModelIconRenderer.RenderBatch(jobs)desde un elemento de menú o el paso de construcción del contenido, luego asigna los sprites resultantes a los datos del objeto (AssetDatabase.LoadAssetAtPath<Sprite>(path)),SetDirty,SaveAssets. Los PNG se importan como sprites (alfa es transparencia, sin mipmaps). - Revisa cada icono con
scripts/icon_sheet.pyy corrige los casos problemáticos individualmente (ver Bucle de revisión) en lugar de ajustar las heurísticas globales. - Sube los PNG usando Git LFS. No subas iconos renderizados de arte con licencia a repositorios públicos.
Cómo se elige la pose
- Modelos alargados (el eje más largo > 1.6 veces el mediano: espadas, bastones, flechas, varitas) se colocan a lo largo del eje largo de la ranura - vertical en ranuras altas, horizontal en ranuras anchas, diagonal en ranuras cuadradas - luego se giran 25° alrededor de ese eje para que el lado mire a la cámara.
- Modelos compactos (gemas, pociones, libros, cofres) comienzan desde una vista 3/4 vertical, luego un barrido de guiñada (−90…90), cabeceo (−50, −25, 0) y alabeo (0…135) renderiza una miniatura y mantiene la vista con la mayor cantidad de píxeles de silueta. La cobertura de píxeles en lugar de los límites evita que diagonales finas ganen; las vistas frontales se penalizan un 7% porque se ven planas; la pose automática obtiene un 10% de bonificación para que solo se reemplace por una vista claramente mejor.
- Modelo compacto en una ranura alargada (cobertura < 30%, ranura ≥ 3:1): se duplican copias a lo largo de la ranura con ligera variación de rotación/escala (tres fragmentos en una ranura 1×3) en lugar de un modelo pequeño.
- Orientación: la mayoría de los paquetes (incluido Synty) crean las piezas mirando +Z; la cámara mira a lo largo de +Z, por lo que los modelos reciben un giro de guiñada de 180° primero (
Options.facingCorrection). Cámbialo si un paquete mira hacia otra dirección.
Bucle de revisión
Nunca confíes en un lote que no hayas revisado. Una hoja de contactos detecta lo que cien líneas de registro no detectarán.
- Hoja:
python scripts/icon_sheet.py <carpeta_iconos> sheet.png --match ^Armor_ Shield --strip-prefix, luego visualiza la imagen. Los iconos mantienen su relación de aspecto, por lo que un objeto 3×1 se muestra como una tira, tal como aparecerá en la cuadrícula. - Clasifica cada icono malo - la solución varía:
- De lado / tumbado / de espaldas a la cámara → pósalo con
rotationOverride. Se aplica comoEuler(override) * facingCorrection, por lo que valores como(0,0,0)significan "tal como fue creado, mirando a la cámara": pequeñas inclinaciones como(-5, 15, 0)dan una vista 3/4 vertical.(0, 180, 0)muestra la parte trasera. Cero significa "automático", así que usa un valor no nulo pequeño si quieres la pose exacta de creación. - Objeto plano visto de canto (escudo, placa, moneda) → el prefab suele tener un hijo rotado (los prefabs de visualización de armas suelen rotar la malla 90° en X para que quede plana en el juego). Registra los
localEulerAnglesdel hijo antes de adivinar; cancélalos en el override y añade un alabeo para colocar el lado largo a lo largo de la ranura, ej.(0, 90, 80)para un escudo ovalado plano en una ranura 3×1. - Silueta incorrecta para la ranura (un modelo alto y delgado en una ranura cuadrada: maniquíes de entrenamiento, soportes de armadura, banderas en postes) → ninguna rotación lo soluciona; cambia el modelo.
- De lado / tumbado / de espaldas a la cámara → pósalo con
- Prueba modelos de reemplazo en un solo render: apunta temporalmente varios objetos existentes a prefabs candidatos (
item.iconModel = candidate, override cero), renderiza, genera hoja, elige. Luego coloca los ganadores en el código de contenido; si la construcción de contenido reasigna modelos en cada ejecución, las asignaciones temporales se sobrescribirán de todos modos. - Guarda las poses en el código de contenido, no solo en los assets, para que una reconstrucción con "sobrescribir datos" las mantenga. Aplica una pose registrada solo cuando el objeto no tenga la suya propia, para que las poses ajustadas a mano en el Inspector sobrevivan.
- Vuelve a renderizar y generar la hoja hasta que cada icono se entienda de un vistazo.
Buenas fuentes de modelos para equipo: las partes estáticas de personajes modulares (ej. Synty Modular Fantasy Hero Characters_ModularParts_Static/Chr_Torso_*_Static) producen iconos de armadura de pecho mucho mejores que objetos como soportes de armadura o maniquíes: llenan una ranura cuadrada y se reconocen inmediatamente como "armadura". Los prefabs de accesorios de personaje (cascos, hombreras, capas) funcionan bien para ranuras más pequeñas. Las partes estáticas usan el material de textura normal del paquete, por lo que se convierten a tu shader como cualquier objeto.
Problemas comunes
- Las escenas de previsualización no tienen iluminación ambiental ni cielo - sin las luces de relleno y de borde, el lado opuesto a la luz principal se renderiza en negro.
- El post-procesado y MSAA rompen el canal alfa. Desactiva el post-procesado en la cámara de iconos (tonemapping/bloom escriben alfa = 1) y usa supersample en lugar de MSAA.
- Configura
cam.scene = previewSceneo la cámara también renderizará la escena abierta. - Elimina colliders, desactiva Animators, particle/trail/line renderers en la instancia - los hijos con VFX y animadores de pose de ligadura inflan los límites o aparecen como manchas.
- Premultiplica al hacer downsampling, o los bordes transparentes tomarán bordes oscuros del color de fondo.
- Los personajes con esqueleto se renderizan en pose de ligadura (pose T); dales un
rotationOverrideo una variante de prefab con pose. - Los modelos low-poly con texturas de paleta se ven grises si sus texturas de paleta tienen mipmaps (consulta el skill
unity-asset-pack-extraction).
Archivos
templates/Editor/ModelIconRenderer.cs-RenderBatch(IconJob[], Options),IconJob(model, cells, pngPath, rotationOverride), menú Tools > Icons > Render Selected Prefabs (iconos 1×1 en una carpetaIcons/junto a cada prefab).scripts/icon_sheet.py- hoja de contactos etiquetada de iconos renderizados, filtrable por nombre (necesita Pillow).