From d94dcfb5af5cd5af1df9660fd971452b9e41a094 Mon Sep 17 00:00:00 2001 From: sergio Date: Fri, 15 May 2026 04:31:02 +0000 Subject: [PATCH] =?UTF-8?q?feat(gioser):=20ciclo=20sol=E2=86=94luna?= =?UTF-8?q?=E2=86=94tierra,=20auras=20anchas,=20nuevo=20set=20Software/Qui?= =?UTF-8?q?=C3=A9n=20Soy/Manifiesto/M=C3=ADstica?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shader (gioser-shaders): - 3 cuerpos centrales renderizados realísticamente con interpolación gradual entre ellos (cross-fade smoothstep): - render_sun: núcleo gauss + corona pulsante + textura de plasma FBM (boiling surface). - render_moon: disco con limb darkening, cráteres + mares (2 octavas de fbm), terminador móvil (fase lunar), halo azulado en el limb iluminado. - render_earth: disco con continentes fbm (rotación lenta), polos blancos, nubes en otra capa, día/noche en hemisferio iluminado, halo atmosférico azul (Rayleigh simplificado). - Uniforms u_body_a, u_body_b (int 0/1/2), u_body_blend (float). - Cuerpo central se calcula sólo si inside > 0.001 (perf — saltea pixels fuera de la superficie de la chacana). - radial_mult atenúa los rayos cuando luna/tierra están activos — el sol es el único que irradia tan intensamente. - element_cloud(): aura ancha por cardinal (sigma_along=0.42, sigma_perp=0.34) con textura fbm animada y modulación por elemento. - AIRE: corrientes suaves que ondulan horizontalmente. - FUEGO: lengüetazos rápidos con flicker. - TIERRA: densidad sólida con variación lenta. - AGUA: ondulaciones grandes que viajan hacia afuera. Las nubes cubren todo el cuadrante del cardinal, no solo la punta. - Helper functions vnoise_c + fbm_c agregadas (necesarias para superficies realistas de luna/tierra y para nubes elementales). Renderer (gioser-canvas-web): - body_state(t) -> (body_a, body_b, blend) state machine: - BODY_PHASE_SECS = 45 (≈10 pulsos del sol antes de transicionar). - BODY_TRANSITION_SECS = 4 (cross-fade gradual). - Total cycle: 147s = sol 45s → trans 4s → luna 45s → trans 4s → tierra 45s → trans 4s. - Smoothstep cubic en el blend para curva natural (no linear). - Sube u_body_a/b como int (uniform1i) y u_body_blend como float. App + contenido: - index.html: nuevos labels en los 4 tips - NORTE (aire): SOFTWARE / Tecnología - ESTE (fuego): QUIÉN SOY / Bitácora - SUR (tierra): MANIFIESTO / Invariantes - OESTE (agua): MÍSTICA / Espiritualidad - Íconos SVG nuevos relacionados al tema: - aire: chip de circuito con nodos y conexiones - fuego: libro abierto con líneas - tierra: hexagrama dentro de círculo (sacred geometry / invariante) - agua: ojo en triángulo (mística) - gioser-web src/lib.rs: ensure_page_dom usa nuevos title+tag por elemento. - 4 md/*.md reescritos con contenido seed para los nuevos temas, con manifiesto explícito en tierra.md. Workspace verde + 21 tests. Co-Authored-By: Claude Opus 4.7 --- crates/apps/gioser-web/index.html | 64 +++--- crates/apps/gioser-web/md/agua.md | 45 +++-- crates/apps/gioser-web/md/aire.md | 42 ++-- crates/apps/gioser-web/md/fuego.md | 40 ++-- crates/apps/gioser-web/md/tierra.md | 47 +++-- crates/apps/gioser-web/src/lib.rs | 8 +- .../gioser/gioser-canvas-web/src/lib.rs | 49 +++++ .../modules/gioser/gioser-shaders/src/lib.rs | 190 ++++++++++++++++-- 8 files changed, 377 insertions(+), 108 deletions(-) diff --git a/crates/apps/gioser-web/index.html b/crates/apps/gioser-web/index.html index 99fe19a..9254583 100644 --- a/crates/apps/gioser-web/index.html +++ b/crates/apps/gioser-web/index.html @@ -12,45 +12,59 @@ -
- +
+ + - AIRE - Software · IA + SOFTWARE + Tecnología - + + - FUEGO - Inspiración + QUIÉN SOY + Bitácora - + + - TIERRA - Cuerpo + MANIFIESTO + Invariantes - + + - AGUA + MÍSTICA Espiritualidad
diff --git a/crates/apps/gioser-web/md/agua.md b/crates/apps/gioser-web/md/agua.md index 43d52bb..37564e7 100644 --- a/crates/apps/gioser-web/md/agua.md +++ b/crates/apps/gioser-web/md/agua.md @@ -1,25 +1,38 @@ -# Agua +# Mística · Espiritualidad -> *Lo que fluye. Lo que une dentro y afuera.* +> *La práctica como puente. El misterio como interlocutor.* -El **Agua** es el dominio de la **espiritualidad aplicada**: las -prácticas, lecturas y tradiciones que sostienen la atención y dan -sentido al hacer. No es decoración mística: es la práctica concreta -de mantenerse permeable, vivo, conectado. +Acá vive lo místico, lo espiritual, las prácticas que sostienen la +atención. No es decoración: es la otra mitad del trabajo. Sin esto, +el resto se vuelve ruido. -## Espiritualidad aplicada +## Prácticas -Aplicada significa que no se queda en libros: pasa por la práctica -diaria — la lectura, la meditación, la ceremonia, la conversación -honda. El agua moja todos los otros ejes. +Lo que sostiene día a día: -## Lo que vive acá +- **Meditación.** Sentarse a observar lo que sucede, sin agarrarlo. +- **Lectura contemplativa.** Textos que se vuelven a leer hasta que + cambian. +- **Ceremonia.** Marcar inicios y cierres con gestos que pesan. +- **Naturaleza.** Estar en lugares donde uno no es el centro. +- **Silencio.** Día completo, una vez por mes mínimo. -- Notas de lectura sobre filosofía, mística, sabiduría andina. -- Diario de prácticas (meditación, ceremonias, retiros). -- Conversaciones con maestros y comunidades. +## Por qué mística + +Porque la racionalidad sola no alcanza para vivir. Y porque las +tradiciones llevan miles de años elaborando vocabulario para lo que +nos pasa cuando atendemos en serio: contemplación, ego, símbolo, +muerte, asombro. + +**Mística aplicada** = no quedarse en el libro. Pasar por el cuerpo, +por la relación, por la vida cotidiana. + +## Lo que leo + +Andino, budista, cristiano-contemplativo, hindú, sufí. Sin +exclusividad: cada tradición resuelve algunas cosas mejor que otras. ## Próximamente -*Acá se va a ir armando una bitácora de lecturas y prácticas. Por -ahora el placeholder verifica el render bajo el tema **agua**.* +*Acá se va a ir armando una bitácora de lecturas, prácticas y notas. +Por ahora este placeholder verifica el tema **agua** (cyan).* diff --git a/crates/apps/gioser-web/md/aire.md b/crates/apps/gioser-web/md/aire.md index 8147ce8..4e26052 100644 --- a/crates/apps/gioser-web/md/aire.md +++ b/crates/apps/gioser-web/md/aire.md @@ -1,26 +1,34 @@ -# Aire +# Software · Tecnología -> *Lo que respira el sistema. Lo que sube.* +> *Lo público. Lo que se mantiene abierto.* -El **Aire** es el dominio del **software público y la IA**. Es la capa -intangible que transporta pensamiento — los bits que vuelan entre -máquinas, las inferencias que destilan sentido del ruido, las APIs -que conversan sin verse. +Acá viven los proyectos de **software libre**, herramientas, librerías +y exploraciones técnicas que voy publicando. La premisa es mantener +el código abierto, documentado y útil más allá del autor. -## Aspiración +## Qué vas a encontrar acá -El Aire **aspira**: empuja hacia arriba. Es el movimiento de subir el -nivel de abstracción, de hacer que una cosa difícil parezca obvia, de -regalarle al usuario una herramienta que no le pesa. +- Repos públicos de cosas que escribo (Rust, Python, embedded, web). +- Notas técnicas sobre arquitectura, sistemas distribuidos, runtimes. +- Ensayos sobre **IA aplicada** — sin hype, con ejemplos concretos. +- Bitácoras de exploración: lo que probé, lo que descarté, lo que sigo + usando. -## Lo que vive acá +## Por qué open source -- Herramientas open source que **GioSer** publica y mantiene. -- Modelos de IA que asisten al ciclo de creación. -- Documentación, ensayos, manifiestos. +Porque el conocimiento técnico se multiplica cuando circula. Y porque +mucho de lo que uso a diario me lo regaló alguien que decidió compartir. +La reciprocidad importa. + +## Stack actual + +- **Rust** para lo que necesita ser rápido, seguro y portable. +- **Python** para análisis, ML y prototipos rápidos. +- **Linux** (Artix/Arch) como sistema operativo de trabajo. +- **gitea** + **nix** para infraestructura personal. ## Próximamente -*Esta sección se va a llenar con los proyectos concretos del eje aire.* -Por ahora, este placeholder vive en `md/aire.md` y se renderiza vía -`pluma-md` con tema *aire*. +*Voy a ir enlazando proyectos específicos acá: tools, runtimes, +experimentos. Por ahora, este placeholder vive en `md/aire.md` y se +renderea con el tema **aire** (azul-blanco).* diff --git a/crates/apps/gioser-web/md/fuego.md b/crates/apps/gioser-web/md/fuego.md index f78e4fb..212d2c9 100644 --- a/crates/apps/gioser-web/md/fuego.md +++ b/crates/apps/gioser-web/md/fuego.md @@ -1,25 +1,33 @@ -# Fuego +# Quién Soy · Bitácora -> *Lo que enciende. Lo que transforma.* +> *La identidad como verbo. La crónica como práctica.* -El **Fuego** es el dominio de la **inspiración**. Es la chispa que -convierte una idea en gesto, una frase en ritual, un problema en -prototipo. Sin fuego, los otros tres elementos se enfrían y se quedan -contemplándose. +Acá vive lo personal: quién soy, qué hago, qué leo, qué pienso. Una +bitácora honesta, no curada para impresionar. Si vas a leer esto, +asumí que es borrador. -## Inspiración +## Quién soy -El fuego no se planea, se **atiende**. Llega — y la respuesta es no -dejarlo pasar. Acá viven los ensayos, los videos, los manifiestos y -los experimentos que nacieron porque algo prendió. +**Sergio**. Programador, lector, padre, alguien que practica +mantenerse despierto. Vivo entre código, café, montañas y libros. -## Lo que vive acá +Las cosas que más me importan no son las que mejor cuento todavía. +Por eso escribo: para precisar lo que sé y lo que no. -- Charlas, ensayos cortos, posts crudos. -- Bocetos visuales, exploraciones tipográficas. -- Documentos de manifiesto sobre cómo trabajar y para qué. +## Bitácora + +Notas más o menos diarias sobre lo que voy pensando, viviendo, +fallando. Sin algoritmo de engagement, sin métricas. Sólo crónica. + +Las entradas se ordenan por fecha. Las más viejas a veces dicen cosas +que ya no pienso así — las dejo igual. + +## Por qué publicarlo + +Porque escribir en público obliga a precisar. Y porque a veces lo que +uno escribe para sí mismo le sirve a otra persona que no conoce. ## Próximamente -*Voy a ir enlazando archivos `.md` específicos acá. Por ahora este -texto sirve para verificar el render bajo el tema **fuego**.* +*Acá se va a ir armando una bitácora con entradas fechadas. Por ahora +este placeholder verifica el tema **fuego** (ámbar/escarlata).* diff --git a/crates/apps/gioser-web/md/tierra.md b/crates/apps/gioser-web/md/tierra.md index 49cb2a4..d1fb42e 100644 --- a/crates/apps/gioser-web/md/tierra.md +++ b/crates/apps/gioser-web/md/tierra.md @@ -1,26 +1,39 @@ -# Tierra +# Manifiesto · Invariantes -> *El cuerpo. La materia. Lo que sostiene.* +> *Lo que no cambia. La piedra de toque.* -La **Tierra** es el dominio del **cuerpo**. Es lo que se toca, lo que -huele, lo que se siembra. El eje terrestre de GioSer recuerda que -todo proyecto —por muy abstracto que parezca— pasa por un cuerpo que -respira, come, descansa y se conmueve. +Acá vive el manifiesto de GioSer: las **invariantes** que sostienen +todo lo demás. Lo que no negocio, lo que define la forma del trabajo +antes que cualquier proyecto particular. -## Cuerpo +## Invariantes -El cuerpo no es una metáfora: es donde aterriza el aire, donde el -agua se vuelve vida, donde el fuego deja huella. Cuidarlo es parte -del trabajo. +Cosas que considero **no-negociables** en cómo hago el trabajo: -## Lo que vive acá +- **Código abierto por defecto.** Si tiene sentido, se publica. +- **Honestidad por encima de marketing.** No prometo lo que no puedo + cumplir, ni vendo lo que no probé. +- **El cuerpo es infraestructura.** Cuidarlo es parte del trabajo, no + opuesto al trabajo. Sin cuerpo no hay nada. +- **Las ideas se prueban escribiéndolas.** Si no hay documento, todavía + no existe la idea. +- **Compatibilidad hacia abajo > novedad arriba.** Las invariantes + duran, las modas no. +- **Una sola voz.** Lo que digo en privado coincide con lo que publico. -- Prácticas, rutinas, recetas. -- Materialidad: objetos, lugares, oficios. -- Salud y reposo como infraestructura. +## Por qué un manifiesto + +Porque sin invariantes, cada decisión es ad hoc. Tener un set chico de +principios reduce la energía gastada en cada elección — y deja en +claro cuándo estoy contradiciéndome. + +## Revisión + +Este manifiesto se revisa una vez al año, no antes. Si una invariante +deja de aplicarse, se quita con una explicación pública. ## Próximamente -*Esta sección va a recibir notas, fotos y enlaces a oficios y -prácticas concretas. Por ahora el placeholder verifica el tema -**tierra**.* +*Esta sección va a recibir el manifiesto completo + revisiones +históricas. Por ahora este placeholder verifica el tema **tierra** +(ocre cálido).* diff --git a/crates/apps/gioser-web/src/lib.rs b/crates/apps/gioser-web/src/lib.rs index 86f9b3e..03af2fb 100644 --- a/crates/apps/gioser-web/src/lib.rs +++ b/crates/apps/gioser-web/src/lib.rs @@ -226,10 +226,10 @@ impl AppState { return; }; let (title, tag) = match element { - "aire" => ("Aire", "Software · IA · Aspiración"), - "fuego" => ("Fuego", "Inspiración"), - "tierra" => ("Tierra", "Cuerpo"), - "agua" => ("Agua", "Espiritualidad aplicada"), + "aire" => ("Software", "Tecnología · Open Source · IA"), + "fuego" => ("Quién Soy", "Bitácora · Crónica"), + "tierra" => ("Manifiesto", "Invariantes · Piedra de toque"), + "agua" => ("Mística", "Espiritualidad aplicada"), _ => return, }; let html = format!( diff --git a/crates/modules/gioser/gioser-canvas-web/src/lib.rs b/crates/modules/gioser/gioser-canvas-web/src/lib.rs index bb34011..1311531 100644 --- a/crates/modules/gioser/gioser-canvas-web/src/lib.rs +++ b/crates/modules/gioser/gioser-canvas-web/src/lib.rs @@ -31,6 +31,16 @@ const COT_HALF_FOV: f32 = 2.414_213_5; /// con `FS_CHACANA::ringR_main` del shader. const RING_FACTOR: f32 = 1.45; +/// Duración en segundos de cada "cuerpo central" (sol / luna / tierra) +/// antes de empezar a transicionar al siguiente. ~45 s = ~10 pulsos del +/// sol (`sin(t*1.4)` período ≈ 4.5 s). +const BODY_PHASE_SECS: f32 = 45.0; +/// Duración del cross-fade entre cuerpos. Más alto = transiciones más +/// graduales. +const BODY_TRANSITION_SECS: f32 = 4.0; +/// Cantidad de cuerpos en el ciclo: sol(0), luna(1), tierra(2). +const BODY_COUNT: i32 = 3; + /// Identidad de cada cardinal (id, color de acento, label). Orden `[N, E, S, W]`. pub mod tips { use gioser_palette::{elements, Rgb}; @@ -227,6 +237,9 @@ impl Renderer { "u_agua_color", "u_zodiac[0]", "u_sun_pulse", + "u_body_a", + "u_body_b", + "u_body_blend", ], ) .map_err(JsValue::from)?; @@ -475,6 +488,16 @@ impl Renderer { if let Some(u) = self.chacana_prog.u("u_sun_pulse") { gl.uniform1f(Some(u), self.sun_pulse); } + let (body_a, body_b, blend) = body_state(t); + if let Some(u) = self.chacana_prog.u("u_body_a") { + gl.uniform1i(Some(u), body_a); + } + if let Some(u) = self.chacana_prog.u("u_body_b") { + gl.uniform1i(Some(u), body_b); + } + if let Some(u) = self.chacana_prog.u("u_body_blend") { + gl.uniform1f(Some(u), blend); + } gl.bind_vertex_array(Some(&self.chacana_vao)); gl.draw_arrays(GL::TRIANGLES, 0, self.chacana_quad_count); gl.bind_vertex_array(None); @@ -486,3 +509,29 @@ fn upload_rgb(gl: &GL, loc: Option<&WebGlUniformLocation>, c: Rgb) { gl.uniform3f(Some(u), c.0, c.1, c.2); } } + +/// Devuelve `(body_a, body_b, blend)` para el tiempo `t` (segundos). +/// El ciclo es sol → luna → tierra → sol → ... cada uno estable por +/// `BODY_PHASE_SECS` y con `BODY_TRANSITION_SECS` de cross-fade al +/// siguiente. +fn body_state(t: f32) -> (i32, i32, f32) { + let slot = BODY_PHASE_SECS + BODY_TRANSITION_SECS; + let cycle = slot * BODY_COUNT as f32; + let pt = t.rem_euclid(cycle).max(0.0); + let mut acc = 0.0_f32; + for i in 0..BODY_COUNT { + let stable_end = acc + BODY_PHASE_SECS; + if pt < stable_end { + return (i, i, 0.0); + } + let trans_end = stable_end + BODY_TRANSITION_SECS; + if pt < trans_end { + let blend = ((pt - stable_end) / BODY_TRANSITION_SECS).clamp(0.0, 1.0); + // Smoothstep para una curva más natural que linear lerp. + let s = blend * blend * (3.0 - 2.0 * blend); + return (i, (i + 1) % BODY_COUNT, s); + } + acc = trans_end; + } + (0, 0, 0.0) +} diff --git a/crates/modules/gioser/gioser-shaders/src/lib.rs b/crates/modules/gioser/gioser-shaders/src/lib.rs index 773dbc5..fdfb568 100644 --- a/crates/modules/gioser/gioser-shaders/src/lib.rs +++ b/crates/modules/gioser/gioser-shaders/src/lib.rs @@ -192,6 +192,11 @@ uniform vec3 u_tierra_color; uniform vec3 u_agua_color; uniform vec3 u_zodiac[12]; uniform float u_sun_pulse; +// Ciclo del cuerpo central: 0=sol, 1=luna, 2=tierra. Se interpola entre +// `u_body_a` y `u_body_b` con `u_body_blend ∈ [0, 1]`. +uniform int u_body_a; +uniform int u_body_b; +uniform float u_body_blend; const float PI = 3.14159265; @@ -201,12 +206,147 @@ float hash21c(vec2 p) { float hash11c(float n) { return fract(sin(n * 78.233) * 43758.5453); } +// Value noise + fbm para superficies (lunar craters, continentes terrestres). +float vnoise_c(vec2 p) { + vec2 i = floor(p); + vec2 f = fract(p); + f = f * f * (3.0 - 2.0 * f); + float a = hash21c(i); + float b = hash21c(i + vec2(1.0, 0.0)); + float c = hash21c(i + vec2(0.0, 1.0)); + float d = hash21c(i + vec2(1.0, 1.0)); + return mix(mix(a, b, f.x), mix(c, d, f.x), f.y); +} +float fbm_c(vec2 p) { + float v = 0.0; + float a = 0.5; + for (int i = 0; i < 4; i++) { + v += a * vnoise_c(p); + p = p * 2.03 + vec2(1.7, 9.2); + a *= 0.5; + } + return v; +} float sdBox(vec2 p, vec2 b) { vec2 d = abs(p) - b; return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0); } +// ===== CUERPO CENTRAL: Sol / Luna / Tierra ===== +// +// Cada uno renderea dentro del cuadrado central de la chacana con su +// propia personalidad realista. El loop temporal (cambio entre cuerpos +// + transiciones graduales) lo decide el host vía u_body_a/b/blend. + +vec3 render_sun(vec2 p, float r, float pulse) { + // Núcleo brillante + corona difusa con pulso. + float coreR = u_thickness * 0.42; + float core = exp(-(r * r) / (2.0 * coreR * coreR)); + float halR = u_center_half * 0.70; + float halo = exp(-(r * r) / (2.0 * halR * halR)); + // Superficie boiling (plasma) muy sutil. + float plasma = fbm_c(p * 18.0 + vec2(u_time * 0.15, u_time * 0.10)) * 0.20; + vec3 base = u_sun_color * (core * (1.20 + 0.25 * pulse) + halo * (0.55 + 0.15 * pulse)); + return base + u_sun_color * core * plasma * 0.6; +} + +vec3 render_moon(vec2 p, float r, float time) { + float moonR = u_thickness * 1.35; + // Disco con borde suave. + float disk = 1.0 - smoothstep(moonR * 0.86, moonR * 1.00, r); + // Limb darkening: el borde más oscuro que el centro. + float limb_factor = sqrt(max(1.0 - (r * r) / (moonR * moonR), 0.0)); + // Cráteres y mares lunares vía fbm. + float craters = fbm_c(p * 22.0) * 0.45 + fbm_c(p * 48.0) * 0.20; + // Fase: terminator se mueve de izquierda a derecha lentamente. + // 1 ciclo lunar visible cada ~38 s. sin(t*0.165) recorre full→new→full. + float phase = sin(time * 0.165) * 0.5 + 0.5; // 0..1 + float terminator_x = mix(-moonR * 1.1, moonR * 1.1, phase); + float nx = p.x; + float lit = smoothstep(terminator_x - moonR * 0.12, terminator_x + moonR * 0.05, nx); + // Color superficie lunar (gris-azulado). + vec3 surface = vec3(0.86, 0.88, 0.94) * (0.70 + craters * 0.55); + // Halo cercano al limb iluminado (luz dispersada). + float outer_glow = smoothstep(moonR * 1.18, moonR * 0.94, r) - disk; + vec3 glow = vec3(0.55, 0.70, 0.95) * max(outer_glow, 0.0) * lit * 0.50; + return surface * disk * lit * limb_factor + glow; +} + +vec3 render_earth(vec2 p, float r, float time) { + float earthR = u_thickness * 1.35; + float disk = 1.0 - smoothstep(earthR * 0.86, earthR * 1.00, r); + float limb_factor = sqrt(max(1.0 - (r * r) / (earthR * earthR), 0.0)); + // Rotación lenta: continentes drift horizontalmente. + float rot = time * 0.08; + vec2 rp = p + vec2(rot, 0.0); + // Continentes con fbm orgánico. + float landmass = fbm_c(rp * 7.5); + float is_land = smoothstep(0.50, 0.56, landmass); + vec3 ocean = vec3(0.08, 0.28, 0.52); + vec3 land = vec3(0.30, 0.52, 0.24); + vec3 land_high = vec3(0.55, 0.45, 0.28); // montañas / desiertos + land = mix(land, land_high, smoothstep(0.60, 0.78, landmass)); + vec3 surface = mix(ocean, land, is_land); + // Casquetes polares. + float ny = abs(p.y / max(earthR, 1e-4)); + float polar = smoothstep(0.70, 0.94, ny); + surface = mix(surface, vec3(0.96, 0.97, 1.0), polar * 0.85); + // Nubes flotando en otra capa rotando algo distinto. + float clouds = fbm_c(rp * 6.0 + vec2(rot * 0.3, 0.0)) * 0.7; + float cloud_mask = smoothstep(0.55, 0.75, clouds); + surface = mix(surface, vec3(0.95, 0.96, 0.99), cloud_mask * 0.40); + // Día / noche: hemisferio iluminado. + float lit = smoothstep(-0.45, 0.55, p.x / max(earthR, 1e-4) + 0.15); + surface *= 0.30 + 0.70 * lit; + // Atmósfera azul en el limb (Rayleigh scattering simplificado). + float atm_inner = smoothstep(earthR * 1.10, earthR * 0.95, r); + float atm = max(atm_inner - disk, 0.0); + return surface * disk * limb_factor + vec3(0.30, 0.55, 0.95) * atm * 0.55; +} + +vec3 render_body(int kind, vec2 p, float r, float time, float pulse) { + if (kind == 0) return render_sun(p, r, pulse); + if (kind == 1) return render_moon(p, r, time); + return render_earth(p, r, time); +} + +// ===== AURA ELEMENTAL (NUBE ANCHA POR CARDINAL) ===== +// Se suma a las partículas puntuales: una cobertura ancha del cuadrante +// del cardinal correspondiente, con personalidad por elemento. +vec3 element_cloud(vec2 p, vec2 tip, vec2 outward, vec3 color, float time, int kind) { + vec2 perp = vec2(-outward.y, outward.x); + // Centro de la nube: bien adentro del cuadrante (más allá del tip). + vec2 cloud_center = tip + outward * 0.22; + vec2 to_p = p - cloud_center; + float along = dot(to_p, outward); + float perp_d = dot(to_p, perp); + // Anisotropía: bien ancha perpendicular, larga a lo largo del eje. + float sigma_along = 0.42; + float sigma_perp = 0.34; + float base = exp(-(along * along) / (2.0 * sigma_along * sigma_along) + -(perp_d * perp_d) / (2.0 * sigma_perp * sigma_perp)); + // Textura noise animada por elemento. + vec2 noise_uv = (p - tip) * (3.5 + float(kind) * 0.4) + + vec2(time * (0.20 + float(kind) * 0.05), time * 0.10); + float n = fbm_c(noise_uv); + float modulation; + if (kind == 0) { + // AIRE: corrientes suaves que se mueven horizontalmente. + modulation = 0.55 + 0.45 * (n * 0.7 + sin(time * 0.6 + perp_d * 4.0) * 0.3); + } else if (kind == 1) { + // FUEGO: lengüetazos que parpadean rápido. + modulation = (0.40 + 0.60 * n) * (0.7 + 0.3 * sin(time * 3.2 + along * 8.0)); + } else if (kind == 2) { + // TIERRA: densidad sólida con variación lenta. + modulation = 0.65 + 0.35 * fbm_c(p * 4.0 + vec2(time * 0.05, 0.0)); + } else { + // AGUA: ondulaciones grandes que viajan hacia afuera. + modulation = 0.50 + 0.50 * sin(time * 0.9 - along * 5.0 + n * 4.0); + } + return color * base * max(modulation, 0.0) * 0.28; +} + // Chacana de 2 escalones (mística clásica de Tiwanaku). float sdChacana(vec2 p, float s, float c) { float d = sdBox(p, vec2(c, c)); @@ -311,22 +451,31 @@ void main() { float d = sdChacana(p, u_thickness, u_center_half); float r = length(p); - // === SOL DETRÁS === - // Halo grande, sólo visible dentro de la superficie de la chacana. + // === CUERPO CENTRAL — SOL / LUNA / TIERRA === + // Sólo se computa dentro de la superficie de la chacana (perf + estética). float inside = 1.0 - smoothstep(-0.004, 0.004, d); - float sunR = u_thickness * 0.42; - float sun = exp(-(r * r) / (2.0 * sunR * sunR)); - float corR = u_center_half * 0.75; - float corona = exp(-(r * r) / (2.0 * corR * corR)); - float halo = sun * (1.15 + 0.20 * u_sun_pulse) + corona * (0.55 + 0.15 * u_sun_pulse); + vec3 central = vec3(0.0); + if (inside > 0.001) { + central = render_body(u_body_a, p, r, u_time, u_sun_pulse); + if (u_body_blend > 0.001) { + vec3 next_body = render_body(u_body_b, p, r, u_time, u_sun_pulse); + central = mix(central, next_body, u_body_blend); + } + } - // Rayos radiales sutiles desde el centro, sólo visibles donde la superficie - // de la chacana los recibe. + // Rayos radiales sutiles desde el centro (el cuerpo los proyecta a + // través de la superficie). El multiplicador disminuye cuando la luna + // o la tierra están activas — el sol es el que más irradia. float ang = atan(p.y, p.x); + float radial_mult = (u_body_a == 0) ? 1.0 : 0.35; + if (u_body_blend > 0.001) { + float next_mult = (u_body_b == 0) ? 1.0 : 0.35; + radial_mult = mix(radial_mult, next_mult, u_body_blend); + } float radial = pow(abs(cos(ang * 4.0 + sin(u_time * 0.3) * 0.2)), 8.0) * smoothstep(0.0, u_center_half * 0.8, r) * (1.0 - smoothstep(u_center_half * 0.85, u_center_half * 1.2, r)) - * 0.30; + * 0.30 * radial_mult; // === DOBLE OUTLINE === // Línea interior (sobre la SDF=0). @@ -362,6 +511,15 @@ void main() { particles += element_particles(p, vec2(0.0, -L), vec2(0.0, -1.0), u_tierra_color, 2, 3.11); particles += element_particles(p, vec2(-L, 0.0), vec2(-1.0, 0.0), u_agua_color, 3, 5.97); + // === AURA ELEMENTAL ANCHA === + // Una nube wide por cardinal — ocupa el cuadrante entero, no sólo la + // punta. Las partículas puntuales aportan detalle agudo encima. + vec3 clouds = vec3(0.0); + clouds += element_cloud(p, vec2(0.0, L), vec2(0.0, 1.0), u_aire_color, u_time, 0); + clouds += element_cloud(p, vec2( L, 0.0), vec2( 1.0, 0.0), u_fuego_color, u_time, 1); + clouds += element_cloud(p, vec2(0.0, -L), vec2(0.0, -1.0), u_tierra_color, u_time, 2); + clouds += element_cloud(p, vec2(-L, 0.0), vec2(-1.0, 0.0), u_agua_color, u_time, 3); + // === TRAZOS ZODIACALES === // 12 líneas radiales muy sutiles entre la chacana y el aro principal, // una por signo, con sus colores significativos (Aries=fuego rojo, @@ -401,11 +559,13 @@ void main() { // === COMPOSICIÓN === vec3 col = vec3(0.0); - // Sol detrás (clip a interior). - col += u_sun_color * halo * inside * 1.55; + // Cuerpo central (sol / luna / tierra) clipeado al interior de la chacana. + col += central * inside * 1.55; col += u_line_color * radial * inside * 0.6; // Niebla oscura translúcida en el interior para profundidad. col += u_dark_color * inside * 0.20; + // Auras anchas de los elementos (debajo de los aros, sin clip al interior). + col += clouds; // Líneas y aros. col += u_line_color * line * 1.70; col += u_line_color * glow * 0.95; @@ -416,9 +576,13 @@ void main() { col += zodiac * 0.55; // muy sutil — apenas visible. float zodiac_lum = zodiac.r + zodiac.g + zodiac.b; + float cloud_lum = clouds.r + clouds.g + clouds.b; + float central_lum = central.r + central.g + central.b; float alpha = clamp( - halo * inside + line + glow + ring_main + ring_inner + dots + inside * 0.12 + central_lum * inside * 0.7 + line + glow + ring_main + ring_inner + + dots + inside * 0.12 + (particles.r + particles.g + particles.b) * 0.5 + + cloud_lum * 0.45 + zodiac_lum * 0.3, 0.0, 1.0); fragColor = vec4(col, alpha);