하늘, 일몰, 행성 렌더링
(blog.maximeheckel.com)- 브라우저 셰이더가 Rayleigh 산란, Mie 산란, 오존 흡수를 조합해 파란 하늘과 일몰·일출을 실시간 렌더링함
- 카메라 광선의 광학 깊이와 Beer's Law 투과율을 누적하고, 위상 함수로 태양 방향에 따른 산란 분포를 계산함
- 일몰 효과는 각 샘플에서 태양 방향으로 별도 light-march를 수행해, 태양빛이 대기를 통과하며 잃는 양을 반영함
- 평면 하늘 셰이더는 depth buffer와 월드 좌표 복원으로 후처리 효과가 되어, 장면 오브젝트 사이 대기 안개까지 처리함
- 행성 규모에서는 logarithmic depth buffer, ray-sphere intersection, LUT 기반 Transmittance·Sky-view·Aerial Perspective로 확장됨
대기 산란 셰이더의 목표와 참고 자료
- 우주왕복선 Endeavour의 저궤도 일몰 사진처럼, 지구 상층 대기의 어두운 주황색·파란색·우주 배경의 검정색으로 이어지는 층을 브라우저 셰이더로 재현하는 것이 목표임
- 구현 범위는 raymarching, Rayleigh 산란, Mie 산란, 오존 흡수를 조합한 사실적인 sky dome에서 시작해, 행성 주변 대기 껍질과 LUT 기반 최적화로 확장됨
- 주요 참고 자료는 Three Geospatial, Sébastien Hillaire의 A Scalable and Production Ready Sky and Atmosphere Rendering Technique, Atmospheric Scattering (and also just faking it)임
하늘 렌더링의 기본 모델
-
단순 그라디언트가 부족한 이유
- 하늘색은 단순한 파란 배경이 아니라, 빛이 공기와 그 구성 요소와 상호작용한 결과로 다뤄야 함
- 관측자의 고도, 먼지의 양, 시간대 같은 변수를 고려해야 하며, 계산은 부피(volume) 안에서 이뤄짐
-
대기 밀도 샘플링
- 대기는 volumetric clouds나 volumetric light처럼 raymarching으로 샘플링함
- 카메라 위치에서 광선을 쏘고 투명한 매질을 따라 전진하면서, 대기를 통과하며 살아남는 빛인 투과율(transmittance) 과 각 샘플에서 카메라 쪽으로 재지향되는 산란(scattering) 을 계산함
- raymarching 복습 자료로 Painting with Math: A Gentle Study of Raymarching를 참고할 수 있음
-
Rayleigh 밀도와 광학 깊이
- 투과율을 구하려면 광선이 지나며 만나는 대기 밀도를 누적해 광학 깊이(optical depth) 를 계산해야 함
- Rayleigh 밀도 함수는 고도
h에서 “공기”가 얼마나 있는지를 나타내며, 고도가 높아질수록 대기가 희박해지는 효과를 반영함 - 예시 구현은
RAYLEIGH_SCALE_HEIGHT = 8.0km,ATMOSPHERE_HEIGHT = 100.0km,VIEW_DISTANCE = 200.0km,PRIMARY_STEPS = 24를 사용함 rayleighDensity(h)는exp(-max(h, 0.0) / RAYLEIGH_SCALE_HEIGHT)이고, 루프에서viewOpticalDepth += dR * stepSize로 누적됨
-
Beer's Law와 낮 하늘의 파란색
- 광학 깊이에서 특정 지점의 투과율
T를 계산하며,T=1.0은 빛 손실 없음,T=0.0은 빛이 완전히 사라졌다는 뜻임 - 투과율은 Beer's Law로 계산되며, 예시 코드는
vec3 transmittance = exp(-rayleighBeta * viewOpticalDepth)를 사용함 rayleighBeta는 Rayleigh 산란 계수이며, 셰이더에서는vec3(0.0058, 0.0135, 0.0331)로 저장됨- 태양광 방향과 시선 광선 사이의 각도는
3.0 / (16.0 * PI) * (1.0 + mu * mu)형태의 Rayleigh phase function으로 모델링됨 - Rayleigh 산란 계수 때문에 빨강은 거의 산란되지 않고, 초록은 조금 더 산란되며, 파랑이 가장 많이 산란되어 낮의 하늘이 파랗게 보임
- 픽셀마다 하나의 광선으로 확장하면 지평선 쪽은 더 많은 대기를 통과해 밝은 흰 안개처럼 보이고, 고도가 높아질수록 더 깊고 어두운 파란색으로 바뀜
- 광학 깊이에서 특정 지점의 투과율
Mie 산란과 오존 흡수
-
Rayleigh만으로 부족한 효과
- Rayleigh 산란만으로도 괜찮은 결과를 얻을 수 있지만, 더 현실적인 하늘에는 추가 대기 효과가 필요함
- Mie 산란은 먼지나 에어로졸처럼 더 큰 입자와 빛의 상호작용을 나타내며, 밀도 함수와 방향별 재분배를 나타내는 위상 함수를 가짐
- 오존 흡수는 상층 대기를 통과하는 빛의 일부 파장을 산란시키지 않고 경로에서 제거함
- 오존 흡수는 특히 지평선, 일몰, 일출 전후 박명에서 하늘색을 더 깊게 만들고 색을 이동시킴
-
Mie와 오존의 누적
- Rayleigh, Mie, 오존을 함께 쓰는 구현은
viewODR,viewODM,viewODO로 각각의 광학 깊이를 누적함 - 각 샘플에서는
dR = rayleighDensity(h),dM = mieDensity(h),dO = ozoneDensity(h)를 계산하고,tau를BETA_R * viewODR,BETA_M_EXT * viewODM,BETA_OZONE_ABS * viewODO의 합으로 구성함 - 투과율은
exp(-tau)로 계산되고,sumR,sumM,sumO에 각 밀도와 투과율,stepSize가 누적됨 - 최종 산란은
SUN_INTENSITY * (phaseR * BETA_R * sumR + phaseM * BETA_M_SCATTER * sumM + BETA_OZONE_SCATTER * sumO)형태로 계산됨
- Rayleigh, Mie, 오존을 함께 쓰는 구현은
-
주요 상수와 효과
MIE_SCALE_HEIGHT는 에어로졸용RAYLEIGH_SCALE_HEIGHT에 해당하며, 입자가 보통 지평선 가까이에 집중되므로1.2km로 더 작게 설정됨MIE_BETA_SCATTER는 입자가 빛을 카메라 쪽으로 얼마나 산란시키는지 제어하며, 대부분 파장 독립적이어서vec3(0.003)으로 설정됨MIE_BETA_EXT는 경로에서 얼마나 많은 빛이 제거되는지 나타내는 Mie 소멸 계수이며, 먼 대기를 더 뿌옇게 보이게 만듦MIE_G는 이방성을 제어하며,0.0은 균일 산란,1.0은 더 강한 전방 산란 편향을 뜻함OZONE_BETA_ABS는vec3(0.00065, 0.00188, 0.00008)값을 가지며, 초록과 노랑-주황 계열을 더 많이 흡수해 하늘색을 파랑·빨강·보라 쪽으로 이동시킴- Mie와 오존을 통합하면 더 자연스러운 “sky blue” 색과 태양 주변의 뿌연 빛무리가 생기며, 태양이 지평선 가까이에 있을 때 Mie 산란 효과가 더 뚜렷해짐
빛 경로와 일몰·일출
-
기존 구현의 한계
- sky fragment shader는 다양한 고도에서 자연스러운 색을 렌더링하고 Mie, Rayleigh, 오존 투과율 모델을 반영할 수 있음
- 그러나 태양을 지평선 가까이 옮겨도 빛 감쇠나 일몰·일출 효과 없이 흰색의 뿌연 빛무리만 나타남
- 기존 raymarching 루프가 카메라에서 각 샘플까지의 시선 광선에서만 빛 감쇠를 계산했기 때문임
- 샘플 지점에 도달하기 전, 태양광이 대기를 통과하며 얼마나 손실되는지도 계산해야 함
-
light-march 중첩 루프
- 각 샘플 지점에서 광원 방향으로 별도 중첩 루프를 돌려 해당 경로의 투과율을 샘플링함
- 관련 접근은 real-time cloudscapes와 volumetric lighting에서도 사용됨
lightMarch(float start, float sunY)는LIGHTMARCH_STEPS만큼 반복하면서odR,odM,odO를 누적함- 기존 구현의 광학 깊이
viewODR,viewODM,viewODO에 태양 방향 광학 깊이sunOD를 더함 - 최종
tau는BETA_R * (viewODR + sunOD.x),BETA_M_EXT * (viewODM + sunOD.y),BETA_OZONE_ABS * (viewODO + sunOD.z)를 합쳐 구성됨 - 이 구현으로 일몰, 일출, 천정의 태양, 그 사이 조명 조건의 하늘을 렌더링할 수 있음
sun angleuniform으로 하루 동안의 하늘 파란색 변화를 만들고, Mie 산란은 일몰과 일출에서 빛을 지평선과 자연스럽게 섞어줌- 태양이 낮을 때 오존은 하늘에 보랏빛 톤을 더함
행성 대기로 확장
-
평면 배경에서 후처리 효과로
- 앞서 만든 셰이더는 좋은 하늘 배경을 제공하지만, React Three Fiber 장면의 평면 배경에 가까움
- 다음 단계는 이를 후처리 효과(post-processing effect) 로 바꿔 장면 깊이를 고려하는 부피와 행성 메시에 둘러싼 대기 껍질로 렌더링하는 것임
- 이를 위해
screenUV좌표에서 월드 공간 좌표를 재구성하고, 장면의 depth buffer를 raymarching에 반영함
-
월드 공간 재구성과 3D 광선
- 대기 산란을 장면에 적용하려면 하늘만 그리는 것이 아니라 카메라와 화면에 렌더링된 오브젝트 사이의 공간을 채워야 함
- 필요한 데이터는 장면의 depth buffer, 카메라의
projectionMatrixInverse,matrixWorld,position이며, 이 값들을 후처리 효과의 uniform으로 전달함 getWorldPosition(vec2 uv, float depth)는depth * 2.0 - 1.0으로clipZ를 만들고,uv * 2.0 - 1.0로 NDC 좌표를 만든 뒤projectionMatrixInverse와viewMatrixInverse를 적용함- 같은 과정은 On Shaping Light의 volumetric lighting 후처리 효과에도 사용됨
- 현재 픽셀의
worldPosition을 얻은 뒤rayOrigin은 카메라 위치로,rayDir은normalize(worldPosition - rayOrigin)으로 계산해 화면상의 픽셀별 3D 광선을 따라 전진함
-
깊이 버퍼로 raymarch 구간 조정
- 장면 지오메트리를 고려하려면 고정
stepSize대신 depth buffer로 현재 광선의 raymarch 구간을 정해야 함 sceneDepth = depthToRayDistance(uv, depth)로 광선상의 장면 깊이를 구함- 배경 픽셀은
depth >= 1.0 - 1e-7로 판별하고, “sky pixels”에는sceneDepth = atmosphereHeight * SKY_MARCH_DISTANCE_MULTIPLIER를 적용함 - 광선이 아래쪽을 향하면
tGround = observerAltitude / max(-rayDir.y, 1e-4)로 지면 교차를 계산하고rayEnd = min(rayEnd, tGround)로 제한함 - 최종
stepSize는(rayEnd - rayStart) / float(PRIMARY_STEPS)로 계산함 - 가까운 오브젝트나 지면에 닿는 광선은 작은
stepSize로 더 정확히 샘플링되고, 멀리 가는 광선은 같은 수의 샘플을 더 긴 거리 위에 분포시킴
- 장면 지오메트리를 고려하려면 고정
-
장면 안 대기 안개
- 후처리 효과로 구현된 셰이더는 장면의 부피 전체에 대기 산란을 적용하고, 장면 지오메트리를 고려하면서 sky shader를 배경으로 사용할 수 있음
- 카메라에 가까운 오브젝트는 더 선명하게 보이고, 멀리 있는 오브젝트는 더 많이 흐려짐
Raycaster로 드래그 가능한 천체를 넣은 상호작용 예시는 MaximeHeckel의 트윗에서 확인 가능함
행성 렌더링
-
필요한 두 단계
- 행성 주변에 사실적인 대기를 렌더링하려면 큰 스케일을 처리하기 위한 logarithmic depth buffer와, 광선이 대기에서 어디서 시작하고 끝나는지 정의하는 구 형태의 대기 껍질이 필요함
-
logarithmic depth buffer
- 행성 규모에서는 멀리서 볼 때 대기와 행성 껍질의 깊이 차이를 셰이더가 구분하기 어려워 depth fighting이 발생할 수 있음
- 대기 높이는 몇 km에 불과하므로, 장면의 depth buffer 정의와 후처리 효과에서의 읽기 방식을 모두 조정해야 함
- React Three Fiber의
Canvas를 감싸는glprop에서logarithmicDepthBuffer: true를 설정함 - 예시 설정은
<Canvas shadows gl={{ alpha: true, logarithmicDepthBuffer: true }}>형태임 - 셰이더에서는 logarithmic depth buffer를 광선상의 거리로 되돌리기 위해
sceneDepth계산을 다시 정의함 logDepthToViewZ(depth)는pow(2.0, depth * log2(cameraFar + 1.0)) - 1.0를 사용하고-d를 반환함
-
ray-sphere intersection으로 대기 구간 찾기
- 시선 광선이 대기 구(atmospheric sphere) 에 들어가고 나오는 지점을 찾기 위해 ray-sphere intersection test를 사용함
- 두 교차점을 얻으면 대기 밖에서 샘플을 낭비하지 않고, 해당 구간으로만 raymarching 루프를 제한할 수 있음
- 행성은 구형 메시이고 그보다 약간 큰 대기 구가 둘러싼 형태이므로, 같은 교차 테스트를 행성 자체에도 수행함
- 광선이 대기를 빠져나가기 전에 지면에 닿으면, 지면 교차점을 raymarching 구간의 끝으로 사용함
- 사용된
raySphereIntersect구현은 Inigo Quilez의 Ray-Surface intersection functions를 참고함
-
장면 오브젝트와 대기 종료 조건
- 대기는 행성 표면에 닿거나, 지면에 닿기 전에 다른 장면 오브젝트를 만나면 종료되어야 함
- 행성에 닿는 경우 기본적으로
atmosphereFar = min(atmosphereFar, planetHit.x)로 지면에서 멈춤 - 다른 메시가 지면 앞에 렌더링되어 있으면
sceneDepth < planetHit.x - 2.0조건으로 판별하고atmosphereFar = min(atmosphereFar, sceneDepth)를 적용함 - 이 로직이 없으면 행성 표면이 오브젝트보다 앞에 나타나는 문제가 생김
-
React Three Fiber 데모와 남은 글리치
- 두 조정을 코드에 반영하면 대기 산란을 후처리 효과로 구현하고, 행성 주변의 대기를 렌더링할 수 있음
- 데모 장면은 React Three Fiber에서 간단한 “Sun - Earth system”을 렌더링하고 커스텀 효과를 적용함
- 태양 위치를 조정하고 줌아웃하면 지상에서 궤도까지 다양한 각도에서 셰이더가 만드는 하늘색을 볼 수 있음
- 같은 효과가 4월 초 글 예고용 포스터 이미지에 사용되었으며, 렌더 이미지는 트윗으로 공유됨
- 장면의 torus는 해가 진 뒤에도 여전히 “lit-up” 상태로 보일 수 있음
- 원인은 주 directional light의 shadow-map 또는 shadow-camera 스케일이 작아, 너무 멀리 있는 torus를 덮지 못하는 데 있음
- 우회책으로 volumetric lighting article의 shadow-mapping 접근을 재사용할 수 있지만, 실제로 시도되지는 않음
일식 처리
- 큰 천체가 태양을 가리는 경우는
lightMarch이후sunVisibility함수를 호출하고, 반환값[0, 1]을 투과율에 곱하는 방식으로 추가할 수 있음 - 기본 아이디어는 현재 샘플 지점에서 달 방향과 태양 방향의 내적을 비교하는 것임
- 두 방향이 거의 같아 내적이
1.0에 가까우면 달이 태양을 가리는 상태이고, 직교해0.0에 가까우면 가림이 없음 - 단순 내적만으로는 장면 안 객체의 크기와 스케일을 반영하지 못하므로, 구현은 태양과 달의 각거리와 각각의 각반지름을 비교함
sunVisibility는 달이 태양을 가리지 않는 경우, 달이 카메라 시점에서 태양보다 크거나 비슷한 크기로 보이는 상태에서 가리는 경우, 달이 카메라 시점에서 태양 반지름 안에 들어가는 상태로 가리는 경우를 다룸- 데모는 기존 대기 산란 예제 위에
sunVisibility와 달 메시를 추가해, 달을 태양과 정렬했을 때 빛이 부족한 상황을 Atmospheric Scattering 셰이더가 처리하도록 함 - 더 정교한 일식과 코로나 시뮬레이션은 Physically Based Real-Time Rendering of Eclipses 논문에서 다루며, 해당 논문 구현은 WebGL로 포팅하지 않음
다른 행성의 대기
- 사용한 대기 밀도와 산란 모델은 행성과 대기의 반지름,
RayleighScaleHeight,RayleighBeta,MieScaleHeight,MieBeta,mieBetaExt,mieG,OzoneHeight,OzoneWidth같은 몇 가지 상수로 대부분 결정됨 - 이 값들을 조정하면 화성 대기나 다른 행성의 대기에 가까운 결과를 만들 수 있음
- 화성용으로 사용한 값은 근사치임
planetRadius: 3390atmosphereRadius: 3500, 약110 km두께rayleighScaleHeight: 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'
- 기존 상수를 이 값들로 바꾸면 더 먼지 많고 주황빛인 대기가 나오며, 화성의 특징적인 일몰 시 푸른 색조도 얻을 수 있음
- 관련 논문으로 Physically Based Rendering of the Martian Atmosphere가 있음
LUT 기반 대기 산란
-
접근 방식과 단축한 부분
- 기존 셰이더는 작은 스케일과 큰 스케일의 대기를 직관적으로 렌더링할 수 있지만,
PRIMARY_STEPS가 많은 레이마칭 루프,lightmarching중첩 루프, 전체 화면 해상도 계산 때문에 실행 비용이 큼 - Sebastian Hillaire의 A Scalable and Production Ready Sky and Atmosphere Rendering Technique는 비용이 큰 산란 계산을 텍스처에 저장하고, 최종 렌더에서 미리 계산된 텍스처를 샘플링·합성하는 Look Up Tables(LUTs) 기반 방식을 제안함
- 다루는 LUT는 빛이 대기를 통과하면서 살아남는 양을 저장하는 Transmittance LUT, 특정 카메라 위치에서의 하늘 색을 저장하는 Sky-view LUT, 카메라와 보이는 장면 지오메트리 사이의 대기 헤이즈와 산란광을 저장하는 Aerial Perspective LUT임
- 전체 논문 구현을 그대로 옮기지는 않았고, LUT는 WebGPU의 compute shader에 적합하지만 시간 부족과 글의 연속성 때문에 WebGL을 유지함
- 논문에서 Aerial Perspective LUT는 3D texture지만, 구현에서는 2D render target을 사용함
- 이 방식은 카메라가 움직일 때마다 올바른 픽셀 값을 위해 텍스처를 다시 생성해야 하며, 미리 계산해두기 어려움
- Multi-Scattering은 시간 부족으로 생략됨
- 기존 셰이더는 작은 스케일과 큰 스케일의 대기를 직관적으로 렌더링할 수 있지만,
-
Transmittance LUT
- 기존 셰이더에서는 모든 샘플 지점이
lightmarch를 호출해 태양빛이 얼마나 도달하는지 계산했으며, 이 과정이 비쌈 - Transmittance LUT는 이 데이터를 낮은 해상도로 미리 저장해, 이후 LUT들이 빛 데이터를 필요로 할 때 읽어 쓰도록 함
- 구현은
250 x 64해상도의 전용 Frame Buffer Object를 정의하고, 커스텀 셰이더 material을 전용 장면transmittanceLUTScene의 full-screen quad에 적용한 뒤 렌더 결과 텍스처를 downstream LUT의 uniform으로 전달함 - 각 픽셀에서
vec3(0.0, radius, 0.0)부터 레이마칭하며,radius는vUv.y좌표를 따라planetRadius에서atmosphereRadius까지 증가함 - LUT의 x축은 빛의 각도, y축은 고도를 나타내며, 순수한 흰색은
100%투과율이고 검은색 또는 색이 있는 영역은 지면이나 공기가 가장 두꺼운 부분을 나타냄 - 이후 LUT들은 “주어진 각도와 고도에서 대기를 통과해 살아남는 빛의 양”을 텍스처 조회만으로 얻을 수 있음
- 기존 셰이더에서는 모든 샘플 지점이
-
Sky-view LUT
- Sky-view LUT는 특정 방향을 지상에서 올려다볼 때 하늘이 어떤 색인지 계산함
getSkyViewRayDir는vUv.x를 azimuth[-PI, PI]에,vUv.y를 elevation[-PI/2, PI/2]에 매핑해 레이마칭 방향을 정의함- elevation에는
(vUv.y * vUv.y - 0.5) * PI라는 quadratic mapping을 사용하며, 먼 거리에서 Sky View가 너무 많이 깜빡이는 것을 피하기 위한 우회책임 - 레이가 대기에 들어가지 않으면 검은색을 반환하고, 행성에 닿는 레이는 보이는 대기 구간까지만 레이마칭하며 행성에 닿으면 더 일찍 멈춤
- 산란 루프는 이전과 같지만, Sky View 방향을 따라 진행하며 태양빛에는 Transmittance LUT를 사용함
-
Aerial Perspective LUT
- Hillaire 논문과 달리 구현 결과는 2D 텍스처이고, 각 픽셀은 보이는 화면 픽셀 하나에 대응함
- 장면 depth buffer를 사용해 해당 레이를 따라 얼마나 멀리 행진하고 산란을 누적할지 결정함
- 기존 산란 코드를 거의 재사용하되, 각 샘플이 Transmittance LUT에서 태양빛 가시성을 가져옴
- 출력은 RGB에 누적된 대기 산란을 저장하고, 알파에는 합성 때 사용할 packed view transmittance 값을 저장함
- 구현 흐름은
depthBuffer에서 깊이를 읽고,getWorldPosition(vUv, depth)로 화면 픽셀의 월드 공간 위치를 복원한 뒤, 카메라 위치에서 월드 위치까지의rayDir을 계산하는 방식임 - 이어서
logDepthToRayDistance(vUv, depth)로 장면 깊이를 레이 거리로 변환하고, 대기와 행성 교차를 계산한 뒤 보이는 대기 구간만 march함
-
합성
- Sky-view LUT와 Aerial Perspective LUT를 생성한 뒤 마지막 post-processing pass에서 둘을 결합함
- 핵심 작업은 현재
rayDir을 Sky View UV 좌표로 변환하는 것임 - 장면 지오메트리에는 Aerial Perspective LUT를 적용하며, 알파 채널은 view transmittance로, RGB 채널은 산란광으로 사용해
color = color * aerialPerspective.a + aerialPerspective.rgb를 계산함 - 배경 픽셀에는 Sky View LUT를 샘플링하며,
depth >= 1.0 - 1e-7이면 배경으로 보고color = inputColor.rgb + sampleSkyViewLUT(rayDir, planetCenter)를 적용함 - 마지막으로
ACESFilm(color)와pow(color, vec3(1.0 / 2.2))를 적용함 - 전체 LUT 기반 대기 구현 코드는 Github link에서 확인할 수 있음
마무리
- LUT 기반 대기 산란 결과는 이전 완전 레이마칭 버전과 거의 같아 보일 수 있지만, 내부 과정은 다름
- 작업을 더 작은 LUT들로 나누고 마지막 효과에서 합성하며, 각 샘플마다 태양 쪽으로 반복 레이마칭해 도달 빛을 계산하지 않음
- Transmittance LUT에서 조명 정보를 직접 가져오므로, 비용 큰 중첩 루프를 단순 텍스처 조회로 대체하고 최종 장면에서 무시할 수 없는 성능 향상을 얻음
- 구현은 Sébastian Hillaire와 다른 분야 구현에 비하면 부족하며, 특히 Sky View에서 banding과 flickering이 있고 단축한 부분 때문에 최적성이 떨어짐
- 처음부터 WebGPU를 썼어야 했을 가능성이 있음
- 실제 production-grade 구현으로 Shoda Matsuda(@shotamatsuda)의 three-geospatial을 추천함
- 추가로 volumetric clouds를 얹는 작업도 했지만, 결과가 아직 mixed bag이며 글로 보여줄 만큼 만족스럽지는 않아 더 작업이 필요함
댓글과 토론
Hacker News 의견들
- 예전에 본 거라 완전히 관련 있진 않을 수 있지만, Sebastian Lague가 행성 생성 실험에서 대기 렌더링을 다룬 영상도 정말 재미있었음 https://www.youtube.com/watch?v=DxfEbulyFcY
시각 효과를 개발하고 점점 현실처럼 구현되는 걸 보는 데는 특별히 재미있는 면이 있고, 언젠가 이 분야를 직접 실험해보고 싶음- Sebastian Lague에서 가장 놀라운 건 YouTube 알고리즘이 사람을 얼마나 망칠 수 있느냐임
예전엔 영상 조회수가 수백만이었는데 지금은 50만도 겨우 넘김. 코로나 때 모두 집에 있으면서 랜덤한 것들에 관심을 가졌던 영향일 수도 있음 - Sebastian Lague에 대한 유일한 불만은 영상이 충분히 많지 않다는 것뿐임
보통 잠들 때 틀어두는데, 차분하지만 깊이 있게 기술 주제를 파고드는 이런 콘텐츠가 더 있었으면 해서 직접 만들어볼까 생각한 적도 있음
- Sebastian Lague에서 가장 놀라운 건 YouTube 알고리즘이 사람을 얼마나 망칠 수 있느냐임
- 의도적으로 뺀 건지 모르겠지만, 일몰 모델에서 해가 지평선 아래로 내려가자마자 하늘이 검게 변하면 안 된다는 점은 짚을 만함
해가 진 뒤에도 한동안 머리 위 대기와 지평선 위 영역은 여전히 햇빛을 받고, 지구 대기에서는 태양이 지평선 아래 18도까지 내려갈 때까지 눈에 띄는 황혼이 남음. 광선 추적으로 구현하긴 실용적이지 않을 수 있지만, 이를 모델링하는 일반적인 알고리즘은 있음 - 좋은 그래픽 글은 언제나 반가움. 나도 절차적 우주/행성 생성기에서 비슷한 걸 작업해왔고, 대기 산란은 체적 구름 렌더링과 결합하면 멋진 일몰과 하늘 장면을 만들 수 있다는 점이 좋음
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... - 요즘 휴대폰과 브라우저가 할 수 있는 일이 정말 놀라움
1993년 논문이자 이 주제의 원조에 가까우며 아주 읽기 쉬운 Nishita 등의 “Display of The Earth Taking into Account Atmospheric Scattering”를 구현했던 기억이 남: https://www.researchgate.net/publication/2933032_Display_of_...- 예전에 Rayleigh 산란과 Mie 산란을 구현하면서 읽었던 논문을 다른 댓글에서 떠올렸는데, 이게 확실함
작동하게 만들었을 때 “이 복잡한 현실 세계 현상을 비교적 단순한 계산 몇 개로 꽤 잘 모델링할 수 있네”라는 순간이 왔음. 정적인 파란 하늘상자에서 순식간에 완전한 주야 순환으로 넘어갔음
- 예전에 Rayleigh 산란과 Mie 산란을 구현하면서 읽었던 논문을 다른 댓글에서 떠올렸는데, 이게 확실함
- 정말 훌륭함
예전에 웹에서 하늘을 여러 그라데이션을 겹쳐 렌더링해보면 어떨까 생각한 적이 있음. 어느 정도는 성공해서 그럭저럭한 결과를 얻었을 수는 있겠지만, 여기서 만든 것과는 비교가 안 됨. 결과물이 인상적이고 영감을 줌- 예전에 취미로 만든 게임 엔진에서 Rayleigh 산란과 Mie 산란을 구현한 적이 있음
그것만으로도 꽤 그럴듯한 일몰/일출 순환이 나오는 걸 보고 놀랐고, 기억이 맞다면 태양 자체도 어쩐지 거기서 자연스럽게 튀어나왔음. Microsoft의 C# 게임 개발 플랫폼인 XNA를 쓰면서 Riemer의 훌륭한 튜토리얼 시리즈를 따라갔고, 보존본은 여기 있음 https://github.com/SimonDarksideJ/XNAGameStudio/wiki/Riemers...
다만 산란 관련 내용은 보이지 않아서 그 부분은 다른 데서 가져왔을 수도 있음. 수식이 들어간 논문들을 읽었던 기억은 남
- 예전에 취미로 만든 게임 엔진에서 Rayleigh 산란과 Mie 산란을 구현한 적이 있음
- SpaceEngine도 이 분야에 꽤 공을 들인 것으로 유명해서 강력 추천함: https://www.youtube.com/watch?v=_4TjdVAbXks
https://spaceengine.org/- 이런 것들의 FAQ는 스케일과 질문의 다양성을 보여줘서 좋음
“SpaceEngine에는 객체가 몇 개 있나요?”에 대한 답이 Hipparcos 별 목록 전체, 알려진 외계 행성 전부, 만 개가 넘는 은하, 태양계 대부분의 객체를 합쳐 13만 개이며, 여기에 관측 가능한 우주 전체에 실제로 존재하는 것보다 더 많은 은하와 항성계가 추가된다는 식임. “물 행성이 어떻게 뜨거울 수 있나요?”에는 상층 대기의 물은 뜨거운 수증기지만 아래로 내려가면 고압에서 액체로 부드럽게 전이되고, 더 깊게는 ice VII라는 고체 상태가 된다고 답함. “어떻게 이동하나요?”의 답은 WASD 키임 - 한쪽 탭에는 Wikipedia, 다른 탭에는 SpaceEngine을 띄워두는 게 내가 좋아하는 준교육적 게임 경험 중 하나임
훌륭한 게임이고, 꽤 오래됐는데도 이만큼 괜찮은 걸 아직 못 봄 - 여러 해 전부터 있었던 훌륭한 소프트웨어이고, 이 주제뿐 아니라 많은 부분에서 디테일에 대한 집착이 대단함
이 글을 보니 나도 SpaceEngine이 떠올랐음
- 이런 것들의 FAQ는 스케일과 질문의 다양성을 보여줘서 좋음
- 산란은 오래전부터 사실적인 렌더링 이미지를 만드는 핵심이었음
내가 좋아하는 논문 중 하나: http://www.graphics.stanford.edu/papers/bssrdf/bssrdf.pdf
우유를 렌더링하는 게 까다로운 문제라는 걸 아마 이때 처음 배웠던 것 같음 - 와, 꽤 엄청난 여정이었음
아마 5% 정도밖에 이해 못 했지만, 정말 크게 감탄함- 나도 마찬가지임. 시각 자료만으로도 읽을 가치가 있었음
- 오, 이건 정말 아름답고 읽기 좋은 글임
게다가 MIT 라이선스라면 내 게임의 하늘상자 문제는 해결된 셈임. 원근이 고정될 테니 태양이 하늘을 가로지르는 렌더만 있으면 되고, 여기에 사인파 주기로 연중 태양 각도 변화를 확장할 수 있음