Céu, pôr do sol e renderização de planetas
(blog.maximeheckel.com)- O shader no navegador combina espalhamento de Rayleigh, espalhamento de Mie e absorção de ozônio para renderizar em tempo real o céu azul e o pôr/nascer do sol
- Ele acumula a profundidade óptica do raio da câmera e a transmitância pela Lei de Beer, e calcula com funções de fase a distribuição do espalhamento conforme a direção do sol
- O efeito de pôr do sol realiza um light-march separado na direção do sol em cada amostra, refletindo quanto da luz solar é perdido ao atravessar a atmosfera
- O shader de céu plano se torna um efeito de pós-processamento com buffer de profundidade e reconstrução de coordenadas de mundo, tratando até a névoa atmosférica entre os objetos da cena
- Em escala planetária, ele se expande com logarithmic depth buffer, interseção raio-esfera e LUTs de Transmittance, Sky-view e Aerial Perspective
Objetivos do shader de espalhamento atmosférico e materiais de referência
- O objetivo é reproduzir em um shader no navegador, como na foto do pôr do sol em órbita baixa do ônibus espacial Endeavour, as camadas que passam do laranja escuro, azul e preto do fundo do espaço na alta atmosfera da Terra
- O escopo da implementação começa com um sky dome realista que combina raymarching, espalhamento de Rayleigh, espalhamento de Mie e absorção de ozônio, e depois se expande para a casca atmosférica ao redor de um planeta e otimizações baseadas em LUT
- As principais referências são Three Geospatial, A Scalable and Production Ready Sky and Atmosphere Rendering Technique de Sébastien Hillaire e Atmospheric Scattering (and also just faking it)
Modelo básico de renderização do céu
-
Por que um gradiente simples não basta
- A cor do céu não deve ser tratada como um simples fundo azul, mas como o resultado da interação da luz com o ar e seus componentes
- É preciso considerar variáveis como altitude do observador, quantidade de poeira e horário do dia, e os cálculos acontecem dentro de um volume
-
Amostragem da densidade atmosférica
- A atmosfera é amostrada com raymarching, como em volumetric clouds ou volumetric light
- Dispara-se um raio a partir da posição da câmera e ele avança pelo meio transparente, calculando a transmitância, que é a luz que sobrevive ao atravessar a atmosfera, e o espalhamento, que é redirecionado de cada amostra em direção à câmera
- Como revisão de raymarching, dá para consultar Painting with Math: A Gentle Study of Raymarching
-
Densidade de Rayleigh e profundidade óptica
- Para obter a transmitância, é preciso acumular a densidade atmosférica encontrada pelo raio ao longo do caminho e calcular a profundidade óptica
- A função de densidade de Rayleigh representa quanto “ar” existe na altitude
h, refletindo o efeito de a atmosfera ficar mais rarefeita quanto maior a altitude - A implementação de exemplo usa
RAYLEIGH_SCALE_HEIGHT = 8.0km,ATMOSPHERE_HEIGHT = 100.0km,VIEW_DISTANCE = 200.0km ePRIMARY_STEPS = 24 rayleighDensity(h)éexp(-max(h, 0.0) / RAYLEIGH_SCALE_HEIGHT), e no loop o acúmulo é feito comviewOpticalDepth += dR * stepSize
-
Lei de Beer e o azul do céu diurno
- A partir da profundidade óptica, calcula-se a transmitância
Tem um ponto específico;T=1.0significa nenhuma perda de luz eT=0.0significa que a luz desapareceu completamente - A transmitância é calculada pela Lei de Beer, e o código de exemplo usa
vec3 transmittance = exp(-rayleighBeta * viewOpticalDepth) rayleighBetaé o coeficiente de espalhamento de Rayleigh e, no shader, é armazenado comovec3(0.0058, 0.0135, 0.0331)- O ângulo entre a direção da luz solar e o raio de visão é modelado pela função de fase de Rayleigh na forma
3.0 / (16.0 * PI) * (1.0 + mu * mu) - Por causa do coeficiente de espalhamento de Rayleigh, o vermelho quase não é espalhado, o verde é espalhado um pouco mais e o azul é o mais espalhado, fazendo o céu diurno parecer azul
- Ao expandir isso para um raio por pixel, a região do horizonte atravessa mais atmosfera e parece uma névoa branca brilhante, enquanto em maiores altitudes a cor muda para um azul mais profundo e escuro
- A partir da profundidade óptica, calcula-se a transmitância
Espalhamento de Mie e absorção de ozônio
-
Efeitos que o Rayleigh sozinho não cobre
- Só com o espalhamento de Rayleigh já dá para obter bons resultados, mas um céu mais realista precisa de efeitos atmosféricos adicionais
- O espalhamento de Mie representa a interação da luz com partículas maiores, como poeira ou aerossóis, e tem uma função de densidade e uma função de fase que descreve a redistribuição por direção
- A absorção de ozônio remove do caminho alguns comprimentos de onda da luz que atravessa a alta atmosfera, sem espalhá-los
- A absorção de ozônio aprofunda a cor do céu e desloca os tons, especialmente no horizonte, no pôr do sol, no nascer do sol e no crepúsculo
-
Acúmulo de Mie e ozônio
- Em uma implementação que usa Rayleigh, Mie e ozônio juntos, as profundidades ópticas de cada um são acumuladas em
viewODR,viewODMeviewODO - Em cada amostra, calculam-se
dR = rayleighDensity(h),dM = mieDensity(h)edO = ozoneDensity(h), etaué formado pela soma deBETA_R * viewODR,BETA_M_EXT * viewODMeBETA_OZONE_ABS * viewODO - A transmitância é calculada com
exp(-tau), e densidade, transmitância estepSizesão acumulados emsumR,sumMesumO - O espalhamento final é calculado na forma
SUN_INTENSITY * (phaseR * BETA_R * sumR + phaseM * BETA_M_SCATTER * sumM + BETA_OZONE_SCATTER * sumO)
- Em uma implementação que usa Rayleigh, Mie e ozônio juntos, as profundidades ópticas de cada um são acumuladas em
-
Principais constantes e efeitos
MIE_SCALE_HEIGHTcorresponde, para aerossóis, aoRAYLEIGH_SCALE_HEIGHT; como as partículas normalmente se concentram perto do horizonte, ele é definido com um valor menor,1.2kmMIE_BETA_SCATTERcontrola quanto as partículas espalham a luz em direção à câmera e, por ser em grande parte independente do comprimento de onda, é definido comovec3(0.003)MIE_BETA_EXTé o coeficiente de extinção de Mie, que representa quanta luz é removida do caminho e faz a atmosfera distante parecer mais enevoadaMIE_Gcontrola a anisotropia:0.0significa espalhamento uniforme e1.0indica uma tendência mais forte ao espalhamento para frenteOZONE_BETA_ABStem os valoresvec3(0.00065, 0.00188, 0.00008)e absorve mais as faixas de verde e amarelo-alaranjado, deslocando a cor do céu em direção a azul, vermelho e roxo- Ao integrar Mie e ozônio, surgem uma cor “sky blue” mais natural e uma auréola difusa ao redor do sol, e o efeito do espalhamento de Mie fica mais evidente quando o sol está perto do horizonte
Caminho da luz e pôr do sol·nascer do sol
-
Limites da implementação existente
- O fragment shader do céu consegue renderizar cores naturais em várias altitudes e refletir os modelos de transmitância de Mie, Rayleigh e ozônio
- Porém, mesmo ao mover o sol para perto do horizonte, só aparece um halo branco e difuso, sem atenuação da luz nem efeitos de pôr do sol ou nascer do sol
- Isso acontecia porque o loop de raymarching existente calculava a atenuação da luz apenas no raio de visão da câmera até cada amostra
- Também é preciso calcular quanto da luz solar se perde ao atravessar a atmosfera antes de chegar ao ponto de amostragem
-
Loop aninhado de light-march
- Em cada ponto de amostragem, é executado um loop aninhado separado na direção da fonte de luz para amostrar a transmitância desse caminho
- Essa abordagem relacionada também é usada em real-time cloudscapes e volumetric lighting
lightMarch(float start, float sunY)repeteLIGHTMARCH_STEPSvezes enquanto acumulaodR,odM,odO- À profundidade óptica da implementação existente
viewODR,viewODM,viewODOé adicionada a profundidade óptica na direção do solsunOD - O
taufinal é composto pela soma deBETA_R * (viewODR + sunOD.x),BETA_M_EXT * (viewODM + sunOD.y),BETA_OZONE_ABS * (viewODO + sunOD.z) - Com essa implementação, é possível renderizar céus com pôr do sol, nascer do sol, sol no zênite e condições de iluminação intermediárias
- Com o uniform
sun angle, dá para criar a variação do azul do céu ao longo do dia, e o espalhamento de Mie mistura naturalmente a luz com o horizonte no pôr do sol e no nascer do sol - Quando o sol está baixo, o ozônio adiciona tons arroxeados ao céu
Expansão para atmosfera planetária
-
De fundo plano para efeito de pós-processamento
- O shader criado antes fornece um bom fundo de céu, mas ainda se parece com um fundo plano em uma cena de React Three Fiber
- O próximo passo é transformá-lo em um efeito de pós-processamento (post-processing effect) para renderizar um volume que considera a profundidade da cena e uma casca atmosférica ao redor da malha do planeta
- Para isso, reconstrói-se a coordenada em espaço mundial a partir de
screenUVe incorpora-se o depth buffer da cena ao raymarching
-
Reconstrução em espaço mundial e raio 3D
- Para aplicar o espalhamento atmosférico à cena, não basta desenhar apenas o céu; é preciso preencher o espaço entre a câmera e os objetos renderizados na tela
- Os dados necessários são o depth buffer da cena,
projectionMatrixInverse,matrixWorldepositionda câmera, e esses valores são passados como uniforms do efeito de pós-processamento getWorldPosition(vec2 uv, float depth)criaclipZcomdepth * 2.0 - 1.0e coordenadas NDC comuv * 2.0 - 1.0, depois aplicaprojectionMatrixInverseeviewMatrixInverse- O mesmo processo também é usado no efeito de pós-processamento de volumetric lighting de On Shaping Light
- Depois de obter o
worldPositiondo pixel atual,rayOriginé definido como a posição da câmera erayDiré calculado comnormalize(worldPosition - rayOrigin), avançando ao longo de um raio 3D por pixel na tela
-
Ajuste do intervalo de raymarch com o depth buffer
- Para considerar a geometria da cena, o intervalo de raymarch do raio atual deve ser definido com o depth buffer em vez de um
stepSizefixo - A profundidade da cena ao longo do raio é obtida com
sceneDepth = depthToRayDistance(uv, depth) - Pixels de fundo são identificados com
depth >= 1.0 - 1e-7, e para “sky pixels” aplica-sesceneDepth = atmosphereHeight * SKY_MARCH_DISTANCE_MULTIPLIER - Se o raio aponta para baixo, a interseção com o solo é calculada com
tGround = observerAltitude / max(-rayDir.y, 1e-4)e limitada comrayEnd = min(rayEnd, tGround) - O
stepSizefinal é calculado como(rayEnd - rayStart) / float(PRIMARY_STEPS) - Raios que atingem objetos próximos ou o solo usam
stepSizemenor para amostragem mais precisa, enquanto raios que vão mais longe distribuem o mesmo número de amostras por uma distância maior
- Para considerar a geometria da cena, o intervalo de raymarch do raio atual deve ser definido com o depth buffer em vez de um
-
Névoa atmosférica dentro da cena
- O shader implementado como efeito de pós-processamento aplica o espalhamento atmosférico a todo o volume da cena e permite usar o sky shader como fundo, considerando a geometria da cena
- Objetos próximos da câmera aparecem mais nítidos, enquanto objetos distantes ficam mais desfocados
- Um exemplo interativo com corpos celestes arrastáveis usando
Raycasterpode ser visto no tweet de MaximeHeckel
Renderização de planetas
-
Duas etapas necessárias
- Para renderizar uma atmosfera realista ao redor de um planeta, são necessários um logarithmic depth buffer para lidar com escalas grandes e uma casca atmosférica esférica que define onde o raio começa e termina na atmosfera
-
logarithmic depth buffer
- Em escala planetária, ao observar de longe, o shader pode ter dificuldade para distinguir a diferença de profundidade entre a atmosfera e a casca do planeta, o que pode causar depth fighting
- Como a altura da atmosfera é de apenas alguns km, é preciso ajustar tanto a definição do depth buffer da cena quanto a forma de leitura nos efeitos de pós-processamento
- Em React Three Fiber, define-se
logarithmicDepthBuffer: truena propglque envolve oCanvas - Um exemplo de configuração é
<Canvas shadows gl={{ alpha: true, logarithmicDepthBuffer: true }}> - No shader, a computação de
sceneDepthé redefinida para converter o logarithmic depth buffer de volta para a distância ao longo do raio logDepthToViewZ(depth)usapow(2.0, depth * log2(cameraFar + 1.0)) - 1.0e retorna-d
-
Encontrando o trecho da atmosfera com ray-sphere intersection
- Usa-se um teste de ray-sphere intersection para encontrar os pontos em que o raio de visão entra e sai da esfera atmosférica (atmospheric sphere)
- Ao obter os dois pontos de interseção, evita-se desperdiçar amostras fora da atmosfera e o loop de raymarching pode ser limitado apenas a esse intervalo
- Como o planeta é uma malha esférica envolvida por uma esfera atmosférica um pouco maior, o mesmo teste de interseção também é aplicado ao próprio planeta
- Se o raio atingir o solo antes de sair da atmosfera, o ponto de interseção com o solo é usado como fim do intervalo de raymarching
- A implementação de
raySphereIntersectusada toma como referência Ray-Surface intersection functions de Inigo Quilez
-
Objetos da cena e condição de término da atmosfera
- A atmosfera deve terminar ao tocar a superfície do planeta ou ao encontrar outro objeto da cena antes de atingir o solo
- Ao tocar o planeta, por padrão ela para no solo com
atmosphereFar = min(atmosphereFar, planetHit.x) - Se outra malha estiver renderizada antes do solo, isso é detectado pela condição
sceneDepth < planetHit.x - 2.0e aplica-seatmosphereFar = min(atmosphereFar, sceneDepth) - Sem essa lógica, surge o problema de a superfície do planeta aparecer à frente do objeto
-
Demo em React Three Fiber e glitches restantes
- Ao aplicar esses dois ajustes no código, é possível implementar o espalhamento atmosférico como efeito de pós-processamento e renderizar a atmosfera ao redor do planeta
- A cena de demonstração renderiza um simples “Sun - Earth system” em React Three Fiber e aplica um efeito customizado
- Ao ajustar a posição do sol e afastar o zoom, é possível ver as cores do céu geradas pelo shader de vários ângulos, do solo até a órbita
- O mesmo efeito foi usado na imagem de pôster para divulgar o post do início de abril, e a imagem renderizada foi compartilhada em um tweet
- O torus da cena ainda pode parecer “lit-up” mesmo depois do pôr do sol
- A causa é que a escala do shadow-map ou da shadow-camera da directional light principal é pequena, então ela não consegue cobrir o torus que está longe demais
- Como contorno, seria possível reutilizar a abordagem de shadow-mapping do artigo sobre iluminação volumétrica, mas isso não foi realmente testado
Tratamento de eclipse
- Quando um grande corpo celeste bloqueia o sol, isso pode ser adicionado chamando a função
sunVisibilitydepois delightMarche multiplicando a transmitância pelo valor de retorno[0, 1] - A ideia básica é comparar, no ponto de amostragem atual, o produto escalar entre a direção da lua e a direção do sol
- Se as duas direções forem quase iguais e o produto escalar estiver próximo de
1.0, a lua está bloqueando o sol; se forem ortogonais e estiver próximo de0.0, não há bloqueio - Como um simples produto escalar não reflete o tamanho e a escala dos objetos na cena, a implementação compara a distância angular entre sol e lua e o raio angular de cada um
sunVisibilitytrata os casos em que a lua não bloqueia o sol, em que bloqueia enquanto parece maior ou de tamanho semelhante ao sol do ponto de vista da câmera, e em que bloqueia ao entrar dentro do raio do sol do ponto de vista da câmera- A demo adiciona
sunVisibilitye uma malha da lua ao exemplo existente de espalhamento atmosférico, para que o shader de Atmospheric Scattering lide com a situação de pouca luz quando a lua é alinhada com o sol - Simulações mais sofisticadas de eclipse e corona são tratadas no artigo Physically Based Real-Time Rendering of Eclipses, cuja implementação não foi portada para WebGL
Atmosfera de outros planetas
- O modelo de densidade atmosférica e espalhamento usado é determinado em grande parte pelo raio do planeta e da atmosfera, além de algumas constantes como
RayleighScaleHeight,RayleighBeta,MieScaleHeight,MieBeta,mieBetaExt,mieG,OzoneHeighteOzoneWidth - Ajustando esses valores, é possível obter resultados próximos da atmosfera de Marte ou da atmosfera de outros planetas
- Os valores usados para Marte são aproximados
planetRadius: 3390atmosphereRadius: 3500, espessura de cerca de110 kmrayleighScaleHeight: 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'
- Ao substituir as constantes existentes por esses valores, obtém-se uma atmosfera mais empoeirada e alaranjada, além do característico tom azulado no pôr do sol de Marte
- Há também o artigo relacionado Physically Based Rendering of the Martian Atmosphere
Dispersão atmosférica baseada em LUT
-
Abordagem e simplificações
- O shader anterior consegue renderizar a atmosfera em pequena e grande escala de forma intuitiva, mas tem custo alto de execução por causa do loop de raymarching com muitos
PRIMARY_STEPS, do loop aninhado delightmarchinge do cálculo em resolução de tela inteira - A Scalable and Production Ready Sky and Atmosphere Rendering Technique, de Sebastian Hillaire, propõe uma abordagem baseada em Look Up Tables (LUTs) que armazena em texturas os cálculos de dispersão mais custosos e, no render final, faz amostragem e composição dessas texturas pré-calculadas
- As LUTs tratadas aqui são a Transmittance LUT, que armazena a quantidade de luz que sobrevive ao atravessar a atmosfera; a Sky-view LUT, que armazena a cor do céu em uma posição específica da câmera; e a Aerial Perspective LUT, que armazena o haze atmosférico e a luz dispersa entre a câmera e a geometria visível da cena
- A implementação não reproduz o artigo inteiro exatamente; embora LUTs combinem bem com compute shaders no WebGPU, por falta de tempo e para manter a continuidade do texto, foi mantido o WebGL
- No artigo, a Aerial Perspective LUT é uma 3D texture, mas nesta implementação foi usado um render target 2D
- Essa abordagem exige recriar as texturas sempre que a câmera se move para garantir valores corretos por pixel, o que dificulta pré-cálculo
- Multi-Scattering foi omitido por falta de tempo
- O shader anterior consegue renderizar a atmosfera em pequena e grande escala de forma intuitiva, mas tem custo alto de execução por causa do loop de raymarching com muitos
-
Transmittance LUT
- No shader anterior, todos os pontos de amostragem chamavam
lightmarchpara calcular quanta luz solar chegava até eles, e esse processo era caro - A Transmittance LUT armazena esses dados previamente em baixa resolução, para que as LUTs seguintes possam ler essas informações de luz quando precisarem
- A implementação define um Frame Buffer Object dedicado com resolução
250 x 64, aplica um material de shader customizado a um full-screen quad da cena dedicadatransmittanceLUTScenee depois passa a textura renderizada como uniform para as LUTs downstream - Em cada pixel, o raymarching parte de
vec3(0.0, radius, 0.0), em queradiusaumenta deplanetRadiusatéatmosphereRadiusao longo da coordenadavUv.y - O eixo x da LUT representa o ângulo da luz, e o eixo y representa a altitude; branco puro significa transmitância de
100%, enquanto áreas pretas ou coloridas indicam o solo ou as partes mais densas da atmosfera - Depois disso, as LUTs seguintes podem obter “a quantidade de luz que sobrevive ao atravessar a atmosfera em um dado ângulo e altitude” com apenas uma consulta de textura
- No shader anterior, todos os pontos de amostragem chamavam
-
Sky-view LUT
- A Sky-view LUT calcula qual cor o céu tem ao olhar do solo em uma direção específica
getSkyViewRayDirmapeiavUv.xpara o azimute[-PI, PI]evUv.ypara a elevação[-PI/2, PI/2], definindo assim a direção do raymarching- Para a elevação, é usado um mapeamento quadrático
(vUv.y * vUv.y - 0.5) * PI, como solução alternativa para evitar cintilação excessiva na Sky View a longas distâncias - Se o raio não entrar na atmosfera, retorna preto; para raios que atingem o planeta, o raymarching percorre apenas o trecho visível da atmosfera e para mais cedo ao tocar o planeta
- O loop de dispersão é igual ao anterior, mas avança na direção da Sky View e usa a Transmittance LUT para a luz solar
-
Aerial Perspective LUT
- Diferentemente do artigo de Hillaire, o resultado desta implementação é uma textura 2D, e cada pixel corresponde a um pixel visível na tela
- O depth buffer da cena é usado para determinar até que distância marchar ao longo do raio e acumular dispersão
- Quase todo o código de dispersão anterior é reaproveitado, mas cada amostra obtém da Transmittance LUT a visibilidade da luz solar
- A saída armazena a dispersão atmosférica acumulada em RGB e, no alfa, um valor packed de view transmittance usado na composição
- O fluxo da implementação consiste em ler a profundidade de
depthBuffer, reconstruir a posição em espaço de mundo do pixel da tela comgetWorldPosition(vUv, depth)e então calcularrayDirda posição da câmera até a posição em mundo - Em seguida,
logDepthToRayDistance(vUv, depth)converte a profundidade da cena em distância ao longo do raio; depois são calculadas as interseções com a atmosfera e o planeta, e só o trecho visível da atmosfera é percorrido
-
Composição
- Depois de gerar a Sky-view LUT e a Aerial Perspective LUT, ambas são combinadas no pass final de pós-processamento
- O trabalho principal é converter o
rayDiratual em coordenadas UV da Sky View - Na geometria da cena é aplicada a Aerial Perspective LUT; o canal alfa é usado como view transmittance, e o canal RGB como luz dispersa, calculando
color = color * aerialPerspective.a + aerialPerspective.rgb - Para pixels de fundo, é feita a amostragem da Sky View LUT; se
depth >= 1.0 - 1e-7, ele é tratado como fundo e aplica-secolor = inputColor.rgb + sampleSkyViewLUT(rayDir, planetCenter) - Por fim, são aplicados
ACESFilm(color)epow(color, vec3(1.0 / 2.2)) - O código completo da implementação atmosférica baseada em LUT pode ser visto em Github link
Encerramento
- O resultado da dispersão atmosférica baseada em LUT pode parecer quase igual à versão anterior com raymarching completo, mas o processo interno é diferente
- O trabalho é dividido em LUTs menores e depois composto no efeito final, sem repetir raymarching em direção ao sol para calcular a luz incidente em cada amostra
- Como a informação de iluminação vem diretamente da Transmittance LUT, os loops aninhados caros são substituídos por consultas simples de textura, trazendo um ganho de desempenho relevante na cena final
- A implementação fica aquém da de Sébastian Hillaire e de outras implementações da área; em especial, há banding e flickering na Sky View, e as simplificações adotadas prejudicam a otimização
- É possível que o ideal tivesse sido usar WebGPU desde o começo
- Como implementação realmente production-grade, a recomendação é three-geospatial, de Shoda Matsuda(@shotamatsuda)
- Também foi feito um trabalho adicional para adicionar volumetric clouds, mas o resultado ainda é irregular e não está satisfatório o bastante para mostrar em um texto, então precisa de mais trabalho
1 comentários
Comentários do Hacker News
Há algo especialmente divertido em desenvolver efeitos visuais e vê-los se tornar cada vez mais realistas, e um dia eu gostaria de experimentar essa área por conta própria
Antes os vídeos dele tinham milhões de visualizações, agora mal passam de 500 mil. Pode ser efeito da pandemia, quando todo mundo estava em casa e se interessava por coisas aleatórias
Normalmente deixo passando para dormir, e já pensei em tentar fazer eu mesmo mais conteúdo assim, calmo mas aprofundado em temas técnicos
Por algum tempo depois do pôr do sol, a atmosfera acima de você e a região sobre o horizonte ainda continuam recebendo luz solar, e na atmosfera da Terra ainda há crepúsculo perceptível até o Sol chegar a 18 graus abaixo do horizonte. Talvez não seja prático implementar isso com ray tracing, mas existem algoritmos comuns para modelar isso
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...
Lembro de ter implementado “Display of The Earth Taking into Account Atmospheric Scattering”, de Nishita et al., um artigo de 1993 e praticamente um dos trabalhos fundadores do tema, além de ser bem fácil de ler: https://www.researchgate.net/publication/2933032_Display_of_...
Quando consegui fazer funcionar, tive aquele momento de “dá para modelar esse fenômeno complexo do mundo real muito bem com só alguns cálculos relativamente simples”. Saí de um céu azul estático para um ciclo completo de dia e noite num instante
Já pensei em tentar renderizar um céu na web sobrepondo vários gradientes. Talvez eu até tivesse conseguido um resultado razoável, mas não chega nem perto do que foi feito aqui. O resultado é impressionante e inspirador
Só isso já produzia um ciclo de pôr do sol/nascer do sol bem convincente, e, se não me engano, o próprio sol meio que surgia naturalmente dali também. Eu usava o XNA, a plataforma da Microsoft para desenvolvimento de jogos em C#, seguindo a excelente série de tutoriais do Riemer, cuja cópia preservada está aqui https://github.com/SimonDarksideJ/XNAGameStudio/wiki/Riemers...
Mas não estou vendo a parte sobre espalhamento, então talvez eu tenha tirado isso de outro lugar. Ainda lembro de ter lido artigos com fórmulas
https://spaceengine.org/
A resposta para “Quantos objetos existem no SpaceEngine?” é algo como: todo o catálogo estelar Hipparcos, todos os exoplanetas conhecidos, mais de dez mil galáxias e a maior parte dos objetos do Sistema Solar, somando 130 mil, além de mais galáxias e sistemas estelares do que realmente existem em todo o universo observável. Para “Como um planeta oceânico pode ser quente?”, a resposta é que a água na alta atmosfera é vapor quente, mas ao descer faz uma transição suave para líquido sob alta pressão, e mais fundo se torna um estado sólido chamado ice VII. E a resposta para “Como se move?” é as teclas WASD
É um jogo excelente e, mesmo sendo bem antigo, ainda não vi nada tão bom quanto ele
Ao ler este texto, eu também pensei em SpaceEngine
Um dos meus artigos favoritos: http://www.graphics.stanford.edu/papers/bssrdf/bssrdf.pdf
Acho que foi aí que aprendi pela primeira vez que renderizar leite é um problema complicado
Acho que entendi só uns 5%, mas fiquei realmente muito impressionado
E sendo licença MIT, então meu problema de skybox no jogo está resolvido. Como a perspectiva vai ficar fixa, eu só preciso de uma renderização do sol atravessando o céu, e dá para expandir isso com uma periodicidade senoidal para a variação anual do ângulo solar