브라우저 탭 안의 행성
진짜 위성 사진, 4만 6천 개의 도시 불빛, 그리고 카메라를 바로잡은 구면삼각법 한 줄. 다 합쳐 1.5메가바이트 남짓이다.
3 분 읽기서울과 나트랑에서 씁니다
우리 사이트에는 이제 스크롤로 카메라를 저궤도에 태워 우리가 일하는 거점 네 곳을 차례로 도는 챕터가 있다. 딱 봐도 50메가바이트쯤 나갈 것처럼 생겼다. 그런데 실제로는 1.5메가바이트 남짓으로 나가고, 거의 전부가 퍼블릭 도메인 과학 데이터로 만들어졌다.
진짜 데이터를, 단단히 구워서
- 행성은 NASA 블루 마블 이미지를 빌드 타임 생성기가 크기를 줄이고 다시 압축한 것이다. 손으로는 절대 손대지 않는다. 바꾸고 싶은 게 있으면 생성기를 고쳐서 다시 굽는다.
- 도시 불빛은 텍스처가 아니다. 스크립트가 NASA 야간 불빛 래스터를 가중치가 실린 약 4만 6천 개의 점으로 샘플링해 작은 바이너리 버퍼에 담고, 이걸 빛나는 스프라이트로 렌더링한다. 픽셀이 아니라 지오메트리라서 어느 줌 배율에서든 또렷하다.
- 클로즈업은 2단계 패치 기법을 쓴다. 지구 전체 이미지는 픽셀당 약 10km라 고도 200km에서는 가망이 없다. 그래서 생성기는 카메라가 실제로 들르는 도시 네 곳 주변에만 고해상도 패치(구름 없는 Sentinel-2 데이터 기반, 픽셀당 76m까지)를 구워 넣는다.
패치는 프래그먼트 셰이더에서 섞이는데, 가장자리를 부드럽게 페더 처리해 이음매가 절대 드러나지 않고, 그 아래 절차적 디테일 노이즈는 서서히 사라지게 해서 실사 사진과 합성이 서로 싸우지 않게 한다.
float fe = smoothstep(edge, edge - feather, dist); // patch edge feather
vec3 col = mix(base, patchTexel, fe * uPatch);
col *= 1.0 - patchAmt * 0.75; // damp noise under imagery
삼각법 한 줄이 카메라를 바로잡았다
스크롤은 정거장 사이를 대원 경로로 비행하게 만든다. 카메라를 겨누는 뻔한 방법인 normalize(next - pos)는 종점에서 정확히 영벡터로 무너지는데, 순진한 플라이오버 카메라가 도착하는 순간 홱 꺾이는 이유가 이것이다. 해법은 구면 보간 그 자체의 해석적 접선이다. t = 0과 t = 1을 포함해 어디서나 안정적이다.
// bearing along slerp(a, b) at parameter t, W = angle between a and b
tangent(t) = normalize( -cos((1 - t) * W) * a + cos(t * W) * b )
도착하면 북쪽을 위로 두고 자리를 잡고, 카메라가 하강하는 동안 그대로 멈춰 있다가, 다음 목적지 쪽으로 돌아서서 살짝 기울인 채 떠난다. 이 모든 동작은 그 접선과 로컬 북쪽 사이의 부호 있는 각도로 굴러간다. 조명은 일부러 시네마틱하게 뒀다. 도착할 때 해가 떠올라 지형이 눈에 읽히고, 야간 비행에서는 다시 여명 띠까지 가라앉는다.
정직한 성능
ema = ema * 0.9 + frameMs * 0.1; // frame-time moving average
if (ema > 24.0 && px > FLOOR) {
px -= 0.2; // shed resolution before jank
renderer.setPixelRatio(px);
}
거버너가 돌아가는 프레임 시간을 지켜보다가, 약한 GPU가 프레임을 떨구기 시작하기 전에 내부 해상도를 한 단계씩 낮춘다. 하한선을 둬서 글자와 해안선은 또렷하게 유지된다. 에셋은 챕터가 다가올 때만 로드되고, 화면 밖에 있을 때는 통째로 공짜로 놀린다. 어워즈 낚시용 비주얼은 허용한다. 어워즈 낚시용 버벅임은 안 된다.