ashen-aetna-ko

재빛 에트나 (Ashen Aetna)

— 재 덮인 화산 위에서 서툴게 비틀거리기

(3D 그래픽스, Rust, Vulkan, ash에 대한/속의/관한/과 함께하는 튜토리얼)

좌표 (Coordinates)

(만약 여러분의 우선순위가 API 세부사항보다는 그래픽스 프로그래밍 자체에 있어서 Vulkan 설정을 따라오지 않았다면, 11장 끝부분에 있는 파일 내용을 가져오세요. 플랫폼별 코드를 일부 수정해야 할 수도 있습니다 (Windows나 Mac의 경우엔 반드시 수정해야 합니다). 해당 코드는 6장에 한정되어 있을 것입니다. 4장의 설정으로 인해 문제가 발생할 수도 있습니다.)

이제 화면에 무언가 표시되니, GPU에서 실행되는 프로그램인 셰이더(shader)를 좀 더 자세히 살펴보겠습니다. 우리의 정점 셰이더(vertex shader)를 기억하실 겁니다:

	#version 450

	void main() {
	    gl_PointSize=2.0;
	    gl_Position = vec4(0.0,0.0,0.0,1.0);
	}

이게 정말 효과가 있는지 한번 확인해 봅시다. 간단하게 점 크기부터 바꿔볼까요:

	    gl_PointSize=200.0;

(프로그램을 실행해 보세요.) 우와, 엄청나게 큰 점이군요! (아마도 좀 더 적당한 값으로 되돌리는 게 좋겠습니다. — 저는 10.0으로 하겠습니다. 그래야 여전히 쉽게 알아볼 수 있으니까요.)

더 흥미로운 것은 점의 좌표입니다. (네 개의 숫자로 이루어져 있고, 그중 네 번째는 지금은 무시하겠습니다. 이 값은 1.0으로 설정되어 있는데, 이 시점에서는 이 값이 점이라는 것을 나타내는 표식 정도로만 취급하기로 했습니다. 처음 세 개의 숫자에 대해 이야기해 봅시다. 저는 이 숫자들을 (아주 창의적으로) x, y, z라고 부르겠습니다.)

현재 점은 창의 정중앙에 위치해 있습니다. 한번 시도해 보세요.

	    gl_Position = vec4(1.0,0.0,0.0,1.0);

점은 이전과 같은 높이에서 창의 오른쪽 경계에 위치하게 될 것입니다.

비슷하게,

	    gl_Position = vec4(-1.0,0.0,0.0,1.0);

는 왼쪽에 놓입니다.

참고: 첫 번째 좌표인 x는 창(더 정확히는 뷰포트)의 왼쪽 -1에서 오른쪽 1까지의 범위를 가집니다. 다음 요소, y입니다.

	    gl_Position = vec4(0.0,-1.0,0.0,1.0);

그리고

	    gl_Position = vec4(0.0,1.0,0.0,1.0);

결론: 두 번째 좌표인 y는 화면의 위쪽 -1에서 아래쪽 1까지의 범위를 가집니다.

만약 다른 맥락(예를 들어, 학교에서)에서 좌표계를 다뤄본 적이 있다면, 여러분이 익숙한 것과 다를 수 있습니다. 이것은 ‘옳고 그름’의 문제가 아니라, 관례의 문제입니다. 그리고 이 방향들이 바로 Vulkan이 선택한 방식입니다.

여러분은 아마 점

	    gl_Position = vec4(0.8,0.4,0.0,1.0);

이 어디에 찍힐지 프로그램을 실행하기 전에도 짐작할 수 있을 겁니다. 이제 첫 두 요소를 0.0으로 되돌리고 세 번째 요소를 가지고 놀아봅시다:

	    gl_Position = vec4(0.0,0.0,1.0,1.0);

이전과 똑같아 보입니다.

	    gl_Position = vec4(0.0,0.0,-1.0,1.0);

오. 이제 사라졌네요.

	    gl_Position = vec4(0.0,0.0,0.0,1.0);

여전히 창 중앙에 존재합니다. (이 점은 전에 봤었죠.)

우리는 다음과 같은 결론을 내릴 수 있습니다: 세 번째 요소인 z는 -1과 1 사이에서 움직이지 않습니다. 실제로는 0.0과 1.0 사이에서 움직이며(z=-0.01 이나 z=1.01인 점은 보이지 않는다는 것을 직접 확인해 보세요) — 아무런 효과가 없는 것처럼 보입니다.

세 번째 좌표는 분명히 존재합니다. 그것은 화면(z=0)에서부터 화면 의 한 점(z=1.0)을 향하고 있습니다. 여러분의 화면이 상자라고 상상해 보세요. 화면과 그 뒤의 일부 공간을 합쳐 [-1,1]x[-1,1]x[0,1] 크기의 상자라고 생각하는 것입니다 (즉, x는 -1과 1 사이, y는 -1과 1 사이, z는 0과 1 사이). 왜 z는 중요하지 않을까요? 음, 여러분의 화면은 아마 상자가 아니라 (대부분) 평평한 표면일 겁니다. 무언가가 화면 “뒤”의 상자 안 좌표 (x,y,z)에 있다면, 그것을 화면의 x-y 평면상 해당 지점, 즉 (x,y,0)에 보여주는 것보다 더 나은 선택지는 없습니다.

이와 같은 투영(projection)은 3차원 객체를 2차원 표면(여러분의 화면 같은, 즉 기본적으로 항상)에 맞게 변환하고자 할 때마다 필요합니다. 그럼에도 불구하고, Vulkan(그리고 이 맨 마지막 단계를 거치기 전)에서는, (대부분) 여러분의 화면이 실제로 상자이며 모든 점이 세 가지 요소로 구성된 좌표를 가지고 있다고 가정할 수 있습니다.

참고로, 이 좌표계는 “오른손 좌표계(right-handed)”입니다: 오른손을 들어 엄지손가락과 집게손가락을 폅니다. (약간의 상상력을 발휘하면, 두 손가락 사이에 직각이 형성될 겁니다.) 그런 다음 가운데 손가락을 반쯤 펴서, 엄지와 집게손가락 모두에 수직인 방향을 가리키도록 합니다. 우리는 엄지가 x축 방향(손가락 밑부분이 0, 끝이 1을 향함), 집게손가락이 y축 방향(마찬가지로 밑부분이 0, 끝이 1을 향함), 그리고 가운데 손가락이 z축 방향(손의 나머지 부분이 0, 끝이 1을 향함)을 나타낸다고 가정할 것입니다.

이제 이 손가락 축들이 Vulkan의 좌표축(‘오른쪽’/’아래쪽’/’화면 안쪽’)과 일치하도록 손을 돌려볼 수 있습니다 — 솔직히 말해, 이 자세는 화면 앞에서 하기에 좀 불편해서 덜 불편하게 하려면 일어서야 할 수도 있습니다. 요점은, 왼손으로도 비슷하게 좌표계를 만들 수 있지만(엄지: x, 집게: y, 가운데 손가락(반쯤 편): z), 어떻게 돌려도 이 좌표계는 Vulkan의 좌표계와 절대로 일치하지 않는다는 것입니다. 손가락 하나는 반드시 반대 방향을 가리키게 될 겁니다.

이제 저 신비한 네 번째 좌표를 살펴봅시다:

다음 코드를 보시죠.

	    gl_Position = vec4(0.8,0.4,0.0,1.0);

이것을 다음과 비교해 봅시다.

	    gl_Position = vec4(0.8,0.4,0.0,2.0);

흠. 바로 눈에 띄지는 않는 효과가 있는 것 같네요.

그런데 이 점, 원점(0.0,0.0,0.0)과 이전 점의 딱 중간 지점 아닌가요?

확인해 봅시다:

	    gl_Position = vec4(0.4,0.2,0.0,1.0);

네. 같은 위치인 것 같네요. 실제로, (x,y,z,w)와 (x/w,y/w,z/w,1)은 동일한 점입니다.

왜 이런 방식을 사용할까요?

이제 점을 표현하고 변환하는 방법에 대해 잠시 외도를 할 시간입니다. 결국, 점을 x,y,z 세 가지 요소로 구성된 “벡터”로 인코딩하고, 표준 “선형대수학” 도구를 사용해 작업하는 것이 매우 합리적으로 보일 것입니다. 그 여정이 끝날 무렵, 이 네 번째 요소를 도입함으로써 해결할 수 있는 한두 가지 단점을 발견하게 될 것입니다.

수학 강의가 이어집니다 …