본문 바로가기
컴퓨터공학/기초

3D 그래픽

by 하이방가루 2022. 10. 2.
728x90
반응형

  3D 그래픽에서 점들은 3개의 좌표를 가진다. X, Y, Z. 물론 2D인 컴퓨터 화면에서 X, Y, Z 좌표들은 존재하지 않는다. 그래서 그래픽 알고리즘들은 3차원 자표를 2차원 평면에 "평평하게" 만들 책임이 있다. 이 과정을 3차원 투영(3D Projection)이라고 한다.

  모든 점들이 3차원에서 2차원으로 변환되며, 일반적인 2D 선 그리기 기능으로 점들은 연결한다. 말 그대로 이것을 와이어 프레임 렌더링(WireFrame Rendering)이라고 한다. 만약 젓가락으로 사각형(큐브)을 만들고 그것에 손전등을 비춘다면, 벽에 생긴 그림자(투영도)는 평면이다. 이때, 큐브를 회전시키면, 이 그림자가 평면 투영일지라도, 우리들은 그것이 3차원의 물체라는 것을 알 수 있다.

  3D투영에는 여러 가지 유형이 있다. 먼저 정사영(OrthoGraphic Projection)이 있다. 예를 들어, 큐브의 평행한 변이 투영에서 평행하게 나타난다.

  그렇지만 실제 3D 세계에서 평행선은 관찰자로부터 멀어질수록 한 점에 수렴한다. 이러한 유형의 3D투영을 투시 투영(Perspective Projection)이라고 한다. 두 투영은 같은 과정이지만 단지 다른 수학을 사용한다.

 

  큐브같이 간단한 모양은 직선으로 쉽게 정의된다. 그러나 더 복잡한 모양을 나타내기 위해, 3D 그래픽에서는 폴리곤(Polygon)이라고 불리는 삼각형들이 나타내기 더 좋다.

  위와 같이 폴리곤들의 무리를 메쉬(Mesh)라고 한다. 메쉬의 밀도가 높을수록 곡선이 매끄러워지고 디테일은 더 세밀해진다. 그러나 폴리곤의 수를 늘리는 것은 컴퓨터에게 더 많은 부하를 주는 것을 의미한다. 따라서 게임 디자이너들은 모형 충실도(Model fielity)와 폴리곤 개수 사이에서 조심스럽게 균형을 맞춰야 한다. 폴리곤의 수가 너무 높아지면, 애니메이션의 프레임 속도가 사용자가 부드럽게 인식하는 것보다 떨어진다. 이러한 이유로 메쉬 단순환 알고리즘이 존재한다.

 

  폴리곤이 사각형 혹은 다른 복잡한 모양이 아니고 삼각형이 사용된 이유는 삼각형이 단순하기 때문이다. 3점은 공간에서 평면을 명확하게 정의한다. 만약 3D 공간에서 점 3개를 준다면, 점들을 통해 딱 하나의 평면만을 그릴 수 있다. 이것은 4개 이상의 점이 있는 도형에 대해서는 보장할 수 없다. 또한 2개의 점은 평면을 정의하는데 충분하지 않다. 그래서 3개의 점이 가장 완벽한 최솟값이다.

 

  와이어 프레임 렌더링은 고전적이지만, 3D 그래픽을 채울 수 있다. 이를 하기 위한 스캔 라인 랜더링(ScanLine Rendering)이라 불리는 고전적인 알고리즘이 1967년에 유타 대학에서 처음 개발됐다. 간단히 예를 들어, 하나의 폴리곤만 고려하여 이 폴리곤이 어떻게 컴퓨터 화면에 채워진 픽셀로 변환하는지 알아보자.

  먼저 채울 픽셀의 격자를 오버레이 한다. 스캔 라인 알고리즘은 폴리곤을 만드는 세 점을 읽고 Y좌표의 최대, 최솟값을 찾는 것부터 시작한다. 이 알고리즘은 두 점 사이의 줄만 고려한다. 

  이다음, 알고리즘은 한 번에 한 줄씩 작업한다. 각 행에서 행의 중심을 통과하는 선이 폴리곤의 모서리와 교차하는 위치를 계산한다. 폴리곤들이 삼각형이기 때문에 만약 한 선분을 가로지른다면 다른 한 선분도 지나가게 된다.

  스캔 라인 알고리즘은 이 두 교차점 사이의 픽셀을 채우는 것이다. 이렇게 Y좌표의 최대, 최솟값 사이에 있는 모든 행을 한 줄, 한 줄 계속 작업한다. 이게 스캔 라인 랜더링이라고 불리는 이유이다.

  이렇게 컴퓨터가 폴리곤을 채우는 속도를 필레이트(Fill Rate)라고 한다.

 

  3D 그래픽에서 "계단현상(Jaggies)"으로 알려진 거친 모서리를 볼 수 있다. 작은 픽셀을 사용할 때에는 이러한 현상이 덜 나타나지만, 특히 저사양 게임에서 이러한 현상을 자주 봤을 것이다.

  이 현상을 부드럽게 하는 한 가지 방법은 안티 앨리어싱(Anti Aliasing;안티 에일리어싱;AA)이다. 같은 색으로 폴리곤의 픽셀 채우기 대신 폴리곤이 각각의 픽셀을 얼마나 잘랐는지에 따라 색상을 조정할 수 있다. 픽셀이 완전히 폴리곤 안에 있는 경우에는 완전히 그 색으로 칠해질 것이다. 그러나 폴리곤이 픽셀을 살짝 스치는 경우, 그 부분은 더 밝은 음영으로 칠한다. 이렇게 가장자리를 부드럽게 하거나 흐리게 처리하는 것을 페더링(fethering)이라고 한다. 이것은 훨씬 보기 좋아진다.

  안티앨리어싱은 2D 그래픽을 포함하여 글꼴 및 아이콘과 같은 곳에 널리 사용된다. 만약 브라우저를 확대해보면 모든 글꼴이 안티앨리어싱 된 것을 볼 수 있다.

 

  3D 장면에서 물체에 일부만 보이는데, 어떤 물체는 다른 물체 뒤에 감추어져 있기 때문이다. 이를 폐색(Occlusion)이라고 한다. 이를 처리하는 가장 간단한 방법은 정렬 알고리즘을 사용하는 것이다. 그 장면에서 가장 먼 곳에서부터 가장 가까운 곳까지 순서대로 렌더링해 모두 정렬하는 것이다. 이를 화가 알고리즘(Painter's Algorithm)이라고 한다. 화가가 배경으로 시작해서 전경 요소까지 점점 나아가며 작업하는 것과 비슷하기 때문이다.

  세 개의 폴리곤이 겹치는 장면을 가정해보자. 단순함을 위해, 폴리곤들이 화면에 평행하다고 가정하자. 3개의 폴리곤 A, B, C의 각 거리는 20, 12, 14이다. 화가 알고리즘의 첫 번째는 모든 폴리곤을 가장 멀리 있는 것부터 가까운 것으로 정렬하는 것이다. A - C - B.

  이제 이것들이 순서대로 있기 때문에, 스캔라인 랜더링을 사용해 한 번에 하나씩 폴리곤을 채운다. 가장 멀리 떨어져 있는 폴리곤 A부터 시작한다. 그리고 그다음으로 먼 폴리곤 C에 스캔라인 랜더링을 반복한다. 그리고 폴리곤 B에 대해 이 작업을 다시 반복한다. 이게 끝이다.

 

  페색을 다루는 또 다른 방법은 Z-버퍼링(Z-Buffering)이 있다. 이전과 같은 결과를 출력하지만 이전과는 다른 알고리즘이다. 폴리곤이 정렬되기 전 예제로 돌아가자. 이 알고리즘은 어떤 폴리곤도 정렬할 필요가 없고, 이전 알고리즘보다 더 빠르다. 즉, Z-버퍼링은 장면에서 모든 픽셀에 대해 폴리곤까지 가장 가까운 거리를 추적한다. 메모리의 값을 나타내는 행렬인 Z-버퍼를 유지함으로써 이 작업을 수행한다.

  처음에는 모든 픽셀이 무한대로 초기화된다. 그럼 다음 Z-버퍼링은 리스트 안의 첫 번째 폴리곤 A부터 차례대로 시작한다.  스캔 라인 알고리즘과 동일한 로직을 사용하지만 픽셀 단위로 색상을 지정하는 대신, 폴리곤의 거리와 Z-버퍼 안에 기록된 거리를 검사하여 두 값 중 낮은 값을 기록한다. 거리가 20인 폴리곤 A가 무한대보다 낮으므로 폴리곤 A의 영역이 20으로 기록된다. 폴리곤 A가 끝나면 목록에 있는 다음 폴리곤으로 넘어가 같은 일을 반복한다. 폴리곤을 정렬하지 않았기 때문에, 이후의 폴리곤이 항상 높은 값을 덮어쓰는 것은 아니다. 폴리곤 C의 경우에는 폴리곤 B가 더 낮은 값이므로 Z-버퍼의 값 중 일부만 거리를 기록한다.

  완성된 Z-버퍼는 스캔 라인 랜더링의 멋진 버전과 함께 사용되어 라인 교차점을 테스트할 뿐만 아니라, 그 픽셀이 최종 장면에서 보이는지 확인하기 위한 룩업도 한다. 만약 보이지 않는다면, 알고리즘은 그것을 건너뛰고 다음으로 진행할 것이다.

  흥미로운 문제는 두 개의 폴리곤이 같은 거리에 있는 상황이다. 만약 A, B 둘 다 거리가 20이면 어느 것이 위에 올라와야 할까? 폴리곤은 끊임없이 메모리 안에서 섞이고 그것들의 액세스 순서를 변경하고 있다. 또한 반올림 오류는 부동 소수점 연산에 내재되어 있다. 그래서 어느 것이 위에 그려지는지 종종 예측할 수 없다. 그 결과 Z-Fighting이라 불리는 깜빡이는 글리치(Glitch) 현상이 생긴다.

 

  3D 그래픽의 또 다른 일반적인 최적화 중 Back-Face Culling이 있다. 생각해 보면, 삼각형은 앞뒤로 두 면을 가지고 있다. 아바타의 머리 또는 게임 속의 그라운드는 바깥을 향한 한쪽 면만 볼 수 있다. 따라서 처리 시간을 절약하기 위해 폴리곤의 뒷면은 종종 랜더링 파이프 라인에서 무시된다. 그리고 이것은 고려해야 할 폴리곤 면의 수를 반으로 줄여준다. 사용자가 객체 속으로 들어가게 하는 버그가 있을 때를 제외하고 이것은 굉장한 것이다. 만약 객체 속으로 들어가게 된다면 아바타의 머리나 바닥이 보이지 않게 된다.

 

  다음으로 쉐이딩(Shading)이라고 알려진 조명에 대해 이야기할 필요가 있다. 3D에서 조명은 물체의 표면에 따라 달라져야 한다. 모든 폴리곤에서 스캔라인 랜더링 색칠을 하면

이전에 주전자 메쉬는 이렇게 된다. 3D 같아 보이지 않는다. 현실성을 높이기 위해서는 조명을 추가해야 한다. 예를 들어, 폴리곤의 3곳의 다른 부분을 선택할 것이다. 이전 예와 달리 이제는 이 폴리곤들이 3D 공간에서 어떻게 방향을 잡을지 고려할 것이다. 이 폴리곤들은 더 이상 화면에 평행이 아니고, 다른 3D 방향으로 기울어져 있다. 이 폴리곤들이 마주하는 방향을 표면 법선(Surface Normal)이라고 부르며, 다음과 같이 폴리곤 표면에 수직인 작은 3D 화살표로 방향을 시각화할 수 있다.

  왼쪽 위에 광원을 추가하면 각 폴리곤은 다른 양으로 조명되어 일부는 더 밝게 나타날 것이다. 왜냐하면 폴리곤들의 각도는 보는 사람 쪽으로 더 많은 빛이 반사되게 하도록 하기 때문이다.

  3개의 폴리곤 중 가장 아래쪽의 폴리곤은 광원에서 멀리 떨어져 아래쪽으로 기울어져 있다. 이것은 어두운 색이 될 거라는 걸 의미한다. 비슷한 방법으로, 가장 오른쪽의 폴리곤은 약간 멀리 떨어져 빛을 향하고 있으므로 부분적으로 조명될 것이다. 마지막으로, 왼쪽 위에 폴리곤은 빛을 광원으로부터 우리의 시선 쪽으로 빛을 반사시킬 것임을 의미한다. 그래서 그것은 밝게 보일 것이다.

  모든 다각형에 대해 이렇게 하면, 주전자는 훨씬 더 현실적으로 보이게 될 것이다. 이 접근법을 플랫 쉐이딩(Flat Shading)이라고 하고, 가장 기본적인 조명 알고리즘이다. 안타깝게도 이 알고리즘은 모든 폴리곤의 경계들이 눈에 띄도록 만들고 메쉬가 부드럽게 보이지 않는다.

  이런 이유로, Gouraud Shading이나 Phong Shading과 같은 더 발전한 조명 알고리즘이 개발되었다. 폴리곤을 하나의 색상만으로 색칠하는 대신에 표면 전체의 색상을 영리한 방법으로 변화시켜 더 좋은 결과를 얻을 수 있다.

 

  3D 그래픽에 대해서 텍스처(Texture)에 대해 언급할 필요가 있다. 이는 그래픽에서 느낌보다는 표면의 표정을 의미한다. 조명과 마찬가지로, 멋진 효과가 있는 많은 알고리즘이 있다. 가장 간단한 방법은 텍스처 매핑(Texture Mapping)이다.

  이 과정을 시각화하기 위해, 단일 폴리곤으로 돌아가 보자. 스캔라인 랜더링을 이용해서 이것을 채울 때, 메모리에 저장된 텍스처 이미지에 따라 모든 픽셀에서 사용할 색상을 찾을 수 있다. 이렇게 하려면, 폴리곤의 좌표와 텍스처의 좌표 사이에 매핑(Mapping)이 필요하다. 텍스처링 알고리즘은 메모리에서 텍스처를 참조하여 매핑된 영역의 평균 색을 따라 폴리곤을 채운다. 이 과정은 폴리곤 안에 있는 모든 픽셀에서 반복한다. 이게 바로 텍스처를 얻는 방법이다.

 

  수백만 개의 폴리곤으로 구성된 큰 장면을 렌더링 하는 것은 상당한 계산량을 필요로 한다. 하지만 그것은 수백만의 폴리곤에 걸쳐 반복적으로 수행되는 동일한 유형의 연산이다. 예를 들자면, 스캔라인 랜더링, 안티앨리어싱, 쉐이딩 그리고 텍스처링과 같은 연산들이다.

  그러나 이들을 훨씬 빠르게 만드는 몇 가지 방법이 있다. 우선, 특정 유형의 계산을 위한 여분의 벨과 호루라기를 가진 특별한 하드웨어를 갖춤으로써 속도를 높여 번개의 속도로 일을 처리할 수 있다. 둘째, 3D 장면을 여러 작은 부분으로 나누고 그것들을 병렬로 랜더링 할 수 있다.

  CPU는 이를 위해 설계되지 않아서, 특별히 빠르지 않다. 그래서 컴퓨터 기술자들은 그래픽 전용 프로세서인 GPU(그래픽 처리 장치;Graphics Processing Unit)를 만들었다. GPU는 그래픽 카드 내부에서 찾을 수 있다. 또한 그래픽을 위한 램이 예약되어 있다. 이는 모든 메시와 텍스처가 있는 곳이며 여러 개의 다른 코어로 동시에 매우 빠르게 액세스 할 수 있다.

  GeForce GTX 1080 Ti 그래픽 카드는 3584개의 처리 코어를 가지고 있어서, 대규모 병렬 처리가 가능하다. 매 초마다 수억 개의 폴리곤을 처리할 수 있다. 

728x90
반응형

'컴퓨터공학 > 기초' 카테고리의 다른 글

월드 와이드 웹( WWW ; World Wide Web)  (0) 2022.10.30
컴퓨터 네트워크  (0) 2022.10.09
그래픽 유저 인터페이스(GUI)  (0) 2022.09.24
화면 및 2D 그래픽  (0) 2022.09.18
키보드 & 명령 라인 인터페이스(CLI)  (0) 2022.09.12

댓글