리콘랩스는 전 구성원들의 회사의 핵심 기술의 이해를 돕기 위해 내부 세미나를 개최하며 노력하고 있습니다. 지난 8월에 열린 첫 세미나의 뜨거운 반응에 이어, 세미나 2회차로 3차원 그래픽스에 대한 기초 지식 쌓는 시간을 가졌습니다. 이번 세미나는 지난 세미나보다 더 많은 구성원분이 온•오프라인을 통해 참석해 주셨습니다.
아래 그림이 이해되시나요? 이 그림은 화면에 픽셀로 표시되는 정보를 생성하기까지의 과정을 보여줍니다. 이후에는 그림의 내용을 자세히 다룰 예정입니다. 그럼, 본격적으로 강의 내용을 전달해 드릴게요.
우선 3차원 표현 방식에 대한 기본 개념부터 가볍게(?) 짚고 넘어가는 게 필요했어요.
3차원 표현 방식에 대한 이해
그림 파일은 ‘픽셀’이라는 최소 단위로 표현되는 것은 아시는 분이 많을 것 같아요. 하지만 3차원 정보가 어떻게 표현되는지에 대해서는 생각보다 모르는 분들이 많이 계십니다. 때문에 우리는 먼저 3차원 표현이 어떤 식으로 이루어지는지, 그리고 왜 이렇게 표현되는지를 먼저 짚어보려고 해요.
복셀 (Voxel)
그림 이야기를 했으니, 그림에 빗대어 생각해 보죠. 만약에 그림과 비슷한 방식으로 3차원을 표현하게 된다면 어떻게 될까요? 이를테면, 아래 그림처럼 픽셀에 차원을 하나 더 추가해 보는 거죠. 이걸 복셀(Voxel) 표현 방식이라고 합니다.
좋아요, 이게 뭐가 문제죠? 파일 용량이 아마 문제가 될 가능성이 높아요.
미래에 1GB 정도를 아무 생각 없이 찍고 저장하고 인터넷으로 부담 없이 주고받을 수 있는 기술이 생긴다면야 모르겠지만… 글쎄요. 아직 최신 핸드폰도 512GB 정도만 저장할 수 있는데, 그럼 이 방식으로는 3D 사진 500장만 저장하면 용량을 다 써 버리겠네요.
어떻게 하면 좋을까요?
메쉬 (Mesh)
비슷한 고민을 했던, 그리고 우리보다 훨씬 적은 용량으로 문제를 해결해야 했던 선배 엔지니어들은 그래서 효율적인 방식을 생각해 냅니다. 바로 메쉬Mesh 방식이에요. 단어의 뜻 자체는 그물 혹은 망인데, Mesh가 무엇인지 감이 오지 않으시면 아래 그림을 보시면 좋을 것 같아요.
게임 파이널 판타지 7의 캐릭터 모델링. 그림 출처: https://www.polygon.com/2020/4/16/21221953/final-fantasy-7-remake-original-playstation-worth-playing
캐릭터가 엄청 각져 있죠? 메쉬Mesh는 이처럼 점과 면으로 3차원 형상을 표현하는 방식을 일컫습니다. 위 그림은 파이널 판타지 7이라는 1997년도에 나온 게임의 첫 장면인데요, 보시면 배경은 그림, 캐릭터는 3D 모델인 것을 알 수 있어요. 캐릭터를 자세히 보면 평면을 여러 개 써서 캐릭터를 만든 건지, 왠지 모르게 각지고 뭉툭한 느낌이 들죠? 당시만 해도 저장 공간의 크기가 충분하지 않아서, 어떻게든 적은 정보를 써서 캐릭터 모양을 만들어 내느라 그랬을 거예요.
아래 그림은 모두 Mesh를 표현해서 만든 다양한 3차원 형상들입니다.
Mesh는 현 세대 게임을 포함한 대부분의 3차원 표현에 있어서 기초가 되는 방식이에요. 때문에 Mesh를 이용해서 어떻게 3차원을 표현하는지 잘 정리하고 넘어가는 게 좋겠어요. 먼저 용어부터 알아볼까요?
Vertex (정점)
정점, 또는 버텍스는 3D 그래픽스에서 사용되는 기본 단위로, 말 그대로 점을 나타냅니다. 3D 모델링에서 가장 기초적인 단위로 사용됩니다.
Face (면)
정점을 모아서 우리는 하나의 면을 만들 수 있어요. 보통은 삼각형을 많이 쓰는데요, 때에 따라선 3개 이상의 점을 모아 다각형을 만들기도 합니다.
자 그럼 면에 어떤 식으로 색깔을 칠해야 할지가 문제가 되겠죠? 면에 색을 지정하려면 어떻게 해야 할까요? 가장 쉽게 생각할 수 있는 방법은 아마도 픽셀 별로 색깔을 지정하듯이, 면을 이루는 각 꼭짓점마다 색을 정해주면 되지 않을까요?
위 그림은 실제로 3D 그래픽스 mesh로 표현한 삼각형인데요, 각 꼭짓점은 빨강, 초록, 파랑인데, 중간으로 갈수록 색이 부드럽게 변하는 것을 보실 수 있어요.
이처럼 Mesh 방식은 면의 모든 색을 일일이 정해주는 게 아니라,
•
형상(형태)을 꼭짓점과 면의 조합으로 표현
•
각 면의 꼭짓점이 어떤 색깔을 가질지 지정
하는 방식으로 정보를 표현할 수 있어요. 이를테면 위 삼각형을 만드는 데에 실제로 쓴 정보는 대략 아래와 같아요.
그럼 정해지지 않은 중간 부분의 색상은 어떻게 하나요? 평균 내나요?
정말이에요. GPU가 하는 일이 바로 이 역할이거든요. 우리가 정해준 면의 정보는 꼭지점의 위치와 색상밖에 없지만, 나머지 정보는 전부 GPU가 정해진 방식대로 알아서 값을 계산을 해 줘서 그려 줍니다. 그것도 실시간으로요. GPU는 동시에 엄청나게 많은 숫자를 계산하는 데에 최적화되어 있기 때문에 믿고 맡기면 된답니다.
그리고 이게 현재 Mesh 방식이 널리 쓰이는 이유이기도 합니다. 상대적으로 적은 정보만으로 모양과 색을 표현하기가 용이하거든요. 정해지지 않은 그 사이 정보는 컴퓨터한테 적당히 계산하라고 맡겨버린다는 게 포인트.
그럼 이제 열심히 점의 위치와 어떤 점을 모아서 면을 만들 것인지, 그리고 각 점을 무슨 색으로 할지를 정하면, 아까 봤던 폴리곤 스타일 캐릭터를 만들어 볼 수 있을 것 같아요. 그런데 이걸로 충분할까요? 만약에 오른쪽과 같은 마인크래프트 블록을 표현하려면 어쩌죠?
텍스처Texture 와 UV 좌표
정점과 면, 그리고 각 정점의 색상만으로 위 마인크래프트 블록을 표현하려면, 커다란 정육면체를 아주 작은 정사각형으로 나누고, 각 정사각형의 색을 정해주면 되겠네요? 근데 왠지 비효율 적인 것 같죠? 왠지 꼭짓점은 8개만 쓰고, 그 면 전체에 그냥 그림 파일을 붙이면 편할 것 같은데요.
이럴 때 쓰는 방식이 바로 텍스처 Texture입니다. 3D 그래픽스에서 텍스처라고 하면 통상 Mesh의 면에 붙일 그림 파일을 의미합니다.
아까 각 꼭짓점의 색상을 정해줬었죠? 텍스처 방식을 쓰게 되면,
•
색상 대신 그림 파일을 하나 정하고,그림을 붙일 3차원 형상의 면 하나를 정해서,그 면의 꼭짓점마다,그림의 어느 지점을 따 올지 정합니다.
Texture 이미지의 조각을 뜯을 때 통상 UV라는 표현을 많이 쓰는데요, 이 표현은 텍스처 이미지의 가로, 세로 위치라고 보시면 됩니다. 왜 xyz를 사용하지 않는지 궁금하실 텐데요. xyz는 공간, 즉 3차원 좌표를 표현하는데 보통 쓰이기 때문에 혼선을 막기 위해 쓰는 것이라고 이해하시면 될 것 같습니다.
노말 (Normal) 과 쉐이딩 (Shading)
이번에는 약간 어려운 개념에 관해 이야기해 보겠습니다.
Normal (노말)은 일반적으로 사용하는 ‘보통’의 의미가 아니라 수학적인 용어로서, 수학에서의 normal 은 수직 혹은 법선을 의미합니다. 음? 그런데 갑자기 법선(수직)이 왜 튀어나오게 되었을까요? 이해를 돕기 위해 빨간색 공을 가지고 예를 들어보도록 하겠습니다.
왼쪽과 오른쪽은 모두 Mesh로 표현한 빨간 공을 그린 그림입니다. 그런데 한 쪽에는 빛과 그림자가 있어서 공처럼 보이는데 왼쪽은 그냥 빨간 동그라미같이 보이죠?
맞습니다. 잘 생각해 보면, Mesh로 표현한 모든 점에 같은 색을 지정해 준다면, 모든 면이 같은 색이기 때문에 원래는 빨간 원처럼 보여야 해요. 오른쪽처럼 빛과 그림자가 없다면 말이죠.
아까 파이널 판타지 캐릭터를 다시 자세히 볼까요?
옷이나 어깨, 머리카락을 자세히 보시면 미묘하게 어느 부분은 그림자가 있고, 어느 부분은 밝게 표현된 것을 볼 수 있어요. 꼭짓점에 전부 같은 색을 정해줬다면 사실은 전부 평면처럼 보여야 하는데 그렇지 않죠. 어떻게 한 것일까요? 이런 표현을 하기 위한 기법을 그래픽스에선 셰이딩 Shading이라고 하는데요, 이 쉐이딩 계산을 하기 위해서 수직 정보가 꼭 필요해요.
아래 그림을 보죠. 실제로 게임과 같은 환경에서는, 형상뿐만 아니라 조명에 관한 정보도 꼭 들어갑니다.
보시는 것처럼 3차원 공간상에 우리 눈에는 보이지 않는, 조명 정보 즉 빛이 어떤 방향으로 물체를 비출지에 대한 정보를 추가해 주면, 우리는 어디에 빛이 들어와 밝아지고, 어디에 빛이 덜 들어와 어두워질지를 계산할 수 있어요.
우리 눈이 보는 색은 어딘가에서 출발한 빛이 물체에 반사되어서 보이는 것을 알고 계실 거예요. 이 반사를 제대로 표현하기 위해 수직 방향이 어디인지에 대한 정보가 필요합니다. 빛은 표면의 수직 방향을 기준으로 반사되기 때문이에요. 그리고 면에 들어오는 빛이 얼마나 센지가 이 각도에 따라 달라지게 되어요.
갑자기 어려운 개념이 튀어나와서 조금 부담되실 테지만 쉽게 말해, 빛을 수직으로 세게 받는 면은 밝게 보일 테고, 빛이 비스듬히 들어와서 약하게 받는 면은 상대적으로 어둡게 보여야 해요. (여름에 우리나라가 덥고, 겨울에 추운 이유를 생각해 보시면 됩니다)
그래픽스로 다시 돌아오면 그래서 우리는 조명과 면이 이루는 각도를 알아야 어느 면이 밝게, 혹은 어둡게 보일지를 계산해 줄 수 있어요. 때문에 통상 Mesh는 각 면이 어느 방향이 수직인지, 즉 Normal 방향이 어디인지를 함께 저장하게 됩니다. 계산은 누가 하죠? 물론 우리 친구 GPU가 빠르게 계산해 줄 겁니다.
쉐이더 (Shader)
자 이제 마지막으로 중요하지만 어려운 개념입니다. 바로 그래픽스의 꽃이라고 볼 수 있는 쉐이더Shader 입니다. 위에서 우리가 GPU에 이런저런 잡일(…)을 많이 맡겼었던 것을 기억하시죠? 그 과정을 되짚어 보면 대략 아래와 같습니다.
가만히 보시면 GPU가 하는 일은 결국 다음과 같아요.
GPU는 Mesh로부터 표현된 정보를 받아서
실제로 모니터 픽셀이 어디에, 그리고 어떤 색으로 그려야 할지를 계산합니다.
여기에서 재미있는 것은, 이 ‘계산’을 우리가 마음대로 정해줄 수 있다는 사실입니다.
다시 말해, 우리는 적당히 두 색상 값을 평균해서 정해주거나 빛의 방향에 따라 밝기를 조절하는 정도를 넘어서서, 우리가 마음대로 색을 정하는 규칙을 바꿔줄 수도 있다는 사실입니다. 이렇게 3차원 정보를 그림으로 그려내는 규칙을 사람들이 마음대로 프로그래밍해 둔 것을 우리는 셰이더 Shader라고 부릅니다.
글로만 보면 어려우니 예를 들어볼게요. 아래 그림은 각각 구, 원기둥, 원뿔, 고리에 대한 Mesh를 그래픽스로 그려낸 예시입니다. 그런데 아까 봤던 부드러운 그림자와는 다르게, 뭔가 애니메이션에서 표현하는 것처럼 그림자가 뚝 끊어져 있지요?
위 그림은 툰 쉐이더의 예시입니다. 툰 쉐이딩은 아까 쉐이딩 개념을 설명할 때 들었던 빛을 다루는 규칙을 조금 바꿔서, 마치 만화처럼 3차원 모델을 표현할 수 있게 하는 기법입니다. 아래 공 모양 그림을 볼까요?
보시면, 왼쪽 공은 그림자가 부드럽지만 오른쪽 공은 그림자가 갑자기 뚝 끊겼을 뿐만 아니라, 그림자가 아닌 부분은 전부 색이 같고, 그림자 영역 역시 전부 색이 같아요. 어떻게 한 것일까요? 말씀드린 대로, 쉐이더를 써서 색을 칠하는 규칙을 바꾼
왼쪽에 적용된 규칙이
•
빛에 들어오는 정도에 비례해서 어둡게 면을 칠한다
라면, 오른쪽은
•
빛이 일정 각도 이상으로 면이랑 만나면 원래 면의 색 그대로, 빛이 일정 각도 미만으로 면이랑 만나면, 면이 가지고 있는 색 대신 검정색을 칠해라
라는 규칙을 적용한 결과라고 보시면 되겠어요.
같은 정보를 가진 3차원 물체이지만, 쉐이더를 써서 그려주는 규칙만 바꾸면 전혀 다르게 그릴 수 있어요. 신기하지 않나요? 이렇게 색을 바꿔주는 쉐이더를 우리는 Pixel shader 혹은 Fragment Shader라고 합니다.
우리는 색의 예시만 들었는데, 사실 GPU를 써서 색 대신 점의 위치에 대한 규칙을 적용해줄 수도 있어요. 볼까요?
출처: https://shaderpark.com/sculpt
이 꾸물거리는 덩어리의 정체는 사실 공 모양 Mesh 정보에 불과합니다. 단지 ‘주어진 점을 그대로 두지 말고 꾸물거리게 움직여라’라는 정보를 쉐이더를 통해 추가해 준 것에 불과해요. 그럼 GPU가 열심히 계산해서 점의 위치를 계속 바꾸게 되는 거죠. 이런 방식은 vextex의 규칙을 정해준다는 의미로 Vextex Shader라고 합니다.
그래픽스 파이프라인
긴 여정이었습니다. 세미나에서 우리는 그래픽스의 중요한 개념들을 살펴볼 수 있었어요. 정리해 보자면
•
픽셀처럼 공간을 정보로 표현하는 것은 비효율적Mesh: 점 Vertex 과 면 Face 만으로 효율적으로 3차원 정보를 표현할 수 있다. 각 점에 색을 정해줄 수 있다.꼭짓점 말고 나머지 면의 색 정보는 GPU가 계산해서 화면에 그리기 전에 채워 넣는다.Texture: 점의 색 대신에 그림파일에서 따올 색을 정해줄 수도 있다. 역시 꼭짓점을 제외한 나머지 정보는 GPU가 계산해 준다.Normal: 빛과 그림자가 있어야 3차원처럼 보이는데, 이를 위해선 수직 정보가 꼭 필요하다. 수직 정보를 쓰면 빛의 세기(입사각)에 따른 밝기 변화를 표현해 줄 수 있다.역시 계산은 GPU가 화면에 그리기 전에 해 준다.Shader: GPU가 점의 위치나 색을 정해주는 규칙 자체를 우리가 마음대로 정해줄 수도 있다.
그럼 이제 마지막으로 각 개념이 어떤 순서로 처리되는지만 보겠습니다.
1.
3차원 파일 (꼭짓점, 면, 텍스처, 노말 정보 등)
2.
각 꼭짓점의 3차원 위치를 정해준다.
◦ Vertex Shader: 이때 꼭짓점의 위치를 쉐이더를 써서 정해줄 수 있다.
3.
꼭짓점과 면, 그리고 수직 정보로부터 면의 색상을 결정한다.
◦ 꼭짓점 색상으로 면의 색상을 계산할 수 있다.
◦ 텍스처 이미지로부터 면의 색상을 계산할 수 있다.
◦ 수직 정보를 통해 면의 색상 밝기를 조절할 수 있다.
◦ 규칙을 마음대로 정해 색을 바꿔줄 수도 있다.
4.
이 정보를 모니터에 전달한다.
보다 세부적인 단계도 있고, 전문 용어를 많이 생략했지만, 대략 위와 같은 과정을 거쳐 화면에 3차원 정보가 그려진다는 것을 알게 되었습니다.