Cielo, atardeceres y renderizado de planetas
(blog.maximeheckel.com)- El shader del navegador combina dispersión de Rayleigh, dispersión de Mie y absorción de ozono para renderizar en tiempo real cielos azules y atardeceres/amaneceres
- Acumula la profundidad óptica de los rayos de cámara y la transmitancia según la ley de Beer, y calcula la distribución de la dispersión según la dirección del sol con funciones de fase
- El efecto del atardecer realiza un light-march separado hacia la dirección del sol en cada muestra, para reflejar cuánto se pierde la luz solar al atravesar la atmósfera
- El shader de cielo plano se convierte en un efecto de posprocesado mediante el depth buffer y la reconstrucción de coordenadas de mundo, y también procesa la niebla atmosférica entre los objetos de la escena
- A escala planetaria, se amplía con logarithmic depth buffer, ray-sphere intersection y Transmittance, Sky-view y Aerial Perspective basados en LUT
Objetivos y materiales de referencia del shader de dispersión atmosférica
- El objetivo es recrear con shaders de navegador capas como las de las fotos de atardeceres en órbita baja del transbordador espacial Endeavour, con naranja oscuro, azul y el negro del fondo espacial en la atmósfera superior de la Tierra
- El alcance de la implementación comienza con un sky dome realista que combina raymarching, dispersión de Rayleigh, dispersión de Mie y absorción de ozono, y luego se extiende a una capa atmosférica alrededor del planeta y optimizaciones basadas en LUT
- Las referencias principales son Three Geospatial, A Scalable and Production Ready Sky and Atmosphere Rendering Technique de Sébastien Hillaire, y Atmospheric Scattering (and also just faking it)
Modelo básico del renderizado del cielo
-
Por qué un gradiente simple no basta
- El color del cielo no debe tratarse como un simple fondo azul, sino como el resultado de la interacción de la luz con el aire y sus componentes
- Hay que considerar variables como la altitud del observador, la cantidad de polvo y la hora del día, y el cálculo se realiza dentro de un volumen (volume)
-
Muestreo de densidad atmosférica
- La atmósfera se muestrea con raymarching, igual que volumetric clouds o volumetric light
- Se lanza un rayo desde la posición de la cámara y se avanza por el medio transparente, calculando la transmitancia (transmittance), que es la luz que sobrevive al atravesar la atmósfera, y la dispersión (scattering) redirigida hacia la cámara en cada muestra
- Como repaso de raymarching, se puede consultar Painting with Math: A Gentle Study of Raymarching
-
Densidad de Rayleigh y profundidad óptica
- Para obtener la transmitancia, hay que acumular la densidad atmosférica que encuentra el rayo a lo largo de su trayecto y calcular la profundidad óptica (optical depth)
- La función de densidad de Rayleigh representa cuánto “aire” hay a una altitud
h, y refleja el efecto de que la atmósfera se vuelve más tenue a mayor altura - La implementación de ejemplo usa
RAYLEIGH_SCALE_HEIGHT = 8.0km,ATMOSPHERE_HEIGHT = 100.0km,VIEW_DISTANCE = 200.0km yPRIMARY_STEPS = 24 rayleighDensity(h)esexp(-max(h, 0.0) / RAYLEIGH_SCALE_HEIGHT), y en el bucle se acumula conviewOpticalDepth += dR * stepSize
-
La ley de Beer y el azul del cielo diurno
- A partir de la profundidad óptica se calcula la transmitancia
Tde un punto dado, dondeT=1.0significa que no hay pérdida de luz yT=0.0que la luz desapareció por completo - La transmitancia se calcula con la ley de Beer, y el código de ejemplo usa
vec3 transmittance = exp(-rayleighBeta * viewOpticalDepth) rayleighBetaes el coeficiente de dispersión de Rayleigh, y en el shader se guarda comovec3(0.0058, 0.0135, 0.0331)- El ángulo entre la dirección de la luz solar y el rayo de visión se modela con la función de fase de Rayleigh de la forma
3.0 / (16.0 * PI) * (1.0 + mu * mu) - Debido al coeficiente de dispersión de Rayleigh, el rojo casi no se dispersa, el verde se dispersa un poco más y el azul es el que más se dispersa, por eso el cielo diurno se ve azul
- Al extenderlo a un rayo por píxel, hacia el horizonte atraviesa más atmósfera y se ve como una neblina blanca brillante, mientras que a mayor altitud cambia a un azul más profundo y oscuro
- A partir de la profundidad óptica se calcula la transmitancia
Dispersión de Mie y absorción de ozono
-
Efectos que Rayleigh por sí sola no cubre
- Aunque solo con la dispersión de Rayleigh ya se pueden obtener resultados aceptables, un cielo más realista necesita efectos atmosféricos adicionales
- La dispersión de Mie representa la interacción entre la luz y partículas más grandes, como polvo o aerosoles, y tiene una función de densidad y una función de fase que describe la redistribución según la dirección
- La absorción de ozono elimina del trayecto ciertas longitudes de onda de la luz que atraviesa la atmósfera superior, en lugar de dispersarlas
- La absorción de ozono profundiza el color del cielo y desplaza sus tonos, especialmente en el horizonte, los atardeceres, los amaneceres y el crepúsculo
-
Acumulación de Mie y ozono
- Una implementación que usa juntos Rayleigh, Mie y ozono acumula la profundidad óptica de cada uno en
viewODR,viewODMyviewODO - En cada muestra se calculan
dR = rayleighDensity(h),dM = mieDensity(h)ydO = ozoneDensity(h), ytause compone de la suma deBETA_R * viewODR,BETA_M_EXT * viewODMyBETA_OZONE_ABS * viewODO - La transmitancia se calcula como
exp(-tau), y ensumR,sumMysumOse acumulan la densidad, la transmitancia ystepSizede cada una - La dispersión final se calcula con la forma
SUN_INTENSITY * (phaseR * BETA_R * sumR + phaseM * BETA_M_SCATTER * sumM + BETA_OZONE_SCATTER * sumO)
- Una implementación que usa juntos Rayleigh, Mie y ozono acumula la profundidad óptica de cada uno en
-
Constantes principales y sus efectos
MIE_SCALE_HEIGHTes el equivalente para aerosoles deRAYLEIGH_SCALE_HEIGHT, y como las partículas suelen concentrarse cerca del horizonte, se configura más bajo, en1.2kmMIE_BETA_SCATTERcontrola cuánto dispersan las partículas la luz hacia la cámara y, como en general es casi independiente de la longitud de onda, se establece envec3(0.003)MIE_BETA_EXTes el coeficiente de extinción de Mie que indica cuánta luz se elimina del trayecto, haciendo que la atmósfera lejana se vea más brumosaMIE_Gcontrola la anisotropía:0.0significa dispersión uniforme y1.0indica un sesgo más fuerte hacia la dispersión frontalOZONE_BETA_ABStiene el valorvec3(0.00065, 0.00188, 0.00008)y absorbe más las gamas verde y amarillo-naranja, desplazando el color del cielo hacia azul, rojo y violeta- Al integrar Mie y ozono se obtiene un color “sky blue” más natural y un halo brumoso alrededor del sol, y el efecto de la dispersión de Mie se vuelve más evidente cuando el sol está cerca del horizonte
Trayectorias de luz y atardeceres·amaneceres
-
Límites de la implementación existente
- El fragment shader del cielo puede renderizar colores naturales a distintas altitudes y reflejar modelos de transmitancia de Mie, Rayleigh y ozono
- Sin embargo, incluso al mover el sol cerca del horizonte, solo aparece un resplandor blanco y difuso sin atenuación de la luz ni efecto de atardecer o amanecer
- Esto se debe a que el bucle de raymarching existente solo calculaba la atenuación de la luz en el rayo de vista desde la cámara hasta cada muestra
- También hay que calcular cuánto se pierde la luz solar al atravesar la atmósfera antes de llegar al punto de muestreo
-
Bucle anidado de light-march
- En cada punto de muestreo se ejecuta un bucle anidado separado en la dirección de la fuente de luz para muestrear la transmitancia de esa trayectoria
- Un enfoque relacionado también se usa en real-time cloudscapes y volumetric lighting
lightMarch(float start, float sunY)repiteLIGHTMARCH_STEPSveces mientras acumulaodR,odM,odO- Suma la profundidad óptica en dirección al sol
sunODa las profundidades ópticas de la implementación existenteviewODR,viewODM,viewODO - El
taufinal se compone sumandoBETA_R * (viewODR + sunOD.x),BETA_M_EXT * (viewODM + sunOD.y),BETA_OZONE_ABS * (viewODO + sunOD.z) - Con esta implementación se puede renderizar el cielo en atardeceres, amaneceres, con el sol en el cenit y en condiciones de iluminación intermedias
- Con el uniform
sun anglese crea la variación del azul del cielo a lo largo del día, y la dispersión de Mie mezcla naturalmente la luz con el horizonte en atardeceres y amaneceres - Cuando el sol está bajo, el ozono añade tonos violáceos al cielo
Extensión a atmósferas planetarias
-
De fondo plano a efecto de postprocesado
- El shader creado antes ofrece un buen fondo de cielo, pero se acerca más a un fondo plano en una escena de React Three Fiber
- El siguiente paso es convertirlo en un efecto de postprocesado (post-processing effect) para renderizarlo como un volumen que considera la profundidad de la escena y como una capa atmosférica que rodea la malla del planeta
- Para ello, se reconstruyen coordenadas del espacio mundial desde
screenUVy se incorpora el depth buffer de la escena al raymarching
-
Reconstrucción del espacio mundial y rayos 3D
- Para aplicar la dispersión atmosférica a la escena, no basta con dibujar solo el cielo: hay que llenar el espacio entre la cámara y los objetos renderizados en pantalla
- Los datos necesarios son el depth buffer de la escena,
projectionMatrixInverse,matrixWorldypositionde la cámara, y estos valores se pasan como uniform al efecto de postprocesado getWorldPosition(vec2 uv, float depth)creaclipZcondepth * 2.0 - 1.0, genera coordenadas NDC conuv * 2.0 - 1.0y luego aplicaprojectionMatrixInverseyviewMatrixInverse- El mismo proceso también se usa en el efecto de postprocesado de volumetric lighting de On Shaping Light
- Después de obtener la
worldPositiondel píxel actual,rayOriginse toma como la posición de la cámara yrayDirse calcula connormalize(worldPosition - rayOrigin)para avanzar siguiendo un rayo 3D por cada píxel en pantalla
-
Ajuste del tramo de raymarch con el depth buffer
- Para tener en cuenta la geometría de la escena, en lugar de un
stepSizefijo hay que determinar el tramo de raymarch del rayo actual con el depth buffer sceneDepth = depthToRayDistance(uv, depth)obtiene la profundidad de la escena sobre el rayo- Los píxeles de fondo se identifican con
depth >= 1.0 - 1e-7, y a los “sky pixels” se les aplicasceneDepth = atmosphereHeight * SKY_MARCH_DISTANCE_MULTIPLIER - Si el rayo apunta hacia abajo, la intersección con el suelo se calcula con
tGround = observerAltitude / max(-rayDir.y, 1e-4)y se limita conrayEnd = min(rayEnd, tGround) - El
stepSizefinal se calcula como(rayEnd - rayStart) / float(PRIMARY_STEPS) - Los rayos que tocan objetos cercanos o el suelo se muestrean con un
stepSizepequeño para mayor precisión, mientras que los rayos que llegan más lejos distribuyen la misma cantidad de muestras sobre una distancia mayor
- Para tener en cuenta la geometría de la escena, en lugar de un
-
Niebla atmosférica dentro de la escena
- El shader implementado como efecto de postprocesado aplica dispersión atmosférica a todo el volumen de la escena y permite usar el sky shader como fondo mientras tiene en cuenta la geometría de la escena
- Los objetos cercanos a la cámara se ven más nítidos, y los objetos lejanos se difuminan más
- Un ejemplo interactivo con cuerpos celestes que se pueden arrastrar con
Raycasterpuede verse en el tuit de MaximeHeckel
Renderizado de planetas
-
Dos pasos necesarios
- Para renderizar una atmósfera realista alrededor de un planeta, se necesita un logarithmic depth buffer para manejar escalas grandes y una capa atmosférica esférica que defina dónde comienza y termina el rayo dentro de la atmósfera
-
logarithmic depth buffer- A escala planetaria, al verlo desde lejos, al shader le puede costar distinguir la diferencia de profundidad entre la atmósfera y la superficie del planeta, lo que puede provocar depth fighting
- Como la altura de la atmósfera es de apenas unos km, hay que ajustar tanto la definición del depth buffer de la escena como la forma de leerlo en los efectos de postprocesado
- En la prop
glque envuelve elCanvasde React Three Fiber, se configuralogarithmicDepthBuffer: true - Un ejemplo de configuración es
<Canvas shadows gl={{ alpha: true, logarithmicDepthBuffer: true }}> - En el shader, se vuelve a definir el cálculo de
sceneDepthpara convertir el logarithmic depth buffer de nuevo en distancia a lo largo del rayo logDepthToViewZ(depth)usapow(2.0, depth * log2(cameraFar + 1.0)) - 1.0y devuelve-d
-
Encontrar el tramo atmosférico con
ray-sphere intersection- Se usa una prueba de intersección rayo-esfera para encontrar los puntos donde el rayo de visión entra y sale de la esfera atmosférica (atmospheric sphere)
- Al obtener dos puntos de intersección, se evita desperdiciar muestras fuera de la atmósfera y se puede limitar el loop de raymarching solo a ese tramo
- Como el planeta es una malla esférica rodeada por una esfera atmosférica un poco más grande, se aplica la misma prueba de intersección también al propio planeta
- Si el rayo toca el suelo antes de salir de la atmósfera, se usa el punto de intersección con el suelo como final del tramo de raymarching
- La implementación de
raySphereIntersectusada toma como referencia Ray-Surface intersection functions de Inigo Quilez
-
Objetos de la escena y condición de fin de la atmósfera
- La atmósfera debe terminar al tocar la superficie del planeta o al encontrarse con otro objeto de la escena antes de llegar al suelo
- Cuando toca el planeta, por defecto se detiene en el suelo con
atmosphereFar = min(atmosphereFar, planetHit.x) - Si otra malla se renderiza delante del suelo, se detecta con la condición
sceneDepth < planetHit.x - 2.0y se aplicaatmosphereFar = min(atmosphereFar, sceneDepth) - Sin esta lógica, aparece el problema de que la superficie del planeta se muestra por delante del objeto
-
Demo en React Three Fiber y glitches pendientes
- Al aplicar estos dos ajustes en el código, se puede implementar la dispersión atmosférica como un efecto de postprocesado y renderizar la atmósfera alrededor del planeta
- La escena de demo renderiza un sencillo “Sun - Earth system” en React Three Fiber y le aplica el efecto personalizado
- Si se ajusta la posición del sol y se aleja el zoom, se puede ver el color del cielo que genera el shader desde distintos ángulos, desde la superficie hasta la órbita
- El mismo efecto se usó en la imagen de póster para adelantar la publicación de principios de abril, y la imagen renderizada se compartió en un tuit
- El torus de la escena todavía puede verse “lit-up” incluso después del atardecer
- La causa es que el shadow-map o la escala de la shadow-camera de la directional light principal es pequeña y no alcanza a cubrir el torus, que está demasiado lejos
- Como solución alternativa, se podría reutilizar el enfoque de shadow mapping del artículo sobre volumetric lighting, aunque en la práctica no se intentó
Manejo de eclipses
- Cuando un cuerpo celeste grande tapa el sol, esto se puede agregar llamando a la función
sunVisibilitydespués delightMarchy multiplicando la transmitancia por el valor de retorno[0, 1] - La idea básica es comparar el producto punto entre la dirección hacia la luna y la dirección hacia el sol desde el punto de muestra actual
- Si ambas direcciones son casi iguales y el producto punto está cerca de
1.0, la luna está bloqueando el sol; si son ortogonales y está cerca de0.0, no hay bloqueo - Como un producto punto simple no puede reflejar el tamaño y la escala de los objetos dentro de la escena, la implementación compara la distancia angular entre el sol y la luna y el radio angular de cada uno
sunVisibilitymaneja los casos en que la luna no tapa el sol, en que lo tapa mientras se ve más grande o de tamaño similar al sol desde el punto de vista de la cámara, y en que lo tapa al entrar dentro del radio solar visto desde la cámara- La demo añade
sunVisibilityy una malla de luna sobre el ejemplo existente de dispersión atmosférica, para que el shader de Atmospheric Scattering maneje la situación de falta de luz cuando la luna se alinea con el sol - Una simulación más sofisticada de eclipses y corona se trata en el paper Physically Based Real-Time Rendering of Eclipses, cuya implementación no se llevó a WebGL
Atmósferas de otros planetas
- El modelo de densidad atmosférica y dispersión usado se determina en gran medida por unos cuantos valores constantes, como el radio del planeta y de la atmósfera,
RayleighScaleHeight,RayleighBeta,MieScaleHeight,MieBeta,mieBetaExt,mieG,OzoneHeightyOzoneWidth - Ajustando estos valores, se pueden lograr resultados más cercanos a la atmósfera marciana o a la atmósfera de otros planetas
- Los valores usados para Marte son aproximados
planetRadius: 3390atmosphereRadius: 3500, unos110 kmde espesorrayleighScaleHeight: 11.1rayleighBeta: new THREE.Vector3(0.019, 0.013, 0.0057)mieScaleHeight: 1.5mieBeta: 0.04mieBetaExt: 0.044mieG: 0.65ozoneCenterHeight: 0.0ozoneWidth: 1.0ozoneBetaAbs: new THREE.Vector3(0.0, 0.0, 0.0)sunIntensity: 15.0planetSurfaceColor: '#8B4513'
- Si se reemplazan las constantes existentes por estos valores, se obtiene una atmósfera más polvorienta y anaranjada, además del característico tono azulado al atardecer de Marte
- Un paper relacionado es Physically Based Rendering of the Martian Atmosphere
Dispersión atmosférica basada en LUT
-
Enfoque y atajos tomados
- El shader anterior puede renderizar de forma intuitiva atmósferas a pequeña y gran escala, pero su costo de ejecución es alto por el bucle de raymarching con muchos
PRIMARY_STEPS, el bucle anidado delightmarchingy el cálculo a resolución de pantalla completa - A Scalable and Production Ready Sky and Atmosphere Rendering Technique de Sebastian Hillaire propone un enfoque basado en Look Up Tables (LUTs) que guarda en texturas los cálculos de dispersión costosos y, en el render final, muestrea y compone texturas precalculadas
- Las LUT que se usan son la Transmittance LUT, que guarda cuánta luz sobrevive al atravesar la atmósfera; la Sky-view LUT, que guarda el color del cielo desde una posición específica de la cámara; y la Aerial Perspective LUT, que guarda la bruma atmosférica y la luz dispersada entre la cámara y la geometría visible de la escena
- No se trasladó tal cual toda la implementación del paper; aunque las LUT encajan bien con los compute shaders de WebGPU, por falta de tiempo y para mantener la continuidad del artículo se conservó WebGL
- En el paper, la Aerial Perspective LUT es una textura 3D, pero en esta implementación se usa un render target 2D
- Este enfoque requiere regenerar las texturas cada vez que la cámara se mueve para obtener los valores correctos por píxel, por lo que es difícil dejarlas precalculadas
- Multi-Scattering se omitió por falta de tiempo
- El shader anterior puede renderizar de forma intuitiva atmósferas a pequeña y gran escala, pero su costo de ejecución es alto por el bucle de raymarching con muchos
-
Transmittance LUT
- En el shader anterior, todos los puntos de muestreo llamaban a
lightmarchpara calcular cuánta luz solar llegaba, y ese proceso era costoso - La Transmittance LUT guarda previamente esos datos a baja resolución para que las LUT posteriores los lean cuando necesiten información de iluminación
- La implementación define un Frame Buffer Object dedicado de resolución
250 x 64, aplica un material de shader personalizado a un full-screen quad de la escena dedicadatransmittanceLUTSceney luego pasa la textura renderizada como uniform a las LUT downstream - En cada píxel se hace raymarching desde
vec3(0.0, radius, 0.0), donderadiusaumenta desdeplanetRadiushastaatmosphereRadiusa lo largo de la coordenadavUv.y - El eje x de la LUT representa el ángulo de la luz y el eje y representa la altitud; el blanco puro indica una transmitancia del
100%, mientras que las zonas negras o coloreadas representan el suelo o las partes donde el aire es más denso - Después, las LUT posteriores pueden obtener “la cantidad de luz que sobrevive al pasar por la atmósfera a un ángulo y altitud dados” solo con consultar la textura
- En el shader anterior, todos los puntos de muestreo llamaban a
-
Sky-view LUT
- La Sky-view LUT calcula de qué color se ve el cielo al mirar hacia arriba desde el suelo en una dirección específica
getSkyViewRayDirmapeavUv.xal azimuth[-PI, PI]yvUv.ya la elevation[-PI/2, PI/2]para definir la dirección del raymarching- Para la elevation se usa un mapeo cuadrático,
(vUv.y * vUv.y - 0.5) * PI, como solución alternativa para evitar que la vista del cielo parpadee demasiado a larga distancia - Si el rayo no entra en la atmósfera devuelve negro, y los rayos que tocan el planeta hacen raymarching solo hasta la parte visible de la atmósfera, deteniéndose antes cuando alcanzan el planeta
- El bucle de dispersión es igual al anterior, pero avanza a lo largo de la dirección de Sky View y usa la Transmittance LUT para la luz solar
-
Aerial Perspective LUT
- A diferencia del paper de Hillaire, el resultado de esta implementación es una textura 2D, y cada píxel corresponde a un píxel visible de la pantalla
- Usa el depth buffer de la escena para determinar qué tan lejos avanzar por ese rayo y acumular dispersión
- Reutiliza casi todo el código de dispersión anterior, pero cada muestra obtiene la visibilidad de la luz solar desde la Transmittance LUT
- La salida guarda en RGB la dispersión atmosférica acumulada y en alfa un valor empaquetado de view transmittance para usarlo durante la composición
- El flujo de implementación consiste en leer la profundidad desde
depthBuffer, reconstruir la posición en espacio mundial del píxel de pantalla congetWorldPosition(vUv, depth)y luego calcularrayDirdesde la posición de la cámara hasta la posición mundial - Después,
logDepthToRayDistance(vUv, depth)convierte la profundidad de la escena en distancia a lo largo del rayo; luego se calculan las intersecciones con la atmósfera y el planeta, y solo se hace march sobre el tramo visible de la atmósfera
-
Composición
- Después de generar la Sky-view LUT y la Aerial Perspective LUT, ambas se combinan en el último pass de post-processing
- La tarea principal es convertir el
rayDiractual en coordenadas UV de Sky View - A la geometría de la escena se le aplica la Aerial Perspective LUT, usando el canal alfa como view transmittance y el canal RGB como luz dispersada para calcular
color = color * aerialPerspective.a + aerialPerspective.rgb - Para los píxeles de fondo se muestrea la Sky View LUT; si
depth >= 1.0 - 1e-7, se considera fondo y se aplicacolor = inputColor.rgb + sampleSkyViewLUT(rayDir, planetCenter) - Por último, se aplican
ACESFilm(color)ypow(color, vec3(1.0 / 2.2)) - El código completo de esta implementación atmosférica basada en LUT puede consultarse en Github link
Cierre
- El resultado de la dispersión atmosférica basada en LUT puede verse casi igual que la versión anterior de raymarching completo, pero el proceso interno es distinto
- Divide el trabajo en LUT más pequeñas y las compone en el efecto final, sin repetir raymarching hacia el sol en cada muestra para calcular la luz incidente
- Como toma la información de iluminación directamente desde la Transmittance LUT, sustituye los costosos bucles anidados por simples consultas de textura y logra una mejora de rendimiento nada despreciable en la escena final
- La implementación queda corta en comparación con la de Sébastian Hillaire y otras de distintos campos; en particular, hay banding y flickering en Sky View, y por los atajos tomados no es especialmente óptima
- Puede que debí haber usado WebGPU desde el principio
- Como implementación real de nivel production-grade, se recomienda three-geospatial de Shoda Matsuda(@shotamatsuda)
- Además, también se trabajó en agregar volumetric clouds, pero el resultado sigue siendo mixed bag y todavía no convence lo suficiente como para mostrarlo en un artículo, así que necesita más trabajo
1 comentarios
Comentarios de Hacker News
Hay algo especialmente entretenido en ver cómo se desarrollan los efectos visuales y cómo poco a poco se implementan de forma cada vez más realista; algún día me gustaría experimentar directamente con este campo
Antes sus videos tenían millones de vistas, y ahora apenas pasan de 500 mil. También podría ser por la época del covid, cuando todos estábamos en casa y nos interesaban cosas aleatorias
Normalmente los dejo puestos al dormir, y como me gustaría que hubiera más contenido así, tranquilo pero profundo, sobre temas técnicos, alguna vez pensé en intentar hacerlo yo mismo
Incluso después de que se pone el sol, la atmósfera sobre tu cabeza y la zona sobre el horizonte siguen recibiendo luz solar por un tiempo, y en la atmósfera terrestre sigue habiendo un crepúsculo visible hasta que el sol desciende 18 grados por debajo del horizonte. Puede que no sea práctico implementarlo con trazado de rayos, pero sí existen algoritmos comunes para modelarlo
https://www.threads.com/@mrsharpoblunto/post/DVS4wfYiG8f?xmt...
https://www.threads.com/@mrsharpoblunto/post/C6Vc-S1O9mX?xmt...
https://www.threads.com/@mrsharpoblunto/post/C6apksDRa8q?xmt...
Recuerdo haber implementado “Display of The Earth Taking into Account Atmospheric Scattering” de Nishita et al., un paper de 1993 que es casi el origen de este tema y además muy fácil de leer: https://www.researchgate.net/publication/2933032_Display_of_...
Cuando logré hacerlo funcionar, tuve ese momento de “vaya, este fenómeno tan complejo del mundo real se puede modelar bastante bien con unos cuantos cálculos relativamente simples”. En un instante pasé de un simple skybox azul estático a un ciclo completo de día y noche
Hace tiempo pensé en intentar renderizar el cielo en la web superponiendo varios degradados. Tal vez hasta cierto punto habría podido sacar un resultado decente, pero no se compara con lo que hicieron aquí. El resultado es impresionante e inspirador
Me sorprendió ver que solo con eso ya salía un ciclo de atardecer/amanecer bastante convincente, y si mal no recuerdo, el propio sol también aparecía de ahí de forma natural de alguna manera. Usando XNA, la plataforma de desarrollo de juegos en C# de Microsoft, seguí la excelente serie de tutoriales de Riemer, y aquí está una copia preservada: https://github.com/SimonDarksideJ/XNAGameStudio/wiki/Riemers...
Aunque no veo ahí la parte sobre dispersión, así que puede que esa parte la haya tomado de otro lado. Sí recuerdo haber leído papers con fórmulas
https://spaceengine.org/
A la pregunta “¿Cuántos objetos hay en SpaceEngine?”, la respuesta dice algo como que suma las estrellas completas del catálogo Hipparcos, todos los exoplanetas conocidos, más de diez mil galaxias y la mayoría de los objetos del sistema solar para llegar a 130 mil, y además añade más galaxias y sistemas estelares de los que realmente existen en todo el universo observable. A “¿Cómo puede estar caliente un planeta oceánico?” responde que el agua en la atmósfera superior es vapor caliente, pero al bajar hace una transición suave a líquido bajo alta presión, y más abajo pasa a un estado sólido llamado ice VII. Y a “¿Cómo me muevo?” la respuesta es con las teclas WASD
Es un gran juego, y aunque ya tiene bastante tiempo, todavía no he visto nada tan bueno como esto
Este artículo también me hizo pensar en SpaceEngine
Uno de mis papers favoritos: http://www.graphics.stanford.edu/papers/bssrdf/bssrdf.pdf
Creo que fue ahí cuando aprendí por primera vez que renderizar leche es un problema complicado
Creo que solo entendí como un 5%, pero de verdad me impresionó muchísimo
Y además, si tiene licencia MIT, entonces el problema del skybox de mi juego prácticamente queda resuelto. Como la perspectiva será fija, solo necesitaría un render del sol cruzando el cielo, y a eso podría extenderle la variación anual del ángulo solar con un ciclo sinusoidal