Houdini 22.0 · POPs + Attribute Noise · Pyro · Karma XPU · MaterialX · Copernicus
Portal de Dr. Strange en Houdini
Paso a paso para construir el portal del sling ring: un anillo de chispas que nace pequeño, crece, se deforma con un mapa de ruido y lanza chispas naranjas en forma de rayas que rebotan en el piso. Lleva un humo leve en el aro, se renderiza con Karma y el glow se agrega en Copernicus.
Frames 1–240 (10 s a 24 fps), Karma XPU a 1024 × 1024 con humo, compuesto en Copernicus (glow, curva fílmica y saturación). Aquí reducido a 720 × 720. La marca de agua es de la licencia Apprentice.
- Radio
- 2 m
- Chispas
- ~4 300
- Render
- ~10–18 s/frame
- Armarlo
- 60–90 min
La idea en siete líneas
- Un círculo crece y se abre. Un mapa de ruido 3D deforma su borde para que no sea un círculo perfecto.
- Cada punto del círculo recibe una velocidad tangencial, que barre el borde y sale hacia afuera.
- Un POP Network suelta chispas, las hace girar, las frena y les aplica gravedad. Una fuerza Metaball limpia el hueco y un Ground Plane las hace rebotar.
- Después de simular, cada chispa se vuelve una línea orientada por su velocidad. Con puntos parecería un fuego artificial.
- Look: chispas y anillo emisivos (MaterialX), una luz naranja para el piso y render en Karma XPU.
- Humo (opcional): un Pyro Solver suelta humo leve desde el aro, que brilla naranja por sí mismo. Se prende y apaga con un interruptor.
- Composición: en Copernicus se agregan dos glows, una curva fílmica y saturación, y se guarda la secuencia final.
Antes de empezar
- Versión: Houdini 22.0 Apprentice. Algunos nodos se llaman
::2.0(POP Source, POP Solver, Attribute Noise); el menú Tab elige la versión nueva solo. - Unidades: 1 unidad = 1 metro. El portal mide 2 m de radio y está de pie en el plano XY, mirando hacia +Z, que es donde está la cámara.
- Animación: 24 fps, frames 1 a 240 (10 s). Se configura en Global Animation Options, el reloj abajo a la derecha.
- Cómo crear nodos: dentro de una red, pulsa Tab y escribe el nombre del nodo. Para entrar a un nodo, doble clic (o I); para salir, U.
- Renombra cada nodo con el nombre de la guía. Varias expresiones apuntan a los nodos por nombre (por ejemplo
ch("../anillo_emisor/radx")). - Flags: el display flag (azul) dice qué se ve en el viewport y el render flag (morado) qué se renderiza. En
/obj, el display flag de un objeto también decide si Karma lo ve.
chf("nombre") para que los valores sean sliders. Después de pegar el código en un Wrangle, pulsa el botón Create spare parameters. No tiene texto: es un ícono (un cuadrito con un deslizador y un +) arriba a la derecha del cuadro de código, a la altura de la palabra VEXpression; al pasar el ratón dice Creates spare parameters for each unique call of ch(). Si no se ve, ensancha el panel de parámetros. Aparecen los sliders en 0; luego escribe los valores de la tabla.-$F * 4). Escríbela tal cual en el campo; el campo se pone verde. $F es el frame actual y $T el tiempo en segundos.ch("../anillo_emisor/radx") se lee así:ch("…")(ochf("…")dentro de un Wrangle) = «dame el valor de este parámetro».radx= el nombre interno del parámetro Radius (campo X) del Circle. Cada parámetro tiene una etiqueta visible (Radius) y un nombre interno (radx,rady). Para verlo, pasa el ratón sobre la etiqueta del parámetro: el recuadro que aparece diceParameter: radx.anillo_emisor= el nombre del nodo donde está ese parámetro (por eso hay que renombrar los nodos igual que en la guía)...= «sube un nivel», como en las carpetas de una computadora. Desde un nodo dentro de/obj/portal,..es la propia red/obj/portal, así que../anillo_emisores el nodoanillo_emisorque está a su lado. Desde dentro dechispas_simhay que subir dos niveles (../../): uno para salir del nodo y otro para salir del DOP Network.- Es lo mismo que escribir la ruta completa:
ch("/obj/portal/anillo_emisor/radx"). La relativa se usa porque sigue funcionando si renombras o copias el objetoportal. - Truco para no escribirlas: clic derecho sobre el parámetro de origen (por ejemplo Radius de
anillo_emisor) → Copy Parameter; luego clic derecho sobre el parámetro de destino → Paste Relative References. Houdini escribe la expresión con la ruta correcta. - Siglas de los niveles de Houdini: SOP = redes de geometría (dentro de un objeto como
portal); DOP = redes de simulación (chispas_sim); POP = los nodos de partículas que van dentro de un DOP; ROP = nodos de render en/out; COP = composición en/img(Copernicus). Los nodos Null llamadosOUT_…oRENDER_…no hacen nada: marcan la salida de una etapa para que otros nodos la lean por su ruta. - En los códigos VEX:
@Pes la posición del punto y@ptnumsu número. La letra antes de@dice el tipo del atributo:f@= número decimal,v@= vector (x, y, z),i@= número entero.@Framey@Timeson el frame y el tiempo actuales. - Variables en rutas de archivo:
$HIP= la carpeta donde está guardado tu.hipnc;$F3y$F4= el número de frame con 3 o 4 dígitos (portal_073.jpg,portal.0073.exr).
Mapa de lo que vas a construir
Dentro de /obj/portal (nivel SOP):
anillo_emisor (Circle)
→ normal_radial (Wrangle) → mapa_de_ruido (Attribute Noise) → desplazar_por_ruido (Wrangle) → OUT_anillo (Null)
→ velocidad_tangencial (Wrangle) → OUT_emisor (Null) ┄┄ lo lee fuente_anillo
hueco_metaball (Metaball) ┄┄ lo lee la fuerza "hueco"
chispas_sim (DOP Network)
┄ importar_chispas (DOP Import) → color_por_edad (Wrangle) → estelas_lineas (Wrangle)
├→ OUT_chispas (Null, display flag: preview)
└→ look_render (Wrangle) → RENDER_chispas (Null, render flag: Karma)Dentro de chispas_sim (nivel DOP):
chispas (POP Object) ───────────────────────────────────────────────→ popsolver [entrada 1: Object] fuente_anillo → vida_aleatoria → giro_portal → hueco → frenado → gravedad_y_ruido → popsolver [entrada 3] piso (Ground Plane) ─┐ (izquierda) popsolver ───────────┴→ colisiones (Merge) → output
En /obj: portal, anillo_brillante (copia el anillo deformado para la línea brillante), piso, luz_portal y cam_preview. Si llevas humo, también CONTROLES y humo. En /mat, tres materiales (cuatro con el humo); en /out, un ROP de OpenGL y uno de Karma.
Dentro de /obj/humo (paso 12, opcional):
traer_anillo (Object Merge) → fuente_humo (Wrangle) → rasterizar_fuente (Volume Rasterize Attributes) → sim_humo (Pyro Solver) → densidad_y_temperatura (Blast) → cache_humo (File Cache) → material_humo (Material) → OUT_humo (Null)
Dentro de /img/comp_portal (paso 13, Copernicus):
render_karma (File) → glow_cerca (Glow) → glow_lejos (Glow) → curva_filmica (Tonemap) → saturacion (HSV) → OUT_comp (Output) guardar_comp (ROP Image) ┄┄ lee saturacion
Las flechas punteadas (┄) no son cables: el nodo lee la geometría por su SOP Path.
/obj/portal, ordenada en cajas por etapa: 1. anillo, 2. mapa de ruido, 3. velocidad, 4. simulación, 5. color y líneas, 6. look para Karma. irregularidad (gris, arriba a la izquierda) es una prueba descartada que no se conecta; no hace falta crearla.chispas_sim. La fuente y las fuerzas van en cadena a la 3ª entrada del popsolver. El piso entra a la izquierda del merge colisiones./obj terminado. MCP_CAM_CENTER y MCP_CAMERA los creó la herramienta de previews de Claude; no hacen falta.El objeto contenedor
- En
/obj, Tab → Geometry. Llámaloportal. - Entra (doble clic). Los pasos 2 a 7 van adentro.
El anillo emisor
El círculo del que nacen las chispas. Crece de 0.24 m a 2 m en 2.5 s, arrancando rápido y frenando al final (ease-out). También se abre como un arco y gira.
| Nodo | Nombre | Parámetros |
|---|---|---|
| Circle | anillo_emisor | Primitive Type Polygon, Orientation XY Plane, Divisions 360, Arc Type Open Arc |
Expresiones (escríbelas en el campo):
| Parámetro | Expresión | Qué hace |
|---|---|---|
| Radius (X) | 2 * (0.12 + 0.88 * (1 - pow(1 - clamp(($F - 1) / 60, 0, 1), 3))) | Crece hasta 2 m en el frame 61 (curva cúbica ease-out) |
| Radius (Y) | ch("radx") | Copia el valor de Radius X de este mismo nodo (radx es el nombre interno del campo X); así el círculo nunca se vuelve óvalo |
| Arc Angles (2º campo) | fit($F, 1, 36, 1, 360) | El arco se abre de 0° a 360° en 1.5 s |
| Rotate (Z) | -$F * 4 | El anillo gira 4° por frame |
anillo_emisor en el frame 73. Los campos verdes tienen expresión; se ve su valor en ese frame (radio 2, arco 360°). Reverse se marca solo.El mapa de ruido (que no sea un círculo perfecto)
El anillo atraviesa un campo de ruido 3D animado que empuja cada punto hacia afuera o hacia adentro. Así el portal parece que "lucha" por mantenerse circular.
- Debajo de
anillo_emisor, Tab → Attribute Wrangle, nombrenormal_radial(Run Over: Points):
// Normal hacia afuera del portal: el ruido empuja el borde hacia afuera o hacia adentro
v@N = normalize(set(@P.x, @P.y, 0));- Debajo, Tab → Attribute Noise, nombre
mapa_de_ruido:
| Sección | Parámetro | Valor |
|---|---|---|
| General | Attribute Names | Float, ruido |
| Noise Value | Range Values | Zero Centered |
| Amplitude | 0.38 (m de deformación) | |
| Noise Pattern | Noise Type | Simplex |
| Element Size | 6.68 (tamaño de las manchas) | |
| Offset (pulsa xyz para verlo por eje) | X $T * 0.7, Y $T * -0.5, Z 0: el mapa viaja | |
| Animation | Animated | ✔, Pulse Duration 1.5 (qué tan rápido cambia) |
| Fractal | Type | Standard (fBm), Max Octaves 3, Roughness 0.45 |
mapa_de_ruido con Animation y Fractal abiertos.- Debajo, Tab → Attribute Wrangle, nombre
desplazar_por_ruido:
// Empuja el borde por la normal radial segun el valor del mapa de ruido (en metros).
// Se escala con el radio: un portal pequeno se deforma proporcionalmente igual.
// chf("../anillo_emisor/radx") = el Radius X del nodo anillo_emisor (paso 2).
// ".." sube de este wrangle a /obj/portal, donde esta anillo_emisor.
float escala = chf("../anillo_emisor/radx") / 2.0;
@P += v@N * f@ruido * escala;- Debajo, Tab → Null, nombre
OUT_anillo. Es la salida del anillo deformado; la usan las chispas y la línea brillante.
La velocidad inicial de las chispas
Cada punto del anillo guarda una velocidad v. La POP Source la hereda y así las chispas nacen ya en movimiento: barren el borde (dirección tangente) y salen hacia afuera.
Debajo de OUT_anillo, Tab → Attribute Wrangle, nombre velocidad_tangencial:
// Velocidad inicial de cada chispa: barre el borde del portal y sale hacia afuera
vector eje = {0, 0, 1}; // normal del portal
vector r = normalize(set(@P.x, @P.y, 0));
float giro = chf("vel_giro"); // a lo largo del borde
float salida = chf("vel_salida"); // hacia afuera
// Aleatoriedad por chispa: la mayoria normales, pocas muy rapidas (cola larga).
// Asi el borde exterior no termina en un anillo parejo.
float sem = @ptnum * 13.7 + @Frame * 7.1;
float k = fit01(pow(rand(sem), chf("sesgo")), chf("vel_min"), chf("vel_max"));
// Rafagas: el ruido a lo largo del anillo hace zonas con mas y menos fuerza
float rafaga = fit(noise(set(@ptnum * 0.05, @Frame * 0.15, 0)), 0.3, 0.7, 0.6, 1.4);
v@v = (cross(eje, r) * giro + r * salida) * k * rafaga;
v@v += (rand(sem + 1) - 0.5) * chf("variacion");
// Portal pequeno = chispas lentas: la velocidad crece con el radio (1 a 2 m)
// Lee el Radius X del nodo anillo_emisor (paso 2): 0.24 al inicio, 2 al final
float escala_radio = chf("../anillo_emisor/radx") / 2.0;
v@v *= escala_radio;Pulsa Create spare parameters (el ícono del recuadro de arriba) y pon los valores. escala_radio no genera slider: no es un valor nuevo, lee el radio de anillo_emisor.
| Slider | Valor | Qué controla |
|---|---|---|
| vel_giro | 9 | Velocidad a lo largo del borde (m/s) |
| vel_salida | 3.5 | Velocidad hacia afuera (m/s) |
| variacion | 2 | Ruido aleatorio en la dirección |
| sesgo | 3 | Más alto = menos chispas rápidas (la "cola larga") |
| vel_min / vel_max | 0.6 / 2.2 | Rango del multiplicador por chispa |
Debajo, Tab → Null, nombre OUT_emisor.
El hueco del centro
Una metaball del tamaño del hueco. No se ve; solo la usa la fuerza hueco (paso 6) para empujar hacia afuera las chispas que se acercan al centro. Es el truco que usó RISE FX en la película.
| Nodo | Nombre | Parámetros |
|---|---|---|
| Metaball | hueco_metaball | Radius X, Y y Z: ch("../anillo_emisor/radx") * 0.9, es decir, el 90% del radio del anillo (lee el Radius X de anillo_emisor, que está en la misma red). Así el hueco crece con el portal. No se conecta a nada |
La simulación de chispas
Aquí nacen y se mueven las chispas. Todo pasa dentro de un DOP Network (una red de simulación): una fuente crea partículas sobre el anillo, varias fuerzas las empujan y un solver calcula su movimiento frame a frame.
Vas a crear 10 nodos. Esta es la lista; abajo va cada uno explicado, con sus valores pestaña por pestaña y su captura.
| # | Nodo (lo que escribes después de Tab) | Nombre | Para qué sirve |
|---|---|---|---|
| 6.2 | POP Object | chispas | El «contenedor» de las partículas: cuánto rebotan y cuánto se frenan al chocar |
| 6.3 | POP Source | fuente_anillo | Crea las chispas sobre los puntos del anillo |
| 6.4 | POP Wrangle | vida_aleatoria | Le da a cada chispa una vida y un brillo distintos |
| 6.5 | POP Axis Force | giro_portal | Las hace girar alrededor del centro del portal |
| 6.6 | POP Metaball Force | hueco | Las saca del centro para que el hueco quede limpio |
| 6.7 | POP Drag | frenado | Las frena un poco, como el aire |
| 6.8 | POP Force | gravedad_y_ruido | Gravedad suave y un temblor aleatorio |
| 6.9 | POP Solver | popsolver | El que calcula el movimiento |
| 6.10 | Ground Plane | piso | Un piso invisible donde rebotan |
| 6.11 | Merge | colisiones | Junta el piso con las chispas para que choquen |
portal, elige el nodo (por ejemplo OUT_emisor) y acepta. Houdini escribe la ruta solo. Si la escribes a mano, recuerda que ../../ significa «sube dos niveles»: uno para salir del nodo en el que estás y otro para salir de chispas_sim, hasta /obj/portal (ver el recuadro Cómo leer las rutas al inicio).6.1 Crea el DOP Network
- En
/obj/portal, Tab → DOP Network, nombrechispas_sim. - Entra en él (doble clic). Puede traer un nodo
outputadentro: déjalo, es la salida de la simulación.
6.2 chispas (POP Object)
Tab → POP Object, nombre chispas. Es el objeto que guarda todas las partículas.
- Pestaña Physical: Bounce 0.35 (cuánto rebotan), Friction 0.5 y Dynamic Friction Scale 0.5 (cuánto se frenan al arrastrarse por el piso). El resto, por defecto.
chispas, pestaña Physical. El rebote se ajusta aquí y en el piso (6.10), porque los dos valores se combinan.6.3 fuente_anillo (POP Source)
Tab → POP Source, nombre fuente_anillo. Crea chispas nuevas en cada frame sobre los puntos del anillo. Tiene tres pestañas que importan:
Pestaña Source (de dónde salen):
- Emission Type: Points (una chispa por punto del anillo, no repartidas sobre una superficie).
- SOP:
../../OUT_emisor. Es el NullOUT_emisordel paso 4, la salida del anillo con la velocidad ya calculada. Usa el botón del selector de nodos para elegirlo o escribe la ruta:../../sube defuente_anilloa/obj/portal.
OUT_emisor.Pestaña Birth (cuántas y cuánto viven):
- Const. Activation: 1 (la fuente está encendida).
- Const. Birth Rate: 10 000 (chispas nuevas por segundo).
- Life Expectancy: 0.5 (viven medio segundo)…
- …Life Variance: 0.25 (…más o menos un cuarto de segundo, para que no mueran todas a la vez).
Pestaña Attributes (con qué velocidad nacen):
- Initial Velocity: Use inherited velocity (usar la velocidad
vque guardó el anillo en el paso 4). - Inherit Velocity: 1 (toda esa velocidad, sin reducirla).
6.4 vida_aleatoria (POP Wrangle)
Tab → POP Wrangle, nombre vida_aleatoria, y pega este código en el cuadro VEXpression. Le da a cada chispa, una sola vez al nacer, una vida y un brillo propios: la mayoría vive poco y unas pocas mucho, así no forman un anillo parejo.
// Una sola vez por chispa: vida con cola larga para que no mueran todas a la misma distancia
if (i@vida_ok == 0) {
float u = rand(i@id * 3.17 + 0.5);
f@life *= fit01(pow(u, 2.5), 0.6, 1.5);
f@brillo = fit01(rand(i@id * 1.91), 0.55, 1.25);
i@vida_ok = 1;
}Este código no usa sliders: no hace falta Create spare parameters.
6.5 giro_portal (POP Axis Force)
Tab → POP Axis Force, nombre giro_portal. Crea una fuerza que hace girar las partículas alrededor de un eje, como un remolino.
Pestaña Shape (dónde actúa la fuerza):
- Shape: Sphere (actúa dentro de una esfera).
- Center: 0, 0, 0 (el centro del portal).
- Axis: 0, 0, 1 (el eje Z: el portal está de pie mirando a la cámara, así que gira alrededor de Z).
- Radius: escribe la expresión
ch("../../anillo_emisor/radx") * 1.3. Quiere decir: el radio del anillo × 1.3, para que la fuerza siempre cubra un poco más que el anillo mientras crece. (../../sube degiro_portala/obj/portal;radxes el Radius X del Circleanillo_emisor.)
Pestaña Speed (qué tan fuerte gira):
- Orbit Speed: escribe la expresión
1.5 * ch("../../anillo_emisor/radx") / 2. Vale 1.5 cuando el anillo mide 2 m y menos cuando es chico, así el portal pequeño gira más lento. - Lift Speed: 0 (no las empuja a lo largo del eje).
- Suction Speed: 0 (no las jala hacia el centro).
giro_portal, pestaña Speed en el frame 73: el campo Orbit Speed muestra 1.5 porque ahí el anillo ya mide 2 m. Los campos con expresión se ponen en color.6.6 hueco (POP Metaball Force)
Tab → POP Metaball Force, nombre hueco. Empuja hacia afuera las chispas que entran a la metaball del paso 5, para que el centro del portal quede limpio.
- Geometry Source: SOP (usar una geometría de afuera del DOP).
- SOP Path:
../../hueco_metaball(la metaball del paso 5; elígela con el botón del selector de nodos). - Force Scale: 30 (qué tan fuerte empuja).
6.7 frenado (POP Drag)
Tab → POP Drag, nombre frenado. Simula la resistencia del aire.
- Air Resistance: 0.3. Bajo a propósito: con mucho frenado las chispas se detienen y, en el paso 7, sus líneas se encogen a puntos.
6.8 gravedad_y_ruido (POP Force)
Tab → POP Force, nombre gravedad_y_ruido. Una sola fuerza hace dos cosas:
- Force: 0, -2, 0. Una gravedad suave hacia abajo (la real sería -9.8; aquí se quiere que caigan poco).
- Pestaña Noise: Amplitude 1.5 (qué tanto tiemblan) y Swirl Size 0.5 (qué tan chicos son los remolinos del temblor).
6.9 popsolver (POP Solver)
Tab → POP Solver, nombre popsolver. No se cambia nada. Es el nodo que calcula cómo se mueven las partículas con todas las fuerzas. Tiene tres entradas (de izquierda a derecha): 1 Object, 2 Pre-Solve y 3 Sources.
6.10 piso (Ground Plane)
Tab → Ground Plane, nombre piso. Es un piso infinito e invisible para que las chispas reboten; el piso que se ve en el render es otro objeto (paso 9).
- Center Y: -2.6 (a esa altura está el suelo).
- Pestaña Physical: Bounce 0.35, Friction 0.5, Dynamic Friction Scale 0.5 (los mismos valores que
chispas).
6.11 colisiones (Merge)
Tab → Merge, nombre colisiones. Junta el piso y las chispas. Se deja por defecto: su relación es Collide, así que lo que entra a la izquierda (el piso) choca con lo de la derecha (las chispas).
6.12 Conecta todo
Arrastra desde la salida de un nodo (abajo) hasta la entrada de otro (arriba). El orden de las entradas importa:
- La cadena de la fuente:
fuente_anillo→vida_aleatoria→giro_portal→hueco→frenado→gravedad_y_ruido, y de ahí a la 3ª entrada (la de la derecha, Sources) delpopsolver. - El objeto:
chispas→ 1ª entrada (la de la izquierda, Object) delpopsolver. - El piso:
piso→ 1ª entrada (izquierda) decolisiones;popsolver→ 2ª entrada decolisiones. Si los inviertes, las chispas atraviesan el piso. - La salida:
colisiones→output. Pon el display flag (azul) enoutput.
chispas_sim. La cadena de la fuente llega a la 3ª entrada del popsolver y el piso a la izquierda de colisiones.fuente_anillo (6.3) y que la cadena llegue a la 3ª entrada del popsolver. Si cambias un valor y no ves el cambio, pulsa Reset Simulation (o regresa al frame 1).Sacar las chispas a SOPs, color y líneas
Sal del DOP Network (U) y, en /obj/portal:
- Tab → DOP Import, nombre
importar_chispas: DOP Network../chispas_sim(el DOP Network del paso 6; aquí solo se sube un nivel porqueimportar_chispasestá en/obj/portal, junto a él), Objectschispas(el POP Object de adentro), Import Style Fetch Geometry from DOP Network.
- Debajo, Attribute Wrangle
color_por_edad. El color depende de la edad de la chispa:
// Color y tamano de cada chispa segun su edad (0 = nace, 1 = muere)
float t = clamp(f@age / max(f@life, 1e-4), 0, 1);
vector amarillo = {1.0, 0.75, 0.3};
vector naranja = {1.0, 0.3, 0.03};
vector rojo = {0.6, 0.05, 0.0};
v@Cd = t < 0.12 ? lerp(amarillo, naranja, t / 0.12)
: t < 0.55 ? naranja
: lerp(naranja, rojo, (t - 0.55) / 0.45);
v@Cd *= f@brillo; // cada chispa con su propio brillo
f@pscale = 0.012 * (1 - t);
f@Alpha = 1 - t * t;- Debajo, Attribute Wrangle
estelas_lineas. La clave del look: cada partícula se vuelve una línea corta hacia atrás, según su velocidad.
// Convierte cada chispa en una linea: de su posicion hacia atras, segun su velocidad.
// Asi se ve como una chispa (raya por la velocidad) y no como un punto de fuego artificial.
float largo = chf("largo_seg"); // segundos de recorrido que muestra la estela
float vmin = chf("largo_min"); // largo minimo en metros
vector dir = v@v * largo;
if (length(dir) < vmin) dir = normalize(v@v + {0,1e-5,0}) * vmin;
int cola = addpoint(0, @P - dir);
setpointattrib(0, "Cd", cola, v@Cd * chf("brillo_cola"));
setpointattrib(0, "Alpha", cola, 0.0);
setpointattrib(0, "pscale", cola, f@pscale * 0.3);
addprim(0, "polyline", @ptnum, cola);Sliders: largo_seg 0.07, largo_min 0.12, brillo_cola 0.15.
- Debajo, Null
OUT_chispascon el display flag.
importar_chispas hay ~4300 puntos. Si no pasa nada, revisa las conexiones del solver y el SOP Path de fuente_anillo.Revisar el movimiento (OpenGL)
- En
/obj, Tab → Camera, nombrecam_preview: Translate (0, 0, 16), Resolution 1280 × 720 (focal 50, por defecto). El formato final cuadrado se pone en los ROPs.
- En
/out, Tab → OpenGL, nombrepreview_opengl: Camera/obj/cam_preview, Valid Frame Range Render Frame Range de 1 a 240, Override Camera Resolution ✔ 1024 × 1024, Output Picture$HIP/render/preview_opengl_1024/portal_$F3.jpg. Render.
- Arma el video y revísalo (opcional, con ffmpeg):
ffmpeg -framerate 24 -i portal_%03d.jpg -c:v libx264 -crf 18 -pix_fmt yuv420p preview.mp4Tarda ~10 s para los 240 frames. Ajusta la simulación hasta que el movimiento te guste y solo entonces sigue con el look.
chispas_sim y pulsa Reset Simulation (o cambia de frame al 1). Si no, se ve la caché vieja.Look (anillo brillante, piso y luz)
- Chispas para render. En
/obj/portal, debajo deestelas_lineas(en paralelo aOUT_chispas), Attribute Wranglelook_render:
// Look para Karma: color HDR (emision) y grosor de las curvas
float a = f@Alpha;
v@Cd *= chf("intensidad") * a; // la cola (Alpha 0) queda negra = se desvanece
f@width = max(f@pscale, 0.0005) * chf("grosor");Sliders: intensidad 3.5, grosor 0.6. Debajo, Null RENDER_chispas con el render flag (morado). El display flag se queda en OUT_chispas.
- Anillo brillante. En
/obj, Tab → Geometryanillo_brillante. Adentro:- Object Merge
traer_anillo: Object 1/obj/portal/OUT_anillo. - Attribute Wrangle
look_anillo, con Run Over Detail (only once). Rehace el borde como 6 hebras con ruido suave, para que se vea deshilachado y no como una línea limpia:
- Object Merge
// Anillo deshilachado: varias hebras alrededor del borde, cada una con ruido propio.
// El ruido se calcula sobre el ANGULO (cos, sin) y con baja frecuencia:
// las hebras ondulan suave, sin zigzag, y el circulo cierra sin corte.
int hebras = chi("hebras");
int npts = npoints(0);
for (int h = 0; h < hebras; h++) {
int prev = -1;
for (int i = 0; i < npts; i++) {
vector p = point(0, "P", i);
vector r = normalize(set(p.x, p.y, 0));
vector d = r * chf("ondas") + set(h * 3.1, h * 7.7, @Time * chf("velocidad"));
float n = noise(d); // ondulacion de la hebra (suave)
float n2 = noise(d * 4 + set(0, 0, @Time * 6)); // titileo del brillo (rapido, no mueve la forma)
vector q = p + r * (n - 0.5) * chf("deshilachado") + {0,0,1} * (n2 - 0.5) * 0.03;
int pt = addpoint(0, q);
float b = h == 0 ? 1.0 : fit01(rand(h * 4.3), 0.25, 0.7); // hebra 0 = nucleo
vector c = lerp({1.0, 0.42, 0.08}, {1.0, 0.72, 0.32}, b);
setpointattrib(0, "Cd", pt, c * fit(n2, 0.3, 0.7, 1.5, 4) * b);
setpointattrib(0, "width", pt, (h == 0 ? 0.014 : 0.006) * fit(n2, 0.3, 0.7, 0.7, 1.3));
if (prev >= 0) addprim(0, "polyline", prev, pt);
prev = pt;
}
}
// quita el circulo original y sus puntos (solo quedan las hebras)
removeprim(0, 0, 0);
for (int i = 0; i < npts; i++) removepoint(0, i);Sliders: hebras 6, deshilachado 0.12, ondas 3, velocidad 1.5.
- Null
RENDER_anillocon display y render flag.
- Piso visible. En
/obj, Tab → Geometrypiso; adentro, un Grid de 300 × 300 con Center Y -2.6. Déjale el display flag encendido: si lo apagas, Karma no lo renderiza. - Luz. En
/obj, Tab → Point Light, nombreluz_portal: Translate (0, -1.7, 0.3), Color (1, 0.4, 0.1), Exposure 4.5, Intensity(1 - pow(1 - clamp(($F - 1) / 60, 0, 1), 3))(crece con el portal). Las chispas son tan delgadas que casi no iluminan el piso; esta luz lo simula.
Materiales (MaterialX)
- En
/mat, Tab → Karma Material Builder, tres veces:chispas_emisivo,anillo_emisivoypiso_oscuro. - En
chispas_emisivoyanillo_emisivo, entra y:- En
mtlxstandard_surface: Base 0, Specular 0, Emission 1. - Tab → MtlX Geometry Property Value, nombre
color_Cd: Signature Vector 3, GeompropdisplayColor. Conecta su salida a emission_color delmtlxstandard_surface.
- En
- En
piso_oscuro, enmtlxstandard_surface: Base Color (0.12, 0.1, 0.09), Specular 0.5, Specular Roughness 0.55. - Asigna cada material en la pestaña Render → Material del objeto:
portal→/mat/chispas_emisivo,anillo_brillante→/mat/anillo_emisivo,piso→/mat/piso_oscuro.
chispas_emisivo: color_Cd → emission_color del Standard Surface.displayColor, no CdAl pasar a USD (Karma), el color Cd de SOPs se llama displayColor. Si pones Cd en Geomprop, las chispas salen negras.Render con Karma
- En
/out, Tab → Karma, nombrekarma_look: Camera/obj/cam_preview, Resolution 1024 × 1024, Rendering Engine XPU (CPU si no tienes NVIDIA RTX), Path Traced Samples 256, Output Picture$HIP/render/karma/portal.$F4.exr. - Pestaña Rendering → Camera Effects: desactiva Motion Blur. La estela ya está hecha en la geometría (paso 7); con blur quedaría doble.
- Pestaña Objects: Exclude Objects
MCP_*(solo si tienes esas cámaras de Claude). - Prueba un solo frame (Start/End 73 73) y revisa el look. Luego pon 1 a 240 y Render to Disk.
karma_look configurado para el frame de prueba (73). Para el render completo, Start/End 1 y 240.Para renderizar sin abrir Houdini (en la Venator, con el script render_karma.py):
hython ../render_karma.py "experimento de houdini 5 (portal de dr strange).hipnc" --rop /out/karma_look --frames 1 240 --out render/karma_v1Para ver un EXR como PNG: hoiiotool frame.exr --ch R,G,B --colorconvert linear sRGB -o frame.png.
estelas_lineas, así el render solo lee geometría. Con el humo del paso 12 sube a ~18 s por frame en promedio (de ~9 s al inicio a ~22 s al final): ~75 min.El glow de la referencia no se hace en el render: se agrega en composición (paso 13).
Humo leve en el aro (opcional)
Un humo 3D suave que sale del aro y brilla naranja. Es opcional: las chispas lo tapan bastante, y se nota sobre todo después de la composición, como un halo rojizo y una bruma. Por eso lleva un interruptor para prenderlo y apagarlo.
- Interruptor. En
/obj, Tab → Null, nombreCONTROLES. Engrane → Edit Parameter Interface → arrastra un Toggle: Namehumo, LabelHumo en el aro, Default encendido → Accept. - Objeto. En
/obj, Tab → Geometry, nombrehumo. En su parámetro Display (pestaña Transform, abajo) escribe la expresiónch("../CONTROLES/humo"): lee el interruptorhumodel NullCONTROLES(paso 12.1)...sube del objetohumoa/obj, donde estáCONTROLES. Vale 1 si está encendido y 0 si no. Como en/objel display también es la visibilidad en Karma, apagado no aparece ni se renderiza, y no cuesta tiempo. - Entra a
humoy crea:
| Nodo | Nombre | Parámetros |
|---|---|---|
| Object Merge | traer_anillo | Object 1 /obj/portal/OUT_anillo (el aro deformado, que crece con el portal) |
| Attribute Wrangle | fuente_humo | El código de abajo |
| Volume Rasterize Attributes | rasterizar_fuente | Attributes density temperature v, Voxel Size 0.04 |
| Pyro Solver | sim_humo | Voxel Size 0.04. Shape: Dissipation 0.35, Buoyancy Scale 0.4, Disturbance 0.4, Turbulence 0.3 (Swirl Size 0.5). Sparse activado (por defecto) |
| Blast | densidad_y_temperatura | Group Type Primitives, Group @name=flame @name=vel.*. Quita lo que no se renderiza para que la caché pese menos. No borres temperature |
| File Cache | cache_humo | Base Folder $HIP/cache, Base Name humo_portal, frames 1–240. Save to Disk y después activa Load from Disk |
| Material | material_humo | Material /mat/humo_pyro |
| Null | OUT_humo | Display y render flag |
// Puntos fuente del humo sobre el aro: densidad leve con ruido y un empuje hacia afuera
vector r = normalize(set(@P.x, @P.y, 0));
float n = noise(set(@P.x * 2, @P.y * 2, @Time * 0.8));
f@density = chf("densidad") * fit(n, 0.35, 0.65, 0, 1); // manchas: no sale parejo en todo el aro
f@temperature = chf("temperatura");
v@v = r * chf("salida") + cross({0,0,1}, r) * chf("giro");
f@pscale = chf("tamano");Sliders: densidad 0.6, temperatura 0.4, salida 0.6, giro 1, tamano 0.08.
- Material. En
/mat, Tab → Karma Material Builder, nombrehumo_pyro. Adentro, borra el Standard Surface, crea un Karma Pyro Shader (shader_humo) y conecta su salida a volume deMaterial_Outputs_and_AOVs:- Smoke Color (1, 0.6, 0.35), Smoke Brightness 3, Density 1.
- Enable Fire ✔: Intensity Scale 4, Source Range de intensidad y de color 0 a 0.4, Enable Mask ✔.
- Vuelve a renderizar con el paso 11, con
karma_looksin cambios. El humo entra solo, porque el objetohumoestá visible.
- El material en el objeto. Si pones el material en Render → Material del objeto
humo, Karma lo ignora en volúmenes. Tiene que ir en el Material SOP de adentro. - Una luz solo para el humo. El Light Mask del objeto no se respeta con el Karma de
/out, y la luz aclara también el piso. Usa la emisión del fuego. - Fuego sin efecto. El fuego lee
temperatureen un rango de 0 a 1, pero aquí la temperatura llega a 0.4. Pon el Source Range en 0–0.4.
Composición en Copernicus (glow)
El glow, el tono y el color se agregan después del render, dentro de Houdini, con Copernicus (los COPs nuevos). Así se ajusta en segundos sin volver a renderizar.
- En
/img, Tab → Copernicus Network, nombrecomp_portal. Entra y crea:
| Nodo | Nombre | Parámetros |
|---|---|---|
| File | render_karma | File Name $HIP/render/karma/portal.$F4.exr (la salida del paso 11). En la lista de AOVs: File AOV C, Channel Type RGB (no RGBA). Border Constant |
| Glow | glow_cerca | Glow Threshold 0.7, Brightness 0.7, Size 0.01, Tint (1, 0.7, 0.4): halo apretado en el anillo y las chispas |
| Glow | glow_lejos | Glow Threshold 0.35, Brightness 1, Size 0.1, Tint (1, 0.35, 0.08): resplandor naranja grande, también hacia el hueco |
| Tonemap | curva_filmica | Operator Hable Filmic, Exposure 1.3: suaviza las luces altas para que el piso no se queme |
| HSV | saturacion | Hue Shift −9, Saturation Scale 1.35: el tonemap empuja el naranja hacia dorado, y esto lo regresa |
| Output | OUT_comp | Conectado a saturacion |
- Guardar la secuencia. En la misma red, Tab → ROP Image, nombre
guardar_comp:- COP Path
/img/comp_portal/saturacion(el nodo con la imagen, no elOUT_comp). - AOV Name
hsv_adjust: el nombre de la salida de ese nodo (pasa el ratón sobre su salida para verlo). - Output File
$HIP/render/comp/portal_comp.$F4.png. - Valid Frame Range Render Frame Range, 1 a 240. Render.
- COP Path
- Video con ffmpeg:
ffmpeg -framerate 24 -i render/comp/portal_comp.%04d.png -c:v libx264 -crf 18 -pix_fmt yuv420p portal_final.mp4Para componer sin abrir Houdini (en la Venator, con el script comp_copernicus.py, después de render_karma.py):
hython ../comp_copernicus.py "experimento de houdini 5 (portal de dr strange).hipnc" --entrada render/karma_final_humo --salida render/comp_final --nombre portal_finalTarda ~35 s los 240 frames (con la RTX 4060 por OpenCL).
- "No valid output AOVs specified". El ROP Image necesita el COP Path del nodo con la imagen y, en AOV Name, el nombre de su salida (
glow,tonemap,hsv_adjust…). El File también necesita un AOV declarado (C), o no tiene salidas. - Bloques y una costura a la mitad (arriba sin glow, abajo con glow): pasa al cargar el EXR como RGBA. Cárgalo como RGB; el fondo es negro y el alfa no hace falta.
- El piso aparece arriba de la imagen: el Border por defecto (Wrap) repite la imagen al hacer el blur. Usa Constant.
- Lento o con aviso "Falling back to built-in CPU OpenCL driver": Copernicus usa OpenCL. En Linux con NVIDIA instala
opencl-nvidia(sudo pacman -S opencl-nvidiaen Arch);clinfo -ldebe mostrar la tarjeta.
Cómo sé que me salió bien
En el frame 73 deberías tener, más o menos:
- ~4300 chispas vivas (
importar_chispas) y el mismo número de líneas enestelas_lineas. - El borde del anillo entre 1.9 y 2.15 m del centro: no es un círculo perfecto y la forma cambia con el tiempo.
- El centro limpio y unas ~400–500 chispas sobre el piso (y = -2.6).
- En Karma: chispas naranjas hacia afuera (no amarillas), un anillo amarillo deshilachado y un brillo naranja suave en el piso.
- Con humo: una bruma rojiza leve alrededor del aro, que se nota más después de la composición.
- En la composición: un halo naranja alrededor del anillo y de las chispas que entra un poco al hueco, sin costuras ni bloques, y el piso sin quemarse.
Si algo sale mal
| Síntoma | Causa probable | Solución |
|---|---|---|
| Las chispas parecen fuegos artificiales (puntos) | Falta estelas_lineas | Paso 7.3. Las chispas reales se ven como rayas |
| Las líneas parecen pelo pegado al anillo | Demasiado drag o chispas lentas | Air Resistance 0.3, vel_giro 9, vel_salida 3.5 |
| Las chispas de arriba se frenan y caen como polvo | Viven demasiado | Vida corta (0.5 ± 0.25 s, multiplicador 0.6–1.5) y más Birth Rate para compensar |
| El anillo brillante es una línea quebrada (zigzag) | Ruido de alta frecuencia por número de punto en look_anillo | Ruido por ángulo y de baja frecuencia, como en el código del paso 9 |
| Las chispas no se mueven al nacer | No heredan la velocidad | fuente_anillo → Attributes → Use inherited velocity |
| Chispas dentro del hueco | Fuerza hueco débil o metaball pequeña | Force Scale 30 y radio de la metaball = radio del anillo (radx de anillo_emisor) × 0.9 |
| Atraviesan el piso | Ground plane a la derecha del merge | piso en la 1ª entrada (izquierda) de colisiones |
| Un "chorro" enorme al inicio | La velocidad no escala con el radio | Las últimas líneas de velocidad_tangencial (las que leen el radio de anillo_emisor) |
| Las chispas salen negras en Karma | Geomprop Cd | Usa displayColor |
| Las chispas salen amarillas en Karma | El HDR satura el naranja | Colores más saturados e intensidad 3.5, no 6 |
| El piso no sale en Karma | Display flag del objeto piso apagado | Enciéndelo: en /obj el display flag es la visibilidad |
| Reflejo vertical muy marcado en el piso | Piso muy liso | Specular Roughness 0.55 |
| Cambio un valor y no pasa nada | Caché de la simulación | Reset Simulation en chispas_sim |
| No hay sliders / están en 0 | No se crearon los parámetros | Create spare parameters y escribe los valores |
look_anillo sale vacío | Borré el círculo con removeprim(0, 0, 1) | Usa removeprim(0, 0, 0) y luego removepoint como en el código |
| El humo sale con otro color o gris | Material asignado en el objeto | Asígnalo con un Material SOP dentro de humo (paso 12) |
| El humo no brilla | Rango de fuego 0–1 o temperature borrada | Source Range 0–0.4 y no borres temperature en el Blast |
| El humo no sale en el render | Interruptor apagado o caché vacía | CONTROLES → Humo en el aro ✔ y cache_humo con Load from Disk tras guardarla |
| ROP Image: "No valid output AOVs specified" | COP Path o AOV Name mal | COP Path al nodo saturacion y AOV Name hsv_adjust |
| Glow con bloques o una costura horizontal | EXR cargado como RGBA | Channel Type RGB en el File |
| El piso aparece arriba en la comp | Border Wrap | Border Constant en el File |
Para entenderlo
- Por qué líneas: una chispa real se mueve muy rápido y el ojo (o la cámara) ve su recorrido.
estelas_lineasdibuja ese recorrido como un motion blur hecho en geometría. Así se ve desde el viewport y se controla el largo. - Velocidad tangencial:
cross(eje, r)da la dirección que "barre" el borde del círculo. Sumarle algo der(hacia afuera) hace que salgan en espiral. - Cola larga: con
pow(rand, 3)la mayoría de las chispas tiene valores bajos y unas pocas valores altos. Así unas pocas vuelan lejos y el borde exterior se ve irregular, no como un anillo parejo. - Mapa de ruido vs. fórmula: antes de usar el mapa de ruido se probaron deformaciones por ángulo (respiración, óvalo, temblor, abolladuras), y se veían demasiado matemáticas. Un ruido 3D que se mueve por el espacio se ve más orgánico.
- Fuerza Metaball: RISE FX la usó en la película para que las chispas no entraran al círculo. Un objeto de colisión en el centro funciona peor y no debe verse en el render.
- Por qué el glow en composición: en el render cada cambio de glow costaría ~45 min; en Copernicus se ajusta mirando el resultado y se aplica a los 240 frames en segundos. Dos glows (uno chico y fuerte, otro grande y suave) se parecen más a cómo una cámara real ve una luz muy intensa que uno solo.
- Por qué el humo brilla solo: con una luz extra se iluminaba también el piso. La emisión del fuego del Pyro Shader solo afecta al humo.
Variaciones para probar
- Más caótico: sube Amplitude en
mapa_de_ruido(0.5–0.6) o baja Pulse Duration. - Más chispas lejanas: baja
sesgo(2) o subevel_max. - Portal más grande: cambia el
2 *de la expresión del radio (todo lo demás lo sigue). - Sin piso: desactiva el
piso(DOP) y el objetopiso; las chispas caen al vacío. - Otro color (magia verde, azul): cambia los tres colores de
color_por_edady los del anillo enlook_anillo.