목차
지금까지 한 일들은 다음과 같습니다.
이제는 별의 (방위각, 고도)값을 이용하여 브라우저에서 표시될 위치를 계산해야합니다.
투영(Projection)
모니터는 직사각형이고 천구는 반구의 형태입니다. 그렇기 때문에 반구를 평면에 투영하는 과정이 필요합니다. 이 때, 투영을 어떤 식으로 하냐에 따라서 실제 투영된 평면에 맺힌 상이 다릅니다. 다양한 투영방식이 있고, 그에 따른 다양한 왜곡이 생겨납니다.
이 글에서는 Stereographic Projection과 Cylindrical Projection을 활용합니다.
Stereographic Projection
아래 동영상을 봅시다.
동영상 중간(혹은 썸네일)에 보면 땅에 비치는 그림자가 직교하는 격자가 될 때가 있는데, 그 장면이 Stereographic projection이라고 생각하면 됩니다. 위에서 빛을 쏴서 구(동영상에서의 물체)의 각 부분을 평면(동영상에서의 바닥)에 대응시키는 방식입니다. 간략하게 도식화하면 아래 그림과 같습니다.
이 방식을 선택한 이유는 해당 방식이 conformal하면서 구현하기 쉽기 때문입니다. 투영이 conformal 하다는 것은 투영 전의 두 곡선이 이루는 각의 크기를 투영한 후에도 보존할 수 있다는 것이라 생각하면 됩니다.
각의 크기를 보존해야 할 이유는 별자리 때문입니다. 곡선들이 이루는 각의 크기를 보존해야 실제 우리가 보는 밤하늘의 별자리와 비슷한 모양이 나옵니다.
Cylindrical Projection
Cylindrical projection은 말 그대로 원통에 투영하는 방식입니다. 천구에 접하는 원통을 만든 후, 천구 안에서 빛을 쏴서 원통에 천구가 투영되게 하는 방식입니다.
이 글에서는 위의 그림의 왼쪽 방식을 사용합니다. 왼쪽 방식을 사용할 경우 다음과 같은 식을 이용하여 투영이 가능합니다.
$$ x = Az \\
y = Alt $$
구현
Stereographic Projection
위에서 아래로 내리쬐는 경우에는 위키백과에 투영 공식이 나와있습니다.
$$(X, Y) = (\frac{x}{1 - z}, \frac{y}{1 - z})$$
해당 식은 북극에서 빛을 쏴서 남반구를 평면에 투영시키는 식이기 때문에, 식을 바꿔서 접속한 곳의 지구 반대편 적도에서 빛을 쏴서 접속한 곳 앞의 평면으로 투영되도록 수정할 필요가 있습니다.
가장 쉬운 방법은 x, y, z 변수 위치를 바꾸는 방법인데, 해당 방법을 사용하기 위해서는 현재 Az-Alt로 표현되어있는 구면좌표계(Spherical coordinate)를 직교좌표계(Cartesian coordinate)로 바꿔줄 필요가 있습니다.
구면좌표를 직교좌표로 바꾸는 식은 다음과 같습니다.
$$
x = sin(\theta)cos(\phi) \\
y = sin(\theta)sin(\phi) \\
z = cos(\theta)
$$
이 때, $$\theta, \phi$$와 Az, Alt는 출발하는 점과 방향이 다르기 때문에, Az를 반시계방향으로 바꿔주고, Alt는 위에서 아래 방향으로 값이 증가하게 바꿔줄 필요가 있습니다. 해당 내용에 유의하여 구현합시다.
1 | // azaltToCatesian : az -> alt -> {x, y, z} |
직교좌표를 얻은 후에 변형된 투영 식을 이용하여 Stereographic Projection을 구현해줍시다.
1 | // Thx to Daehyun Pyo for nice reference. |
식을 잘 수정할 경우 남반구에 해당하는 별도 투영할 수 있습니다. 해당 코드에서는 FOV가 N이 아닌 경우에 남반구를 투영합니다.
Cylindrical Projection
Cylindrical Projection은 아까 언급한 것과 같이 간단히 구현할 수 있습니다.
1 | // setCelestialMap : star* -> star* |
결과
Stereographic Projection
위의 투영공식을 사용하면
$$
-1 \leq Y \leq 1 \\
-1 \leq Z \leq 1
$$
``
이기 때문에 적절한 크기로 X, Y를 scale해줄 필요가 있습니다. 위의 사진에서는 X, Y에 임의의 값(400.0)을 곱했습니다. 어떤 값을 곱해야 가장 적절하게 표현될지는 아래 그림을 보면 알 수 있습니다.
위 사진의 원은 투영된 밤하늘이고, 직사각형은 브라우저의 창입니다. 직사각형이 지평선과 천구 위쪽과 접하게 만드려면 투영면의 가운데(0, 0)이 브라우저의 (innerWidth / 2, innerHeight)에 위치하도록 원점을 옮겨줄 필요가 있습니다.
따라서 Math.sqrt(W * W + H * H), W = innerWidth/2, H = innerHeight
를 곱한 후 적절하게 투영면을 이동시켜주면 최종적으로 위 그림과 같은 직사각형 영역을 투영할 수 있게됩니다.
실제 결과는 아래와 같습니다.
실제 우리가 보는 밤하늘과 같아졌습니다. 화면 가운데에 북극성이 있고, 오른쪽에 북두칠성을 확인할 수 있습니다.
Cylindrical Projection
Cylindrical projection도 잘 구현된 것을 확인할 수 있습니다. 별이 많은 영역을 보면 sin 함수 곡선처럼 생겼는데, 해당 영역이 우리 은하의 은하수 부분입니다. 자세히 들여다본다면 왼쪽 위 부분에 있는 오리온자리를 확인할 수 있습니다.