feat(gamedev): comfyui_build_weather_overlay_workflow — overlays de clima/atmósfera

Builder puro (dict API format) de capas de clima a pantalla completa: lluvia,
niebla, nieve, god rays, polvo, viñeta de tormenta. Capa de cobertura total
apaisada (16:9, 1024x576) que se superpone sobre la escena con blend del motor.

Dos modos via on_black: True (defecto) genera el clima brillante sobre negro
puro como insumo de comfyui_matting_luma_to_alpha (blend aditivo/screen);
False genera una pelicula translucida semi-transparente (multiply/overlay).
NO inyecta Rembg: el matting de una capa es luma->alpha de disco. Compone
comfyui_build_txt2img_workflow + comfyui_inject_lora.

Diferenciado de decal_overlay (mancha localizada) y vfx_spritesheet (secuencia
animada de un efecto puntual): aqui es una pelicula estatica de cobertura
full-screen que el motor anima por scroll/loop/shader.

10 tests offline verdes. Probado e2e en GPU SD1.5 8GB lowvram: heavy rain
on_black seed 11 1024x576 (16:9 exacto), estrias brillantes sobre negro plano
(esquinas luma 0.00, dark 89.6%) apto luma->alpha (prompt_id 5d2300d1).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-27 02:26:39 +02:00
parent 2a7c77cb56
commit 8a4cc323a3
4 changed files with 559 additions and 0 deletions
+1
View File
@@ -58,6 +58,7 @@ VFX (ver `reports/0143`).
| `comfyui_build_foliage_set_workflow_py_ml` | `(plant, *, view="side", style="game foliage, stylized", checkpoint="dreamshaper_8…", size=512, transparent=True, seed=0, lora=None, …) -> dict` | UN elemento de **vegetación/foliage** de escenario (árbol, arbusto, hierba alta, flores, helecho, hongo, cactus, tronco caído, juncos, hiedra): UN elemento de **naturaleza ORGÁNICA AISLADO** y centrado a perspectiva de juego (`{view} view`, `side` por defecto), fondo limpio recortable a alpha (`{plant}, {view} view, {style}, single plant element, centered, plain background, game nature asset, natural vegetation, organic, isolated plant…`) → txt2img cuadrado + LoRA estilo opcional + Rembg (alpha). **Vegetación que viste el terreno**, distinta del **objeto MANUFACTURADO** suelto (≠ `prop_object`: barril/cofre/mueble) y del **EDIFICIO** (≠ `structure`: casa/torre); el negativo rechaza `building / manmade object / barrel / furniture / person` y `multiple plants / dense forest / jungle / landscape` (UN elemento, no un bosque) + `pot / planter / vase` (planta en maceta = `prop_object`). Recorte por **Rembg** (planta opaca de silueta definida), no luma→alpha. Set coherente = mismo `view`/`style`/`checkpoint`/`lora`, varía solo `plant`. ⚠️ **dos gotchas reales SD1.5+Rembg**: (1) **plantas grandes (árbol) tienden a PAISAJE** (cielo+campo) en lugar de fondo plano → re-roll de seeds buscando fondo uniforme (`comfyui_batch_generate`); (2) **follaje verde claro sobre fondo claro → Rembg se come las hojas** y deja solo tronco/ramas → preferir elementos de **silueta compacta y color saturado** (hongo, arbusto denso) o `transparent=False` + matting manual. Probado e2e en GPU con SD1.5 — golden `a glowing mushroom` seed 7 512×512 RGBA, hongo centrado recortado a alpha limpio (centroide 0.51/0.58, opaco 19%, `prompt_id 8fb65a51`); evidencia del gotcha del roble en `reports/0170`. SD1.5. |
| `comfyui_build_trap_hazard_workflow_py_ml` | `(hazard, *, view="side", style="game hazard trap", checkpoint="dreamshaper_8…", size=512, transparent=True, seed=0, lora=None, …) -> dict` | UNA **trampa/peligro JUGABLE** de nivel (pinchos del suelo, sierra giratoria, foso de lava, placa de presión, columna de llamas, trampa de flechas, charco ácido, descarga eléctrica, prensa, estaca cayendo): UN objeto de **peligro AISLADO** y centrado a perspectiva de juego (`{view} view`, `side` por defecto), fondo limpio recortable a alpha (`{hazard}, {view} view, {style}, single hazard object, trap, dangerous, centered, plain background, game asset, high detail`) → txt2img cuadrado + LoRA estilo opcional + Rembg (alpha). **Peligro al que el motor asigna hitbox de daño + estado activo/inactivo**, distinto del **objeto INERTE de decoración** (≠ `prop_object`: barril/cofre que solo ambienta) y del **enemigo VIVO** (≠ `enemy_creature`); el negativo rechaza `character / person / creature / multiple objects` para que salga el mecanismo, no un enemigo ni una escena. Recorte por **Rembg** (trampa sólida de silueta definida: pinchos/sierra/placa); ⚠️ para hazards **puramente etéreos** (columna de llamas, arco eléctrico, gas) usar `transparent=False` + `comfyui_matting_luma_to_alpha` (conserva el falloff translúcido para blend aditivo), no Rembg. `view` fija la perspectiva del nivel (side/top-down/iso); set coherente = mismo `view`/`style`/`checkpoint`/`lora`, varía solo `hazard`. Probado e2e en GPU con SD1.5 — `spiked floor trap` side seed 7 512×512 RGBA, mecanismo de peligro centrado recortado a alpha (alpha extrema 0255, fondo transparente real, `prompt_id ab1b1560`, `reports/0174`). SD1.5. |
| `comfyui_build_particle_texture_workflow_py_ml` | `(particle, *, soft=True, style="particle texture, soft glow", checkpoint="dreamshaper_8…", size=256, seed=0, lora=None, …) -> dict` | UNA textura de **partícula individual** reutilizable (chispa, humo, polvo, destello/flare, gota, copo, hoja, círculo de energía) — el "ladrillo" que el sistema de partículas del motor (Godot `GPUParticles2D`, Unity VFX Graph) instancia a **miles** y anima (spawn/fade/color over lifetime). Aislada y centrada **sobre fondo NEGRO** (`{particle} particle, {style}, isolated on pure black background, <soft|sharp> edges, single element, for game particle system…`) → txt2img cuadrado + LoRA estilo opcional. **`soft` controla el borde**: `soft=True` (defecto) → `soft glow, feathered edges` (humo/destello/gota); `soft=False``crisp sharp edges, high contrast` (chispa/copo/hoja). **NO inyecta Rembg** (rompería el falloff translúcido): insumo de **`comfyui_matting_luma_to_alpha`** (luma=alpha, additive blend en el motor). **`size` por defecto pequeño (256)** porque se replica a miles. **DISTINTO de `vfx_spritesheet`** (ese es la SECUENCIA animada de un efecto; esto es UNA textura estática reutilizable) **y de `decal_overlay`** (ése es una mancha de desgaste estática para superponer; éste es un emisor de partículas). ⚠️ el `style` por defecto trae "soft glow" → si pides `soft=False` para algo nítido, usa un `style` sin connotación suave. Probado e2e en GPU con SD1.5 — `spark` 256×256 sobre negro plano (dark 85%) + luma→alpha RGBA con falloff preservado (`reports/0163`). SD1.5. |
| `comfyui_build_weather_overlay_workflow_py_ml` | `(weather, *, on_black=True, style="weather overlay, atmospheric", checkpoint="dreamshaper_8…", width=1024, height=576, seed=0, lora=None, …) -> dict` | UNA **capa de clima/atmósfera a PANTALLA COMPLETA** que cubre toda la vista del jugador y se superpone sobre la escena con blend del motor (lluvia, niebla, nieve, rayos de sol/god rays, polvo, viñeta de tormenta): cobertura **uniforme de borde a borde**, generada **APAISADA a resolución de pantalla** (16:9, 1024×576 por defecto — `width>height`, NO cuadrado) (`{weather} overlay, {style}, full screen atmospheric layer, <particles/streaks on pure black background \| translucent layer>, seamless full screen coverage, edge to edge, game VFX…`) → txt2img apaisado + LoRA estilo opcional. **`on_black` elige el modo de blend**: `on_black=True` (defecto) = clima BRILLANTE sobre **NEGRO puro** (estrías de lluvia, copos, haces de luz, motas), **sin Rembg**, insumo de **`comfyui_matting_luma_to_alpha`** (luma=alpha, **blend aditivo/screen** — el negro desaparece, el clima brilla sobre la escena); `on_black=False` = **película TRANSLÚCIDA** semi-transparente (niebla densa, tinte de tormenta) para blend multiply/overlay o alpha global. El negativo rechaza `solid object/single subject/character/building/landscape scene/horizon line/frame` (cobertura total, NO un sujeto centrado) + (si `on_black`) `blue sky/gray/white background` para forzar negro plano. **DISTINTO de `decal_overlay`** (ése es una mancha LOCALIZADA que se pega en un punto de una superficie) **y de `vfx_spritesheet`** (ése es la SECUENCIA animada de UN efecto puntual): la capa de clima es UNA película estática de cobertura full-screen que el motor anima por scroll/loop/shader. Set coherente = mismo `style`/`checkpoint`/`lora`, varía `weather`/`seed`. ⚠️ algunos climas (lluvia/niebla) pintan cielo azul de fondo en SD1.5 aunque pidas negro → subir `cfg` + re-roll de `seed`; climas brillantes-sobre-negro (god rays, snow, sparks) salen más limpios que los difusos (fog). luma Rec601 penaliza el azul → para lluvia azulada ajustar `luma_weights`/`gamma` en el matting. Probado e2e en GPU con SD1.5 — `heavy rain` on_black seed 11 **1024×576** (16:9 exacto), estrías de lluvia brillantes sobre **negro plano** (esquinas luma 0.00, dark 89.6%, lluvia 1.4% brillante) apto luma→alpha aditivo (`prompt_id 5d2300d1`, `reports/0176`). SD1.5/SDXL. |
| `comfyui_build_rune_glyph_workflow_py_ml` | `(glyph, *, glow=True, style="arcane glowing rune", checkpoint="dreamshaper_8…", size=512, seed=0, lora=None, …) -> dict` | UNA **runa / glifo / sigilo mágico** (glifos rúnicos, círculos mágicos, sigilos de invocación, inscripciones brillantes) para hechizos, portales, marcas de conjuro y efectos de magia: símbolo arcano **aislado** sobre fondo uniforme (`{glyph}, {style}, magic symbol, single isolated glyph, centered, glowing on a solid pure black background, occult sigil, arcane inscription, no scenery, game asset…`) → txt2img cuadrado + LoRA estilo opcional. **`glow` elige el camino a alpha**: `glow=True` (defecto) = runa BRILLANTE sobre **NEGRO puro**, **sin Rembg** (recortaría el halo del resplandor), insumo de **`comfyui_matting_luma_to_alpha`** (luma=alpha, **blend aditivo** en el motor — conserva el glow); `glow=False` = runa MATE/grabada sobre fondo plano (el negativo rechaza `glow/neon/bloom`), recorte/inversión por el caller. El negativo rechaza `realistic text/readable words/latin alphabet` (un glifo arcano, **no letras reales**) + fondo texturizado/niebla. **DISTINTO de `status_effect_icon`** (símbolo SÓLIDO de UI, recorte Rembg, legible a 16-32 px en el HUD): la runa es una marca translúcida que **emite luz** e se inscribe en el mundo. Grimorio coherente = mismo `style`/`checkpoint`/`lora`, varía `glyph`/`seed`. ⚠️ luma Rec601 penaliza el rojo → para runas rojas (sigilo demoníaco) pasar `luma_weights` con más peso al rojo + subir `gamma`; runas blancas/azules/doradas van con pesos por defecto. Probado e2e en GPU con SD1.5 — `circular summoning rune` glow seed 11 512×512, círculo de invocación brillante sobre **negro puro** (esquinas luma 0.00, dark 83%, runa 3.4% brillante, max 255) apto luma→alpha (`prompt_id 701d149a`, `reports/0172`). SD1.5. |
| `comfyui_build_title_lettering_workflow_py_ml` | `(text, *, letter_style="epic fantasy metallic", checkpoint="juggernaut_xl_v11…", width=1024, height=512, transparent=True, seed=0, lora=None, …) -> dict` | EL texto/logo de **título** de un juego (el nombre del juego o una palabra) renderizado con un **tratamiento de lettering** (metálico, tallado en fuego/piedra/madera, neón, cristal, oro), formato **apaisado** (`width>height`, 1024×512 por defecto), fondo plano recortable a alpha (`the word "{text}" as a game logo, {letter_style} lettering, stylized typography, centered, plain background…`) → txt2img apaisado + LoRA estilo opcional + Rembg (alpha). El **negativo NO rechaza texto** (el lettering es el sujeto) y empuja contra el ruido textual (`extra letters/jumbled text/deformed letters`). El VALOR es el ESTILO del lettering, **NO** la fidelidad tipográfica: ⚠️ la difusión renderiza texto de forma imperfecta — letras de más, deformadas o mal escritas; mitigar con palabras CORTAS en MAYÚSCULA, **re-roll de seeds** (`comfyui_batch_generate`), SDXL > SD1.5 para texto, o pintar el texto real con una fuente en el motor. **Una palabra que es un objeto concreto (DRAGON) → el modelo dibuja el objeto, no las letras** — usar palabras abstractas o reforzar `letter_style`. Marca coherente = mismo `letter_style`/`checkpoint`/`lora`, varía solo `text`. Recorte por **Rembg** (logo sólido), no luma→alpha. Probado e2e en GPU: `DRAGON`/`fire engraved` SD1.5 1024×512 → ilustró dragones rojos (alpha OK, confirma el gotcha de palabra-objeto, `prompt_id 6f3920b7`); `AETHER`/`epic fantasy metallic` SDXL 768×384 → **logo de texto metálico dorado** legible con ortografía imperfecta + alpha (`prompt_id 2a7fe8ba`, `reports/0165`). SD1.5/SDXL. |
@@ -0,0 +1,174 @@
---
name: comfyui_build_weather_overlay_workflow
kind: function
lang: py
domain: ml
purity: pure
version: 1.0.0
signature: 'def comfyui_build_weather_overlay_workflow(weather: str, *, on_black: bool = True, style: str = "weather overlay, atmospheric", checkpoint: str = "dreamshaper_8.safetensors", width: int = 1024, height: int = 576, seed: int = 0, lora: str | None = None, lora_strength: float = 1.0, negative: str | None = None, steps: int = 28, cfg: float = 7.0, sampler_name: str = "dpmpp_2m", scheduler: str = "karras", filename_prefix: str = "weather_overlay") -> dict'
description: "Construye el dict (API format) del workflow de UN overlay de clima/atmosfera a PANTALLA COMPLETA: una capa que cubre toda la vista y se superpone sobre la escena con un blend mode del motor — lluvia, niebla, nieve, rayos de sol (god rays), polvo, viñeta de tormenta. Cobertura uniforme de borde a borde, generada APAISADA a resolucion de pantalla (16:9, 1024x576 por defecto). on_black=True (defecto) la pone sobre NEGRO puro con el clima brillante encima, para extraer alpha por luminancia con comfyui_matting_luma_to_alpha y blend additive/screen; on_black=False = pelicula translucida semi-transparente (niebla, tinte) para multiply/overlay o alpha global. NO inyecta Rembg (el matting de una capa es luma-to-alpha o alpha global, no un nodo). Compone comfyui_build_txt2img_workflow + comfyui_inject_lora. Hermano de comfyui_build_decal_overlay/particle_texture/vfx_spritesheet_workflow; distinto: capa de cobertura TOTAL, no mancha localizada ni secuencia animada. Pura, sin red ni I/O. class_types verificados contra /object_info."
uses_functions:
- comfyui_build_txt2img_workflow_py_ml
- comfyui_inject_lora_py_ml
uses_types: []
returns: []
returns_optional: false
error_type: ""
tags: [comfyui, ml, gamedev, gamedev-2d, weather, overlay, atmosphere, rain, fog, snow, godrays, fullscreen, alpha, blend, luma, workflow]
tested: true
test_file_path: python/functions/ml/comfyui_build_weather_overlay_workflow_test.py
tests:
- test_golden_weather_on_black_recipe
- test_golden_landscape_dims_default
- test_edge_on_black_toggles_layer_mode
- test_edge_weather_reflected
- test_edge_style_in_prompt
- test_edge_dims_reflected
- test_edge_negative_isolates_layer
- test_edge_lora_reflected
- test_error_empty_weather
- test_determinism
file_path: python/functions/ml/comfyui_build_weather_overlay_workflow.py
imports: []
params:
- name: weather
desc: "Descripcion del clima/atmosfera a generar como capa (ej. 'heavy rain', 'thick fog', 'falling snow', 'god rays', 'sun shafts', 'dust haze', 'storm vignette', 'sandstorm', 'ash fall', 'blizzard'). Se inserta en un prompt scaffold de capa de cobertura total. No puede estar vacio."
- name: on_black
desc: "Si True (defecto) genera la capa sobre fondo NEGRO puro con el clima BRILLANTE encima (estrias de lluvia, copos, haces de luz, motas), pensada para luma->alpha con comfyui_matting_luma_to_alpha y blend additive/screen en el motor (el negro desaparece, el clima brilla sobre la escena). False = pelicula TRANSLUCIDA semi-transparente (niebla densa, tinte de tormenta, bruma) para blend multiply/overlay o alpha global. keyword-only."
- name: style
desc: "Descriptor de estilo de la capa (ej. 'weather overlay, atmospheric', 'stylized rain, painterly', 'photoreal fog', 'anime sun rays'). Pasa el MISMO style + checkpoint + lora a todas las capas de un set para coherencia visual. keyword-only."
- name: checkpoint
desc: "Checkpoint del servidor. 'dreamshaper_8.safetensors' (SD1.5, holgado en 8GB lowvram) por defecto; 'juggernaut_xl_v11.safetensors' para SDXL (mas VRAM, subir width/height a 1344x768). keyword-only."
- name: width
desc: "Ancho de la capa en px (multiplo de 8). 1024 por defecto (16:9 con height 576: resolucion de pantalla apaisada). keyword-only."
- name: height
desc: "Alto de la capa en px (multiplo de 8). 576 por defecto. keyword-only."
- name: seed
desc: "Semilla del KSampler. Misma seed + mismos weather/style -> misma capa; variar seed da variantes del mismo tipo de clima. keyword-only."
- name: lora
desc: "LoRA de estilo opcional en models/loras. None = sin LoRA. keyword-only."
- name: lora_strength
desc: "Fuerza del LoRA sobre model y clip. Se clampa a [0.0, 2.0]. keyword-only."
- name: negative
desc: "Prompt negativo. None usa el negativo por defecto pensado para una capa de cobertura total (sin sujeto/objeto/escena/horizonte/profundidad/marco que rompan la pelicula atmosferica) MAS, si on_black=True, el refuerzo de fondo negro plano. keyword-only."
- name: steps
desc: "Pasos del KSampler. keyword-only."
- name: cfg
desc: "CFG del KSampler. keyword-only."
- name: sampler_name
desc: "Sampler del KSampler. keyword-only."
- name: scheduler
desc: "Scheduler del KSampler. keyword-only."
- name: filename_prefix
desc: "Prefijo del PNG en output/. keyword-only."
output: "dict en API format listo para comfyui_submit_workflow: base txt2img apaisada (16:9) con prompt scaffold de capa de clima ('{weather} overlay, {style}, full screen atmospheric layer, <particles/streaks on pure black background | translucent layer>, seamless full screen coverage, edge to edge, game VFX, ...') + el negativo rechaza sujeto/objeto/escena/horizonte/profundidad/marco que romperian la pelicula de cobertura (y refuerza fondo negro plano cuando on_black=True) + LoRA de estilo opcional. NO lleva Rembg: con on_black=True el PNG resultante se convierte a RGBA con comfyui_matting_luma_to_alpha (luma=alpha) en un paso posterior para blend aditivo. UNA capa; variar seed da variantes del mismo clima."
---
Construye el dict (API format) del workflow de UN overlay de clima/atmosfera a
**pantalla completa**: una CAPA que cubre TODA la vista del jugador y se superpone
sobre la escena del juego con un blend mode del motor — lluvia, niebla, nieve, rayos
de sol (god rays), polvo en suspension, viñeta de tormenta. No es un sujeto centrado
ni un efecto puntual: es una pelicula de cobertura uniforme de borde a borde, generada
APAISADA a resolucion de pantalla (16:9, 1024x576 por defecto). `on_black=True`
(defecto) la pone sobre NEGRO puro con el clima brillante encima, pensada para extraer
alpha por luminancia con `comfyui_matting_luma_to_alpha` y blendear additive/screen
(el negro desaparece, el clima brilla sobre la escena); `on_black=False` la genera como
pelicula translucida semi-transparente (niebla densa, tinte de tormenta) para blend
multiply/overlay o alpha global. NO inyecta Rembg (el matting de una capa es
luma-to-alpha o un alpha global, no un nodo). Compone `comfyui_build_txt2img_workflow`
+ `comfyui_inject_lora` (estilo opcional). Hermano de
`comfyui_build_decal_overlay_workflow` / `comfyui_build_particle_texture_workflow` /
`comfyui_build_vfx_spritesheet_workflow`. Pura, sin red ni I/O. class_types verificados
contra /object_info.
## Cuando usarla
Cuando necesites una CAPA de clima/atmosfera que cubra toda la pantalla y se superponga
sobre la escena del juego: lluvia cayendo sobre todo el nivel, niebla densa que tiñe la
vista, nieve, haces de luz volumetrica (god rays), polvo en suspension, un oscurecimiento
de bordes de tormenta. La capa se genera apaisada a resolucion de pantalla y se compone en
el motor con un blend mode (additive/screen para particulas brillantes sobre negro;
multiply/overlay o alpha global para una pelicula translucida).
Flujo tipico despues de generar:
1. `comfyui_build_weather_overlay_workflow("heavy rain", on_black=True)` -> dict.
2. `comfyui_submit_workflow` -> `comfyui_wait_result` -> `comfyui_fetch_output_image`
(PNG de la capa de lluvia sobre negro).
3. `comfyui_matting_luma_to_alpha(png, gamma=..., black_point=...)` -> PNG RGBA donde la
luminancia ES el alpha: estrias brillantes=opacas, negro=transparente. Blend additive
en el motor encima de la escena.
Pasa el MISMO `style` + `checkpoint` + (`lora`) a todas las capas de un set de clima para
que combinen; varia `seed` para sacar variantes del mismo clima (varias capas de lluvia
distintas para alternar/loopear en el motor).
**Elige este builder y NO `comfyui_build_decal_overlay_workflow` ni
`comfyui_build_vfx_spritesheet_workflow` cuando** lo que quieres es una capa de cobertura
TOTAL de pantalla. Un decal es una mancha LOCALIZADA y recortable que se pega en un punto
de una superficie; un spritesheet es una SECUENCIA animada de N frames de UN efecto
puntual. El weather overlay es UNA pelicula estatica de cobertura full-screen que el motor
anima por scroll/loop/shader.
## Gotchas
- **on_black=True esta pensado para luma->alpha, no es opcional de adorno**: la capa se
genera sobre NEGRO puro precisamente para que `comfyui_matting_luma_to_alpha` mapee la
luminancia a alpha y conserve el falloff de las estrias/particulas (lluvia, nieve, haces
de luz). Con additive/screen el negro desaparece y solo el clima brilla sobre la escena.
Si extraes el alpha con un matting binario (rembg) pierdes el degradado. Para climas que
OSCURECEN/tiñen toda la vista (niebla densa, tinte de tormenta) usa `on_black=False`
(pelicula translucida) y blendea multiply/overlay o con un alpha global bajo.
- **NO inyecta Rembg a proposito**: a diferencia de los builders de sprite/prop/item, este
NO lleva 'Image Rembg (Remove Background)'. El SaveImage toma directo del VAEDecode (capa
sobre fondo uniforme) y el matting es un paso posterior de disco o un alpha del motor.
- **Es una funcion pura**: solo arma el dict. La generacion real (GPU) la hacen
`comfyui_submit_workflow` + `comfyui_wait_result` + `comfyui_fetch_output_image`; el alpha
lo hace `comfyui_matting_luma_to_alpha`.
- **La capa debe quedar de COBERTURA TOTAL, no un sujeto centrado**: si el modelo mete una
escena/objeto/personaje/horizonte, deja de ser una pelicula de cobertura y rompe el blend.
El positivo fuerza "full screen atmospheric layer, seamless full screen coverage, edge to
edge, uniform across frame" y el negativo rechaza "solid object, single subject, character,
building, landscape scene, horizon line, depth, frame, border". Si aun sale un sujeto, sube
`cfg` o reroll de `seed`.
- **on_black con cielo de fondo**: con SD1.5 algunos climas (lluvia, niebla) tienden a pintar
un cielo azul/gris de fondo aunque pidas negro, lo que arruina el luma->alpha. El negativo
por defecto (cuando on_black=True) rechaza "blue sky background, gray/white background,
background scenery". Si aun sale fondo no-negro, sube `cfg` y haz reroll de `seed`; los
climas mas brillantes-sobre-negro (god rays, snow, sparks) salen mas limpios que los
difusos (fog).
- **luma->alpha penaliza colores oscuros/saturados**: la luminancia Rec601 pesa poco el azul
(0.114). Una capa de lluvia AZULADA sobre negro sale semi-transparente con los pesos por
defecto. Para subir opacidad ajusta `luma_weights`/`gamma` en `comfyui_matting_luma_to_alpha`
(es ajuste del paso de matting, no del builder). Para clima blanco/brillante (nieve, haces
de luz, polvo claro) los pesos por defecto van perfectos.
- **Apaisado 16:9, no cuadrado**: por defecto 1024x576 (resolucion de pantalla). Para 21:9 u
otra relacion, ajusta width/height (multiplos de 8). En 8GB lowvram SD1.5 aguanta 1024x576
holgado; si subes a SDXL (juggernaut_xl) ve a 1344x768 y vigila OOM (baja resolucion si peta).
## Ejemplo
```python
import sys, os
sys.path.insert(0, os.path.join(os.environ["HOME"], "fn_registry", "python", "functions"))
from ml.comfyui_build_weather_overlay_workflow import comfyui_build_weather_overlay_workflow
# Una capa de lluvia full-screen sobre negro, lista para submit + luma-to-alpha.
wf = comfyui_build_weather_overlay_workflow(
"heavy rain",
on_black=True,
style="weather overlay, atmospheric",
seed=7,
)
# Pipeline completo de una capa de clima con alpha:
# r = comfyui_submit_workflow(wf); comfyui_wait_result(r["prompt_id"])
# png = comfyui_fetch_output_image(...) # capa de lluvia sobre negro
# rgba = comfyui_matting_luma_to_alpha(png, gamma=1.2, black_point=0.05) # luma=alpha
# Capa translucida (niebla que tiñe la vista, blend multiply):
# wf = comfyui_build_weather_overlay_workflow("thick fog", on_black=False)
# Variantes del mismo clima para loopear en el motor: mismo weather/style, cambia seed.
# for s in range(4):
# wf = comfyui_build_weather_overlay_workflow("falling snow", seed=s)
```
O lanzable directo con: `./fn run comfyui_build_weather_overlay_workflow` (imprime nodos + class_types del ejemplo).
@@ -0,0 +1,239 @@
"""Construye el workflow ComfyUI de UN overlay de clima a pantalla completa (API format).
Un weather overlay es una CAPA de atmosfera/clima que cubre TODA la pantalla y se
superpone sobre la escena del juego con un blend mode del motor: lluvia, niebla,
nieve, rayos de sol (god rays), polvo en suspension, viñeta de tormenta. NO es un
sujeto centrado ni un efecto puntual: es una pelicula de cobertura uniforme de borde
a borde que tiñe/anima toda la vista del jugador. Por eso se genera APAISADA a
resolucion de pantalla (16:9, 1024x576 por defecto), no cuadrada.
Dos modos segun como se vaya a componer en el motor:
- `on_black=True` (defecto): la capa se genera sobre fondo NEGRO puro, con las
particulas/estrias del clima BRILLANTES sobre negro (estrias de lluvia, copos de
nieve, haces de luz, motas de polvo). El PNG resultante esta pensado para pasar por
`comfyui_matting_luma_to_alpha` (brillante -> opaco, negro -> transparente) y
blendearse con additive/screen en el motor: el negro desaparece y solo el clima
brilla encima de la escena. Conserva el degradado/falloff que un matting binario
(rembg) destruiria.
- `on_black=False`: la capa se genera como una pelicula TRANSLUCIDA semi-transparente
(niebla densa, tinte de tormenta, bruma) lista para blendearse con multiply/overlay
o un alpha global bajo. Util para climas que oscurecen/tiñen toda la vista en vez de
añadir particulas brillantes.
Es el builder hermano de comfyui_build_decal_overlay_workflow /
comfyui_build_particle_texture_workflow / comfyui_build_vfx_spritesheet_workflow:
mismo patron PURO (dict API format) que compone funciones existentes del registry, no
reescribe el grafo. A diferencia de los builders de sprite/prop/item, este NO inyecta
Rembg: el matting de una capa de clima es luma-to-alpha (post-proceso de disco) o un
alpha global del motor, no un nodo del workflow.
Diferencia con sus hermanos (NO confundir):
- comfyui_build_vfx_spritesheet_workflow: una SECUENCIA animada de N frames de UN
efecto puntual (una explosion, una llamarada) via AnimateDiff. El weather overlay
es UNA capa estatica de cobertura total (el motor la anima por scroll/loop/shader).
- comfyui_build_decal_overlay_workflow: una mancha LOCALIZADA y recortable (sangre,
grieta) que se pega en un punto de una superficie. El weather overlay cubre la
pantalla ENTERA de borde a borde, no es una mancha centrada.
Cableado:
CheckpointLoaderSimple -> [LoraLoader opcional de estilo] -> KSampler
-> CLIPTextEncode (prompt scaffold de capa full-screen) ...
-> VAEDecode -> SaveImage (capa de clima apaisada)
Compone:
- comfyui_build_txt2img_workflow -> base txt2img apaisada (16:9)
- comfyui_inject_lora -> LoRA de estilo opcional
Pipeline despues de generar (no en este builder):
comfyui_submit_workflow -> comfyui_wait_result -> comfyui_fetch_output_image
-> comfyui_matting_luma_to_alpha (con on_black=True) -> PNG RGBA para blend aditivo
class_types/inputs verificados contra /object_info del servidor (8GB lowvram):
CheckpointLoaderSimple, CLIPTextEncode, EmptyLatentImage, KSampler, VAEDecode,
SaveImage, LoraLoader.
Funcion pura: sin red, sin I/O. Determinista para los mismos argumentos.
"""
from __future__ import annotations
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
# Negativo comun a cualquier capa de clima full-screen: queremos COBERTURA uniforme de
# borde a borde, SIN un sujeto/objeto solido, personaje, edificio, escena o horizonte
# que rompan la capa atmosferica; sin profundidad/marco/borde que delaten una
# composicion en vez de una pelicula de cobertura.
_WEATHER_NEGATIVE_COMMON = (
"solid object, single subject, centered object, foreground object, "
"character, person, creature, building, vehicle, landscape scene, "
"horizon line, depth of field, perspective, vignette frame, frame, border, "
"drop shadow, text, watermark, signature, logo, "
"blurry, low quality, jpeg artifacts"
)
# Refuerzo de fondo cuando on_black=True: el fondo DEBE quedar NEGRO plano para que el
# luma-to-alpha mapee la luminancia a alpha y conserve el falloff de las particulas/
# estrias. Sin esto el modelo puede pintar una escena/cielo de fondo que arruina el
# alpha. Rechaza explicitamente fondos de otro color/texturizados.
_WEATHER_BG_NEG_ON_BLACK = (
"gray background, white background, blue sky background, textured background, "
"busy background, background scenery, background pattern, gradient background"
)
def comfyui_build_weather_overlay_workflow(
weather: str,
*,
on_black: bool = True,
style: str = "weather overlay, atmospheric",
checkpoint: str = "dreamshaper_8.safetensors",
width: int = 1024,
height: int = 576,
seed: int = 0,
lora: str | None = None,
lora_strength: float = 1.0,
negative: str | None = None,
steps: int = 28,
cfg: float = 7.0,
sampler_name: str = "dpmpp_2m",
scheduler: str = "karras",
filename_prefix: str = "weather_overlay",
) -> dict:
"""Construye el dict (API format) del workflow de UNA capa de clima full-screen.
Args:
weather: descripcion del clima/atmosfera a generar como capa (ej. "heavy
rain", "thick fog", "falling snow", "god rays", "sun shafts", "dust
haze", "storm vignette", "sandstorm", "ash fall", "blizzard"). Se
inserta en un prompt scaffold de capa de cobertura total. No puede
estar vacio.
on_black: si True (defecto) la capa se genera sobre fondo NEGRO puro con el
clima BRILLANTE encima (estrias de lluvia, copos, haces de luz, motas),
pensada para extraer alpha por luminancia con
comfyui_matting_luma_to_alpha y blendear additive/screen en el motor
(el negro desaparece, el clima brilla sobre la escena). Si False la capa
se genera como pelicula TRANSLUCIDA semi-transparente (niebla densa,
tinte de tormenta, bruma) para blend multiply/overlay o alpha global.
keyword-only.
style: descriptor de estilo de la capa (ej. "weather overlay, atmospheric",
"stylized rain, painterly", "photoreal fog", "anime sun rays"). Pasa el
MISMO style + checkpoint + (lora) a todas las capas de un set para
coherencia visual. keyword-only.
checkpoint: checkpoint del servidor. 'dreamshaper_8.safetensors' (SD1.5,
holgado en 8GB lowvram) por defecto; 'juggernaut_xl_v11.safetensors'
para SDXL (mas VRAM, subir width/height a 1344x768). keyword-only.
width: ancho de la capa en px (multiplo de 8). 1024 por defecto (16:9 con
height 576: resolucion de pantalla apaisada). keyword-only.
height: alto de la capa en px (multiplo de 8). 576 por defecto. keyword-only.
seed: semilla del KSampler. Misma seed + mismos weather/style -> misma capa;
variar seed da variantes del mismo tipo de clima. keyword-only.
lora: LoRA de estilo opcional en models/loras. None = sin LoRA. keyword-only.
lora_strength: fuerza del LoRA sobre model y clip. Se clampa a [0.0, 2.0].
keyword-only.
negative: prompt negativo. None usa el negativo por defecto pensado para una
capa de cobertura total (sin sujeto/objeto/escena/profundidad/marco que
rompan la pelicula atmosferica) MAS, si on_black=True, el refuerzo de
fondo negro plano. keyword-only.
steps, cfg, sampler_name, scheduler, filename_prefix: parametros de
generacion. keyword-only.
Returns:
dict en API format listo para comfyui_submit_workflow: txt2img base apaisada
(16:9) con prompt scaffold de capa de clima ('{weather} overlay, {style},
full screen atmospheric layer, <particles/streaks on pure black background |
translucent layer>, seamless coverage, game VFX, ...') + LoRA de estilo
opcional. NO lleva Rembg: con on_black=True el PNG resultante se convierte a
RGBA con comfyui_matting_luma_to_alpha (luma=alpha) en un paso posterior para
blend aditivo. UNA capa; variar seed da variantes del mismo clima.
Raises:
ValueError: si weather esta vacio.
"""
from ml.comfyui_build_txt2img_workflow import comfyui_build_txt2img_workflow
if not weather or not weather.strip():
raise ValueError(
"comfyui_build_weather_overlay_workflow: 'weather' no puede estar vacio"
)
weather = weather.strip()
lora_strength = max(0.0, min(2.0, float(lora_strength)))
if negative is None:
if on_black:
neg = f"{_WEATHER_NEGATIVE_COMMON}, {_WEATHER_BG_NEG_ON_BLACK}"
else:
neg = _WEATHER_NEGATIVE_COMMON
else:
neg = negative
# Modo de composicion: sobre negro puro (particulas/estrias brillantes -> luma->alpha
# additive) o pelicula translucida semi-transparente. El segmento literal del scaffold
# ('particles/streaks on pure black background' | 'translucent layer') marca la
# intencion para el motor.
if on_black:
layer = (
"particles and streaks on a solid pure black background, "
"bright weather elements over flat pure black, glowing on black backdrop"
)
else:
layer = (
"translucent layer, semi-transparent atmospheric film, "
"soft alpha blend over the scene"
)
# Prompt scaffold de capa de clima full-screen: cobertura UNIFORME de borde a borde
# (no un sujeto centrado), apaisada a resolucion de pantalla, lista como overlay para
# blendear en el motor. Se diferencia de un decal (mancha localizada) y de un
# spritesheet (secuencia animada): aqui es UNA pelicula de cobertura total.
positive = (
f"{weather} overlay, {style}, full screen atmospheric layer, "
f"{layer}, seamless full screen coverage, edge to edge, uniform across frame, "
"game VFX, weather effect, high detail"
)
wf = comfyui_build_txt2img_workflow(
checkpoint,
positive,
neg,
steps=steps,
cfg=cfg,
width=width,
height=height,
seed=seed,
sampler_name=sampler_name,
scheduler=scheduler,
filename_prefix=filename_prefix,
)
if lora:
from ml.comfyui_inject_lora import comfyui_inject_lora
wf = comfyui_inject_lora(
wf, lora, strength_model=lora_strength, strength_clip=lora_strength
)
return wf
if __name__ == "__main__":
import json
wf = comfyui_build_weather_overlay_workflow(
"heavy rain",
on_black=True,
style="weather overlay, atmospheric",
seed=7,
)
print(
json.dumps(
{
"nodes": list(wf),
"classes": sorted({n["class_type"] for n in wf.values()}),
},
indent=2,
)
)
@@ -0,0 +1,145 @@
"""Tests offline de comfyui_build_weather_overlay_workflow (estructura del dict, sin GPU)."""
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from ml.comfyui_build_weather_overlay_workflow import ( # noqa: E402
comfyui_build_weather_overlay_workflow,
)
def _classes(wf):
return sorted({n["class_type"] for n in wf.values()})
def _by_class(wf, cls):
return [n for n in wf.values() if n["class_type"] == cls]
def _id_of(wf, cls):
return next(nid for nid, n in wf.items() if n["class_type"] == cls)
def _pos_with(wf, needle):
return next(
n for n in wf.values()
if n["class_type"] == "CLIPTextEncode" and needle in n["inputs"]["text"]
)
def _neg(wf):
return next(
n["inputs"]["text"] for n in wf.values()
if n["class_type"] == "CLIPTextEncode" and "solid object" in n["inputs"]["text"]
)
def test_golden_weather_on_black_recipe():
wf = comfyui_build_weather_overlay_workflow("heavy rain", on_black=True, seed=7)
cls = _classes(wf)
# Cadena base txt2img pura: NO lleva Rembg (el matting es luma-to-alpha aparte).
assert "CheckpointLoaderSimple" in cls
assert "KSampler" in cls
assert "VAEDecode" in cls
assert "SaveImage" in cls
assert "Image Rembg (Remove Background)" not in cls
# El clima + la cobertura full-screen + el fondo negro (apto luma->alpha) aparecen.
pos = _pos_with(wf, "heavy rain")
txt = pos["inputs"]["text"]
assert "full screen atmospheric layer" in txt
assert "solid pure black background" in txt
assert "seamless full screen coverage" in txt
assert "game VFX" in txt
# SaveImage toma del VAEDecode directamente (sin recorte intermedio).
vd_id = _id_of(wf, "VAEDecode")
save = next(n for n in wf.values() if n["class_type"] == "SaveImage")
assert save["inputs"]["images"] == [vd_id, 0]
def test_golden_landscape_dims_default():
# Apaisado 16:9 a resolucion de pantalla por defecto (NO cuadrado: es una capa
# full-screen, no un sujeto centrado).
wf = comfyui_build_weather_overlay_workflow("falling snow")
latent = _by_class(wf, "EmptyLatentImage")[0]["inputs"]
assert latent["width"] == 1024
assert latent["height"] == 576
assert latent["width"] > latent["height"] # apaisado
def test_edge_on_black_toggles_layer_mode():
# on_black=True -> particulas/estrias sobre negro plano (luma->alpha additive) +
# el negativo refuerza fondo negro (rechaza gris/blanco/cielo).
wf_black = comfyui_build_weather_overlay_workflow("god rays", on_black=True)
pos_black = _pos_with(wf_black, "god rays")["inputs"]["text"]
assert "solid pure black background" in pos_black
assert "translucent layer" not in pos_black
assert "white background" in _neg(wf_black) # refuerzo de fondo negro plano
# on_black=False -> pelicula translucida semi-transparente (sin refuerzo de fondo).
wf_film = comfyui_build_weather_overlay_workflow("god rays", on_black=False)
pos_film = _pos_with(wf_film, "god rays")["inputs"]["text"]
assert "translucent layer" in pos_film
assert "solid pure black background" not in pos_film
assert "white background" not in _neg(wf_film)
def test_edge_weather_reflected():
for w in ["thick fog", "dust haze", "storm vignette"]:
wf = comfyui_build_weather_overlay_workflow(w)
pos = _pos_with(wf, w)
assert w in pos["inputs"]["text"]
def test_edge_style_in_prompt():
wf = comfyui_build_weather_overlay_workflow(
"heavy rain", style="stylized rain, painterly"
)
pos = _pos_with(wf, "heavy rain")
assert "stylized rain, painterly" in pos["inputs"]["text"]
def test_edge_dims_reflected():
wf = comfyui_build_weather_overlay_workflow("blizzard", width=1344, height=768)
latent = _by_class(wf, "EmptyLatentImage")[0]["inputs"]
assert latent["width"] == 1344
assert latent["height"] == 768
def test_edge_negative_isolates_layer():
# El negativo por defecto rechaza sujeto/objeto/escena/horizonte/profundidad/marco
# que romperian la pelicula de cobertura total.
wf = comfyui_build_weather_overlay_workflow("sandstorm")
neg = _neg(wf)
assert "solid object" in neg
assert "landscape scene" in neg
assert "horizon line" in neg
assert "frame" in neg
assert "gray background" in neg # fondo negro plano forzado (on_black por defecto)
def test_edge_lora_reflected():
wf = comfyui_build_weather_overlay_workflow(
"falling snow", lora="vfx_sd15.safetensors", lora_strength=0.8
)
loras = _by_class(wf, "LoraLoader")
assert len(loras) == 1
assert loras[0]["inputs"]["lora_name"] == "vfx_sd15.safetensors"
assert loras[0]["inputs"]["strength_model"] == 0.8
def test_error_empty_weather():
try:
comfyui_build_weather_overlay_workflow(" ")
assert False
except ValueError as e:
assert "weather" in str(e)
def test_determinism():
a = comfyui_build_weather_overlay_workflow(
"heavy rain", on_black=True, lora="vfx_sd15.safetensors", seed=7
)
b = comfyui_build_weather_overlay_workflow(
"heavy rain", on_black=True, lora="vfx_sd15.safetensors", seed=7
)
assert a == b