2018년 1월 12일 금요일

Understanding Graphics Concepts

Understanding Graphics Concepts


google document

본 글은 directxtutorial.com 의 글을 번역 또는 편집하고 약간 쉽게 설명한 글입니다.
보다 자세한 부분은 다음 링크를 참조하기 바랍니다.


3D 프로그래밍을 하는데 있어서 기본적인 기술과 보다 발전된 기술을 이해하고 숙지 해야 한다. 자신이 직접 작성한 엔진이든, 어디서 가져온 엔진이든, 또는 가져온것을 수정해서 사용하거나, 상용화된 엔진을 구입해서 사용 한다고 하여도, 3D Graphics Concepts 의 이해는 매우 중요한 부분중 하나이다.
이 글에서는 Direct3D를 살펴보면서 이러한 설명을 하고자 한다. 그러므로 이 글은 지극히 기술적인 글이 될 것이다.


이러한 기술적 이해는 우리가 실제로 Direct3D 를 이용하여 프로그래밍 하고 이해하는데 있어서 믿거름이 될것이라 생각한다.


Graphics Hardware
그래픽 프로그래밍을 잘 하기 위해서는 그래픽 하드웨어를 잘 이해하는 것이 필요하다.
Direct3D는 게임 프로그램에 많이 사용하지만 실제로는 게임 플렛폼이 아니다는 것을 알아주기 바란다.


Direct3D를 사용하여 작동되는 프로그램의 모든 코드는 실제로 그리픽 하드웨어 자체를 다루는 것이기도 하다. 그러므로 그래픽 하드웨어에 대한 지식이 없이는 Direct3D 기술은 사용할 수 없다고 생각하면 될 것이다.


그리픽 프로그램에 관심을 갖는다는 것은 바로 GPU 즉 Graphics Processing Unit 에 관심을 갖는것이라 할 수 있다.


사실 GPU는 CPU와 구조적으로는 다르다고 볼 수 있지만, 그 사용에 있어서는 기본적으로 갖다.


CPU는 계산을 수행하고 컴퓨터 시스템의 모든 명령을 처리 한다. 이에 반해, GPU 역시 계산을 수행하지만 이는 컴퓨터가 아닌 그래픽에 관한 계산이며 그래픽을 모니터에 출력하기 위한 명령을 처리하는것이 다르다.


다음 그림은 CPU가 GPU를 관리하는 관계를 보여주고 있다.




위 그림에서 보아 알수 있듯이, GPU는 완전히 독립된 프로세서(processor) 인 것이며 이는 개념적인 부분 뿐만 아니라 물리적 하드웨어로서 완전한 독립체이다.


다음 그림은 컴퓨터의 메인 메모리와 이와는 별도로 분리된 비디오 메모리가 있다는 것을 표현하고 있다. 비디오 메모리는 컴퓨터의 마더보드에 배치된 것이 아니라 그래픽 하드웨어에 위치하는 것이다. 이는 GPU가 보다 빠르게 비디오 메모리에 접근할 수 있다는 것을 말한다.


비디오 메모리는 현재의 모니터에 나타나는 그림이 저장되는 곳이다. 즉 모니터는 비디오 메모리에 저장된 그림을 보여주는 역할을 수행하는 것이다. 또한 비디오 메모리에 저장된 정보는 현재의 이미지 뿐만 아니라 다음에 보여줄 이미지를 준비하기 위한 각종 데이터를 포함한다.




이러한 하드웨어적 구조에 있어서 우리는 많은 것을 관리해야 한다. 이는 시스템 메모리 또는 비디오 메모리에 저장된 데이터들을 관리 해주어야 한다는 것이지만 ,  Direct3D 가 이러한 과정의 대부분을 처리해줄 것이다.
DirectX Graphics Infrastructure (DXGI)


DXGI 는 기존의 GDI 개념에만 집중하여 그래픽 관련 프로그래밍을 했던 사람이 Direct3D 를 처음 접하게 되는 싯점에 많은 궁금증을 갖게 되는 부분중 하나이다.


이러한 상태에서 처음으로 이해해야 하는 부분은 바로 DXGI 부분일 것이다. 이를 이해 하는 것이야 말로 최신의 Direct3D 기술에 대한 가장 기본적인 지식을 알게 되는 것이다.


DXGI가 하는 역할은 바로 기본적인 작업을 처리 하는 것이다. 이러한 기본적인 작업은 “화면에 이미지를 표시하고, 모니터 또는 비디오카드가 처리할 수 있는 해상도는 얼마인가” 와 같은 아주 기본적인 임무인 것이다.


사실적으로 말해서 DXGI는 Direct3D의 한 부분은 아니다. DXGI는 그래픽 구성요소의 기초가 되며 Direct3D와 하드웨어간 인터페이스 역할을 수행한다는 것이다.


위 그림에서 Application 과 Hardware 를 살펴보면,  Application 에서 Hardware까지 가는 길은 최종적으로 DXGI를 통해서 이루어 진다는 것을 알 수 있다.
우리는 Application을 작성하는 프로그래머이다. 이러한 측면에서 우리는 Hardware에 직접적으로 접근할 수 없음을 알수 있으며, 또한 DXGI 역시 직접 접근할 수 없음을 알 수 있다. 사실 DXGI를 직접 처리할 수 있는 방법이 없는것은 아니지만, 우리는 이러한 방법에 대해서는 설명하지 않을 것이다.
우리는 Direct3D에 DXGI를 독점적으로 관리하는 부분이 있고, 이러한 방법을 알고 있는 것이 더 중요하다.

The Swap Chain


GPU는 화면에 나타나는 그림에 대한 데이터를 버퍼 어레이를 지정하는 포인터를 갖고 있게 된며 이는 비디오 메모리에 위치하게 된다.


만약 우리가 3D 모델이나, 2D 이미지를 렌더링 할 경우, GPU는 이러한 정보를 모니터에 표시하기 위하여 비디오 메모리의 버퍼 어레이를 갱신하고 모니터로 전송하게 된다.


모니터는 화면의 가장 위에서부터 아래로 이러한 이미지를 그리게 되는 것이며, 이는  최근의 이미지는 새로운 이미지로 바뀌게 된다는 것을 말한다.


이러한 과정에서 모니터가 실시간적인 렌더링 속도를 따라가지 못할경우 사소한 문제가 발생하게 된다. 모니터의 갱신속도는 refresh rate라고 말하기도 한다.
일반적인 모니터의 갱신 속도는 60Hz 에서 100Hz 정도 된다. 쉬운말로 60Hz는 60 fps 라고 말할 수 있다.


fps = frame per second. 초당 처리할 수 있는 프레임수.


만약 모니터가 하나의 장면을 갱신하고 있는 사이에 새로운 rendering이 이루어 진다고 가정해보자. 그러면 화면의 그림은 기존의 그림과 새롭게 렌더링되는 그림으로 나누어질 것이다.  즉 화면의 윗쪽은 기존의 그림(old image)이 나타날 것이고 화면의 아래쪽은 새로 렌더링되는 그림(new image) 의 한 부분이 나타날 것이다.


우리는 이러한 현상을 “tearing” 이라고 부르고 있다.
우리말로 한다면… “화면이 찢어진다?” 정도 아닐까 한다. 사실 요즘의 기술에 의해 만들어지는 TV나 컴퓨터 모니터는 이런 현상을 결코 볼 수는 없다.


이러한 상황을 피하기 위하여 DXGI는 “swapping” 이라는 기술을 사용하고 있다.


그래픽 장치의 비디오 메모리는 다수의 버퍼를 갖고 있을 수 있다.


프론트 버퍼 (Front Buffer)
백 버퍼 (Back Buffer)


Swapping
새로운 이미지를 모니터에 직접 렌더링하는 대신 DXGI는 백 버퍼라고하는 픽셀의 보조 버퍼에 이미지를 그린다.


프론트 버퍼는 현재 디스플레이되고있는 버퍼 이다. 모든 이미지를 백 버퍼에 그린다. 그리기 작업이 완료되면 DXGI는 백 버퍼의 내용으로 프론트 버퍼를 업데이트하여 이전 이미지를 갱신한다.


그러나 이러한 기술을 사용한다 하더라도 그 확율은 줄어 들 수 있으나 GPU의 처리 속도가 모니터의 처리속도보다 매우 빠를 경우 티어링 현상은 나타날 수 있다는 것이다. 이는 백 버퍼의 내용을 프론트 버퍼로 갱신 하는 과정에서 나타날 수 있다는 것이다.


역시, 이러한 현상을 극복하기 위하여 DXGI는 각 버퍼에 대한 포인터를 스위칭 하는 방식으로 처리한다.
즉 메모리를 업데이트 하는 것이 아니라 프론트 버퍼의 메모리 위치(포인터) 를 백퍼버로 바꾸는 것이다. 그렇게 되면 기존의 프론트 버퍼는 백버퍼가 되고, 새로 옮겨간 백버퍼의 위치가 프론트 버퍼가 되는 것이다. 즉 메모리를 복사하여 업데이트 하는 것이 아니라 메모리의 위치를 상호 교차하여 바꾼다는 것이다.




이러한 주소 연산에 의하여 빠른 스위칭이 이루어 지면서 티어링 현상이 나타나지 않게 하는 것이다.


많은 게임 프로그램들이 보다 좋은 성능을 위하여 백버퍼를 추가적으로 준비하는 경우도 있다. 즉 프론트 버퍼는 하나이지만 백버퍼는 한개 이상을 둘 수 있다는 것이 된다.




이러한 설정을 스왑 체인이라고하며, 버퍼 체인이므로 새 프레임이 렌더링 될 때마다 위치를 바꾸게 된다.


The Rendering Pipeline


그리팩 프로그래밍을 하면서 우리가 가장 많이 듣는 말중 하나가 “Rendering pipeline” 일 것이다. 그 이유는 그림을 그리는 모든 과정이 여기에서 이루어 지기 때문일 것이다.


컴퓨터에서 “그림(이미지)을 그려낸다” 라는 것을 우리는 전문 용어로 “렌더링” “Rendering” 이라 부른다.


즉 모든 렌더링 작업은 이 파이프라인에서 이루어 지며, 이러한 렌더링 파이프라인은 화면에 나타나는 3D 이미지를 생산하는 곳이라 생각하면 된다.


이러한 렌더링 파이프라인은 공장의 조립라인하고 유사하다. 즉 하나의 단계가 끝나면 다음 단계로 진행하고 , 이러한 처리가 연속적으로 이루어 진다는 것이다.


이러한 각각의 단계는 GPU 에서 실행되어지고 처리 된다는 것을 알기 바란다.




위의 그림은 렌더링 파이프라인이 그리고자 하는 데이터(Raw Data)와 최종 이미지(Final Result) 사이에서 체인 형식으로 연결 되어 있다는 것을 보여 준다.


우리는 렌더링 파이프라인의 모든 스텝을 설명하지는 못할 것이다. 몇가지 단계만을 다룰 것이다.  이러한 모든 단계에는 그 시작이 있으며 시작 이후에 거치는 단계들로 진행할 수 있다.


이러한 단계들은 크게 다음의 3가지로 구분된다.


  1. Input-Assembler Stage
  2. Resterizer-Stage
  3. Output-Stage


Input-Assembler Stage
이러한 단계의 시작은 “Input-Assembler Stage” 라고 부르며 파이프라인의 첫번째 단계가 된다.


이러한 첫번째 단계에서 할 일은 렌더링 하고자 하는 3D 모델링 데이터를 비디오 메모리로부터 읽어 오는 일이 될 것이다. 즉 이러한 모델 정보를 컴파일 하고 다음 단계의 렌더링을 위해 준비하는 과정을 말한다.


Resterizer Stage
“Resterizer Stage” 는 백버퍼에 그려지는 이미지의 위치를 결정하는 단계이다. 이 단계에서 그려지는 이미지의 구체적이고 정확한 픽셀과 그 색상을 결정하게 될 것이다.


Output Stage
Output Stage 는 파이프라인의 마지막 단계가 된다. 이 단계에서는 생산되는 최종의 이미지를 조립하는 단계이다. 즉 각각의 개별적인 모델 이미지들을 결합하여 하나의 이미지를 만들어 내고 이를 백버퍼에 올바르게 배치하는 일을 수행한다.


다음 그림은 Direct3D 10에서의 모든 Stage를 보여주는 그림이다.


*초보적인 지식으로서는 다 이해하지 못할 수 있으므로 그냥 참고용으로 보기 바란다.




참고적으로 OpenGL 의 Rendering Pipeline에서 이루어 지는 일의 순서는 다음과 같다.
이는 OpenGL Wiki 에 기술되어진 글이다.


The OpenGL rendering pipeline works in the following order:
    1. Each vertex is acted upon by a Vertex Shader. Each vertex in the stream is processed in turn into an output vertex.
    2. Optional primitive tessellation stages.
    3. Optional Geometry Shader primitive processing. The output is a sequence of primitives.
  1. Vertex Post-Processing, the outputs of the last stage are adjusted or shipped to different locations.
    1. Transform Feedback happens here.
    2. Primitive Clipping, the perspective divide, and the viewport transform to window space.
  2. A Fragment Shader processes each fragment. Each fragment generates a number of outputs.

The Coordinate Systems


좌표계, 좌표 시스템..


3차원을 계산하는 수학적 지식이 없이는 아마도 3D 프로그래밍을 할 수 없을 것이다.  그렇다고 대학에서 배운 대수학을 다시 들여다 보거나 할 정도는 아니지만, 3D 좌표계에 대한 개념과 그것이 작동하는 방식 및 자신이 구현할 수 있는 것들을 이해해야 할 것이다.


3D 렌더링을 이해하기 위해서는 몇가지 유형의 좌표계를 알아야 한다. 데카르트 좌표계라고 , 아마도 처음 들어 봤을 수도 있다.


The Cartesian Coordinate System : 데카르트 좌표계


데카르트 좌표계라고 하는것 보다 2D 좌표계라 부르는 것이 일반적으로 더 알아 듣기 쉬울 것이다.


특정한 하나의 점은 그 좌표계의 축에 대한 위치로 정의된다. 무언가가 얼마나 멀리 있는지 말하고 싶으면 정확한 숫자를 알려주면 된다. 만약 “우리는 12 미터를 걸었다” 라고 한다면 여기서 12 미터는 단일 축을 따라 나타난 거리일 것이다.
이 경우 우리는 0 이 우리의 출발점이 되며 축을 따라 12 미터 이동했다고 말할 수 있다.
이것이 1D 즉 1차원 좌표계이며 아래 그림이 이를 보여준다.




이러한 개념에 의하여 위의 그림을 보면 화면의 좌측 0 에서부터 오른쪽으로 이동한 거리이며 점점 멀어진다는 것을 알 수 있다.
만약 이동한 사람의 관점에서 본다면 어디서부터 출발했는가를 이야기 한다면 아마도 음수의 값으로 나타날 것이다.


만약 위의 걷는 사람이 90도 회전하여 다른 방향으로 걷기 시작한다면 어떻게 될까…
만약 그렇게 된다면 이 사람은 앞에서 걸을때 사용한 축이 아닌 전혀 다른 축(두번째 축) 을 따라 걷는다고 할 수 있고 다음 그림과 같이 새로운 축이 나타날 것이다.




이렇게 새로운 축이 나타나면 우리는 이들을 구분해야 하는 방법을 제공해야 한다.
이제 12 미터를 이동한 축은 x 축이라 부르고, 수직으로 이동한 부분을 y 축이라 부르기로 하자.


물론 새롭게 사용된 y 축 역시 그 출발점인 원점을 갖고 있다는 것을 알 수 있으며 그곳은 바로 x 축을 따라 이동하다 멈추고 방향을 바꾼 부분이 될 것이다. 이 위치역시 y 축에서 대하여 0의 값을 갖는 위치가 된다.


이제 우리는 두개의 축을 갖게 되었다. x-축과 y-축. 또한 각각의 축에 대한 출발 위치를 갖게 되었다.


이것이 바로 데카르트 좌표계이며 2D 직각 좌표계인 것이다.


이제 우리는 평면에 있는 어떤 점에 대하여 위치를 말할 수 있게 되었다.  그 방법은


(x positon, y position)


의 형태가 될 것이며 위의 그림에서는 (12,4) 가 됨을 알 수 있다.
즉 x 축으로 12, y 축으로 4 만큼 거리를 둔 위치 지정할 수 있게 되었다.


여기서 x 와 y 의 값, 12,4 라는 숫자를  “좌표” 라고 부를 것이며, x 와 y 가 각각 0 인 위치를 “원점 origin” 이라 부르게 될 것이다.


3D Coordinate System


3D 좌표계는 크게 다르지 않다. 우리가 앞에서 논의한 2차원 좌표계의 확장일 뿐이다.
데카르트 좌표계에서 “축 axis” 하나를 추가 하면 된다. 즉 세번째 축인 것이다.
이를 z-axis 라 부르게 될 것이며 이는 x 축과 y 축에 대하여 수직의 방향이 된다.
이를 3D 좌표계라 하며 다음 그림과 같다.




데카르트 좌표계와 마찬가지로, 3D 좌표계 역시 양의 방향과 음의 방향이 있다.
3D 좌표계는 (x,y,z) 형식으로 표현할 수 있으며 만약 (12,4,15) 라는 위치를 이야기 한다면 지표면 에서 x 축으로 12, y축으로 4, 여기서 하늘 방향으로 15 만큼 갔다는 것이다.
만약 (12,4,-15) 라고 한다면 하늘이 아닌 땅 속으로의 방향을 말하게 된다.


3D Geometry
이제 3D 좌표가 프로그래밍에 어떻게 사용되는 지를 살펴 보자. 하나의 3D 좌표는 3차원 공간에서 하나의 위치를 지정하게 된다. 이러한 방법을 사용하여 3D 모델이 될 정확한 위치의 배열을 구축할수 있다. 물론 복잡한 3D 모델이 될수록 많은 점의 정보가 필요하며 이러한 점이 많아 질 수록 메모리 공간을 많이 차지하게 될 것이다. 이러한 이유에서 그래픽 엔진은 3D 모델을 표현을 보다 쉽고 빠르게 지정할 수 있는 방법을 요구하게 된다.
이러한 방법은 “삼각형” 을 사용하는 것이다.


삼각형은 모든 수학 영역에서 매우 유용한 도형이다.  삼각형은 원의 넓이를 계산하는데 사용되기도 하고, 튼튼한 건물을 만드는데 사용되기도 하며, 특히 3D 이미지를 만드는데 사용될 수 있다.


우리가 이러한 목적에 삼각형을 사용하는 이유는 삼각형이 면을 구성하기 위한 가장 최소 단위가 되기 때문이다. 즉 삼각형은 꼭지점 3개를 지정하여 만들 수 있으며 이보다 적은 꼭지점을 사용하는 면은 없다.


결국 삼각형은 모든 형태의 면을 만들어 낼 수 있는 최소 단위가 된다.




위의 그림은 3차원 모델링을 삼각형들의 집합으로 표현될 수 있다는 것을 보여준다.
Direct3D (OpenGL역시)는  3D 모델을 생성할 때 삼각형의 이러한 유용한 특성을 사용하게 되며 삼각형을 결합하여 모든 모양을 만들어 낸다.


앞서 언급했듯이 삼각형은 3개의 꼭지점으로 구성한다.  꼭지점을 우리는 정점 (Vertex) 라 부르며 다수의 정점을 Vertex 의 복수형인 Vertices 라고 부르게 된다.


하나의 정점은 3D 공간에서 하나의 점을 지정한며 이는 3D 좌표값, x,y,z 로 표현될 수 있으지만, Direct3D 에서는 약간 더 추가된 내용이 포함되며, 이러한 점들을 표현하는데 있어서 다양한 특성을 포함하게 된다.


그래서 우리는 “3D 공간에서 정확한 점의 위치와 속성” 이라는 뜻으로 정점의 정의를 사용하고 있다.
삼각형은 세개의 꼭지점으로 구성하면 된다. 그러나 프로그램에서는 세개의 점의 순서도 살펴볼 필요가 있다. 일반적으로 우리는 시계 방향으로 세 점을 지정하게 된다.
이러한 점의 방향을 요구하는 것은 만들어진 평면의 앞과 뒤를 구분하기 위함에 있다.


이렇게 지정된 세개의 꼭지점은 평평한 표면을 형성하게 되며, 필요에따라 회전되고 텍스처링되고, 이동 배치 되며 수정될 수 있다.




위의 그림을 표현하기 위한 세개의 점은 다음과 같을 것이다.


x = 0, y = 5, z = 1
x = 5, y = -5, z = 1
x = -5, y = -5, z = 1


모든 정점의 좌표값중 z 축 값이 모두 1 인것을 알 수 있다. 3D 좌표계 이지만 우리는 위의 삼각형을 2D 로 보고 설명하는 것이 보다 편리하기 때문이다.
물론 z 값을 서로 다르게 변경할 수는 있다. 그러나 삼각형이라는 근본적인 문제에 있어서 2D 이든 3D 이든 차이가 없다는 것을 알기 바란다.


원하는 3D 객체를 만들기 위하여 우리는 이러한 삼각형들을 서로 이어 붙여서 조립할 필요가 있다. 간단한 예로, 큐브(6면체) 의 한 면은 두개의 삼각형을 배치하여 만들 수 있다. 이런 방식으로 규브의 모든 면을 삼각형으로 구성할 수 있다는 것이다.


게임 같은 3D 프로그램에서 이런 방식으로 모든 삼각형을 준비 한다는 것은 엄청난 일이 될 것이며 매우 복잡한 일이기 때문에 우리는 보다 편리한 방법을 사용하게 될 것이다.


모든 삼각형의 각 모서리를 정의 하는 대신, 각 꼭지점의 좌표와 정보를 포함하는 꼭지점 목록과 그 순서를 정하여 처리하게 된다.


Primitives
Primitives (프리미티브) 란 3D 환경에 존재하는 단일 요소를 말한다. 즉 복잡한 3차원 객체를 구성하는 작은 기본 단위 객체라 생각하면 된다.
예를 들면 삼각형, 직선 선분, 점, 기타 등등 ..


다음에 열거된 내용은 3D 객체를 만들기 위한 프리미티브를 결합할 수 있는 방법의 목록이다.


  1. Point Lists
  2. Line Lists
  3. Line Strips
  4. Triangle Lists
  5. Triangle Strips


Point List
Point list 는 화면에 개별 점으로 표시되는 정점 목록이다. 이것은 3D 스타 필드 렌더링, 점선 만들기, 미니 맵에 위치 표시 등의 작업에 사용된다.




Line List


Line Strips
Triangle Lists


Triangle Strips

프리미티브 특성
3D 공간에서 그려지는 프리미티브 객체는 양면을 갖고 있다. 이를 굳이 구분한다면 앞면과 뒷면이라 생각할 수 있다.  이러한 프리미티브들을 결합하여 우리는 복잡한 형태를 만들게 되며 이렇게 만들어진 형내는 닫혀진 형태, 즉 면들로 닫혀진 구조가 될 것이다.
우리는 이러한 구조적 객체의 내부를 볼 필요는 없다.
사실 특별한 경우 내부를 볼 필요도 있기도 하지만 이는 다른 영역의 삼차원 그래픽이 된다.
3차원 구조를 표현하는데 있어서 두가지 방식이 있다.


  1. Surface Modeling
  2. Solid Modeling


Surface 모델링일 경우(우리가 지금 다루고 있는 영역이다. 게임등등) 내부를 볼 필요가 없다. 그러나 자동차 엔진의 설계와 같은 물리적인 내부구조를 설계할 경우 Solid 모델링이 필요하며 이는 다른 차원의 기술이 된다.


우리는 여기서 Surface 모델링만을 다룬다. 그러므로 뒷면은 그릴 필요가 없다.




위 그림에서와 같이 정점의 순서가 시계 방향으로 배치 된다면 이것은 앞면(바깥면) 이 될 것이다. 오른쪽에서와 같이 그 반대 방향으로 순서를 정하게 되면 이는 뒷면이 된어 화면에 그려지지 않게 될 것이다.


그러나 다음 그림과 같이 두 방향 모두 그리게 할 수 있는 방법이 없지 않다는 것만 알아두자. 3D 엔진은 앞면의 방향성을 지정할 수 있기 때문이다.

댓글 없음:

댓글 쓰기

Vertex , 정점 정보의 확장

Vertex , 정점 정보의 확장 그동안의 설명에 있어서 우리는 VERTEX 정보에 대하여 3 차원의 위치 정보만 사용하였다. struct VERTEX {float x,y,z;}; 이제 이를 확장하여 색상을...