본문 바로가기

Game/Graphics

Learn OpenGL - Getting started : Coordinate Systems

link : https://learnopengl.com/Getting-started/Coordinate-Systems


Coordinate Systems


 마지막 튜토리얼에서 모든 정점을 변환 행렬로 변환해 행렬을 우리의 장점으로 활용할 수 있는 방법을 배웠다.


OpenGL은 각 정점 쉐이더가 실행된 후에 정규화된 장치 좌표로 표시될 모든 정점을 기대한다.


즉, 각 정점의 x, y, z좌표는 -1.0에서 1.0사이어야한다. 이 범위를 벗어나는 좌표는 보이지 않는다.


우리가 일반적으로 하는 일은 우리가 스스로 설정한 범위의 좌표를 지정하는 것이며 정점 쉐이더에서는


이 좌표를 NDC로 변환한다. 그런 다음 NDC 좌표를 rasterizer에 제공해 화면에서 2D 좌표/픽셀로 변환한다.



 좌표를 NDC로 변환한 다음 화면 좌표로 변환하는 작업은 일반적으로 객체의 정점을 여러 좌표 시스템으로


변환한 다음 최종적으로 화면 좌표로 변환하는 단계별 방식으로 수행된다.


몇 개의 중간 좌표계로 변환할 때의 장점은 특정 좌표계에서 몇 가지 연산/계산이 곧 명백해지기 쉽다는 것이다.


우리에게 중요한 5가지 좌표계가 있다.


    - Local Space (or Object space)


    - World space


    - View space (or Eye space)


    - Clip space


    - Screen space


 그것들은 마침내 우리의 정점들이 변형되어 파편들로 끝나는 다른 상태이다.



 공간이나 좌표계가 실제로 무엇인지 지금 당장은 혼란스러울것이다. 그래서 우리는 전체 그림과


각각의 특정 공간이 실제로 하는 것을 보여주는 것으로 이해할 수 있는 방식으로 설명할 것이다.






The global picture


 한 공간의 좌표를 다음 좌표 공간으로 변환하기 위해 모델뷰 및 투영 행렬이 가장 중요한 여러 변환 행렬을 사용한다.


우리의 정점 좌표는 먼저 로컬 공간에서 로컬 좌표로 시작한 다음, 월드 좌표-뷰 좌표-클립 좌표로


처리되고 마지막에는 화면 좌표로 끝난다. 다음 이미지는 프로세스를 표시하고 각 변환이 수행하는 작업을 보여준다.



1. 로컬 좌표는 객체의 로컬 원점을 기준으로 한 좌표이다. 그들은 당신의 객체가 시작하는 좌표이다.


2. 다음 단계는 local 좌표를 더 큰 세계와 관련된 좌표인 world-space 좌표로 변환하는 것이다.


   이러한 좌표는 world의 origin에 상대적으로 배치된 다른 많은 객체와 함께 world의 origin과 관련이 있다.


3. 다음으로 우리는 월드 좌표를 카메라 또는 뷰어의 시점에서 볼 때와 같은 방식으로 뷰 공간 좌표로 변환한다.


4. 좌표가 뷰 공간에 있으면 좌표를 클립하기 위해 좌표를 투영하려고한다. 클립 좌표는 -1.0 및 1.0 범위로 처리되고,


   어떤 정점이 화면에 나타날지 결정한다.


5. 마지막으로 우리는 -1.0과 1.0의 좌표를 glViewport에 의해 정의된 좌표 범위로 변환하는 뷰포트 변환이라고하는


   프로세스에서 클립 좌표를 화면 좌표로 변환한다. 그런 다음 결과 좌표가 rasterizer로 보내져 조각으로 변환된다.




  아마도 각각의 공간이 무엇을 위해 사용되는지 약간의 아이디어가 있을 것이다. 우리가 정점을 모든 다른 공간으로


변형시키는 이유는 일부 좌표계가 특정 좌표계에서 더 이해하기 쉽거나 사용하기 쉽기 때문이다. 예를 들어, 개체를


수정할 때 로컬 개체 공간에서 이 작업을 수행하는 것이 가장 적합하지만 다른 개체의 위치와 관련해 개체에 대한


특정 작업을 계산하는 것은 world 좌표에서 가장 적합하다. 원한다면, local 공간에서 클립 공간으로 가는 변환 행렬을


하나씩 정의할 수 있지만 유연성이 떨어진다. 아래에서 각 좌표계에 대해 더 자세히 설명하겠다.




Local space


 Local 공간은 객체에 로컬인 좌표 공간, 즉 객체게 시작되는 곳이다. 모델링 소프트웨어 패키지(ex: Blender)에서 큐브를


만들었다고 가정해보자. 큐브가 최종 응용 프로그램의 다른 위치에 있을지라도 큐브의 원본은 (0, 0, 0)일 것이다.


아마 당신이 만든 모든 모델은 초기 위치로 (0, 0, 0)을 가진다. 따라서 모델의 모든 정점은 로컬 공간에 있다.



 우리가 사용하고 있는 컨테이너의 꼭짓점을 0.0을 원점으로해 -0.5와 0.5 사이의 좌표로 지정되었다.


이것들은 로컬 좌표이다.





World space


 우리의 모든 객체를 응용 프로그램에서 직접 가져오면 우리가 원하는 것이 아닌 (0, 0, 0)의 world origin을 서로 쌓을 수 있다.


우리는 더 큰 세계 안에 그들을 배치하기 위해 각 객체에 대한 위치를 정의하고자 한다. 월드 공간의 좌표는 마치


게임 세계와 관련된 모든 정점 좌표이다. 이 좌표 공간은 개체가 변형된 방식으로 모든 장소에 흩어져있는 형태로


배치된다. 개체 좌표는 지역 공간에서 세계 공간으로 변환된다. 이것은 모델 행렬로 수행된다.



 모델 행렬은 객체를 변환, 크기 조정 및 또는 회전시켜 객체가 속한 위치/방향으로 세계에 배치하는 변형 행렬이다.


그것을 축소해 집을 변형시키는 것으로 생각해라. 그것을 교외의 도시로 translate하고, y축의 왼쪽으로 약간 회전시켜


이웃 house들과 깔끔하게 맞춘다. 앞의 튜토리얼에서 행렬을 생각해보면 컨테이너를 장면 전체에 일종의 모델 행렬로


배치할 수 있다. 우리는 컨테이너의 로컬 좌표를 scene/world의 다른 위치로 변형했다.





View space


 뷰 공간은 사람들이 일반적으로 OpenGL의 카메라라고 부르는 곳이다. (때로는 카메라 공간 또는 시각 공간이라고도 함)


뷰 공간은 월드 공간 좌표를 사용자 보기 앞에 있는 좌표로 변환한 결과이다. 따라서 뷰 공간은 카메라의 관점에서


본 공간이다. 일반적으로 translate 및 rotation을 결합해 장면을 translate/rotation해 특정 항목이 카메라의 전면으로


변형되도록한다. 이러한 결합된 변환은 일반적으로 world 좌표를 뷰 공간으로 변환하는 뷰 매트릭스 내에 저장된다.


다음 튜토리얼에서는 카메라를 시뮬레이션 하기 위해 뷰 매트릭스를 만드는 방법을 광범위하게 다룰 것이다.






Clip space


 각 정점 쉐이더가 끝날 때 OpenGL은 좌표가 특정 범위 내에 있고, 이 범위를 벗어나는 모든 좌표가 잘릴 것으로 예상한다.


클리핑 된 좌표는 버려지므로 나머지 좌표는 화면에 표시되는 조각으로 끝난다.


클립 공간에서 이름을 가져오는 위치이기도 하다.



 모든 가시적인 좌표를 -1.0과 1.0의 범위 내로 지정하는 것은 그리 직관적이지 않기 때문에 OpenGL에서 기대할 수 있는


자신의 좌표 세트를 지정하고 다시 NDC로 변환한다.



 정점 좌표를 뷰에서 클립 공간으로 변환하기 위해, 좌표의 범위를 지정하는 소위 projection matrix를 정의한다.


각 차원에서 -1000 및 1000이다. 그런 다음 투영 행렬은 이 지정된 범위 내의 좌표를 정규화된 장치 좌표 (-1.0, 1.0)로 변환한다.


이 범위를 벗어나는 모든 좌표는 -1.0과 1.0사이에 매핑되지 않으므로 클리핑된다. 투영 행렬에서 지정한 이 범위에서


x 좌표가 범위를 벗어나 NDC에서 1.0보다 높은 좌표로 변환되어 클리핑되므로 (1250, 500, 750) 좌표는 표시되지 않는다.

예를 들어 primitive의 일부만이 삼각형이 클리핑 볼륨 외부에 있다. OpenGL은 클리핑 범위 내에 맞게 하나 이상의 삼각형으로 삼각형을 재구성한다.

 투영 행렬이 생성하는 viewing box는 절두 원(frustum)이라고 불리고, 이 절두체 내부에서 끝나는 각 좌표는 사용자 화면에서 끝난다.


투영 행렬이 3D 좌표를 맵핑하기 쉬운 2D 좌표로 변환하는 장치 좌표로 투영하기 때문에 지정된 범위 내의 좌표를


2D 뷰 공간 좌표로 쉽게 매핑할 수 있는 NDC로 변환하는 전체 프로세스를 projection이라고 한다.



 모든 정점이 클립 공간으로 변환되면 원근 분리(perspective division)라고 하는 최종 작업이 수행되어 위치 벡터의


x, y, z 구성 요소를 벡터의 균질 w 구성 요소로 나눈다. perspective divisio은 4D 클립 공간 좌표를 3D 정규화된 장치 좌표로


변환하는 것이다. 이 단계는 각 정점 쉐이더가 끝날때 자동으로 수행된다.



 결과 좌표가 (glViewport의 설정을 사용해) 화면 좌표에 매핑되고 조각으로 바뀌는 단계 이후이다.



 뷰 좌표를 클립 좌표로 변환하는 투영 행렬은 두 가지 다른 형식을 취할 수 있다. 각 형식은 고유한 절두체를 정의한다.


직교 투영 행렬 또는 투시 투영 행렬을 만들 수 있다.





Orthographic projection


 직교 투영 행렬은 이 상자 외부의 각 정점이 잘리는 클리핑 공간을 정의하는 큐브와 같은 절두 상자를 정의한다.


직교 투영 행렬을 만들 때 눈에 보이는 절두체의 폭, 높이 및 길이를 지정한다. 직각 투영 행렬로 클립 공간으로 변환


한 후 이 절두체 내부에서 끝나는 모든 좌표는 잘리지 않는다. 절두체는 컨테이너처럼 보인다:



 절두체는 가시 좌표를 정의하며 너비, 높이 및 근거리 및 원거리 평면으로 지정된다.


가까운 평면 앞에 있는 좌표는 모두 잘리고, 멀리 평면 뒤에 있는 좌표에도 동일하게 적용된다. 직각 절두체는 각 벡터의


w 구성 요소가 변경되지 않았으므로 절두체 내부의 모든 좌표를 표준화된 장치 좌표로 직접 매핑한다.


w 컴퍼넌트가 1.0에 동일한 경우, perspective 분할은 좌표를 변경하지 않는다.



 직교 투영 행렬을 만드려면 GLM의 내장 함수인 glm::ortho를 사용한다:

glm::ortho(0.0f, 800.0f, 0.0f, 600.0f, 0.1f, 100.0f);

 첫 번째 두 매개 변수는 절두체의 왼쪽 및 오른쪽 좌표를 지정하고, 세 번째 네 번째 매개 변수는 절두체의 아래쪽 및 위쪽 부분을


지정한다. 이 4점을 사용해 근거리 및 원거리 평면의 크기를 정의한 다음, 다섯 번째 여섯 번째 매개 변수는 근거리 평면과


원거리 평면 사이의 거리를 정의한다. 이 특정 투영 행렬은 이러한 x, y, z 범위 값 사이의 모든 좌표를 정규화된 장치 좌표로 변환한다.



 직교 투영 행렬은 화면을 나타내는 2D 평면에 직접 좌표를 매핑하지만 실제 투영에서는 원근감을 고려하지 않으므로


비현실적인 결과를 초래한다. 그것은 우리에게 투시 투영행렬이 fix하는 것이다.





Perspective projection


 실생활에서 제공하는 그래픽을 즐기려면 훨씬 더 멀리 떨어져있는 물체가 훨씬 더 작게 보일 것이다.


이 별난 효과는 우리가 원근법이라고 부르는 것이다. 아래의 이미지에서 볼 수 있듯이


무한한 고속도로 또는 철도의 끝을 내려다볼 때 특히 눈에 띈다:



 보시다시피, 원근법 때문에 선들이 멀어질수록 선들이 일치하는 것처럼 보인다. 이는 effect perspective projection을 사용해


투영을 시도한 것과 똑같은 효과를 낸다. 투영 행렬은 주어진 절두체 범위를 클립 공간에 매핑하지만 뷰어에서 더 멀리


떨어진 꼭지점 좌표일수록 각 꼭지점 좌표의 w값을 조작하므로 이 w 요소가 높아진다. 일단 좌표가 클립 공간으로 변환되면


범위는 -w 에서 w이다. OpenGL은 가시적인 좌표가 최종 꼭지점 쉐이더 출력으로 -1.0과 1.0사이의 범위에 있어야하므로


일단 좌표가 클립 공간에 있으면 원근 분할이 클립 공간 좌표에 적용된다:



 정점 좌표의 각 구성 요소는 w 구성 요소로 나눠져 더 작은 정점 좌표가 주어지면 정점은 뷰어에서 멀리 떨어져 있다.


이것이 w 요소가 원근 투영에 도움이되기 때문에 중요한 요소인 또 다른 이유이다. 결과 좌표는 정규화된 디바이스 공간에 있다.



 perspective projection 행렬은 GLM에서 다음과 같이 생성될 수 있다:

glm::mat4 proj = glm::perspective(glm::radians(45.0f), (float)width/(float)height, 0.1f, 100.0f);

 glm::perspective가 하는 일은 보이는 공간을 정의하는 커다란 절두체를 다시 만든다. 절두체 바깥의 것은 클립 공간 볼륨으로


끝나지 않으므로 잘린다. 원추 절두체는 이 상자 안의 각 좌표가 클립 공간의 한 지점에 매핑되는 비 균일한 모양의 상자로


시각화 할 수 있다. 원근 절두체의 이미지는 다음과 같다.



 첫 번째 매개변수는 fov 값을 정의한다. fov 값은 뷰 필드를 나타내며 뷰 공간의 크기를 설정한다. 사실적인 뷰를 위해서는


보통 45도가 설정되지만 더 많은 doom-style의 결과를 얻으려면 더 높은 값으로 설정할 수도 있다.


두 번째 매개 변수는 뷰 포트의 너비를 높이로 나눈 값을 설정한다. 세 번째와 네 번째 매개 변수는 절두체의 near plane과


far plane을 설정한다. 우리는 일반적으로 근거리 거리를 0.1f, 원거리 거리를 100.0f로 설정한다.


가까운 평면과 먼 평면과 절두체 내부의 모든 정점이 렌더링된다.

Perspective 매트릭스의 가까운 값이 너무 높게 (10.0f와 같이) 설정 될 때마다 OpenGL은 카메라에 가까운 모든 좌표(0.0f ~ 10.0f)를 잘라내어 비디오 게임에서 익숙한 시각적 결과를 제공한다.

 orthographic 투영을 사용하는 경우 각 정점 좌표는 멋진 원근감 분할 없이도 클립 공간에 직접 매핑된다.

(여전히 perspective division을 수행하지만, w 구성 요소는 조작되지 않고 그대로 유지되므로 효과가 없다)


orthographic 투영은 perspective 투영을 사용하지 않으므로 멀리있는 사물은 더 작아 보이지 않아 이상한 시각적


출력을 낸다. 이러한 이유 때문에 orthographic 투영은 주로 2D 렌더링과 일부 건축 또는 엔지니어링 응용 프로그램에 사용된다.


여기에서는 정점이 원근감에 의해 왜곡되지 않는다. 3D 모델링에 사용되는 블렌더 (Blender)와 같은 응용 프로그램은 모델링을


위해 orthographic 투영법을 사용한다. 아래에서 블렌더의 두 프로젝션 방법을 비교해 보겠다:



 원근 투영을 사용하면 멀리있는 정점을 훨씬 더 작게 보일 수 있지만 orthographic 투영에서는 각 정점이 사용자와 동일한 거리를


가짐을 볼 수 있다.





Putting it all together


 앞서 언급한 각 단계에 대한 변환 행렬, 즉 model, view 그리고 projection 행렬을 만든다. 정점 좌표는 다음과 같이 클립 좌표로


변환된다:



행렬 곱셈의 순서가 반대로 됨을 유의해라. (오른쪽에서 왼쪽으로 행렬 곱셈을 읽어야한다) 결과 정점은 정점 쉐이더의


gl_Position에 할당되어야하며 OpenGL은 원근감 분할 및 자르기를 자동으로 수행한다.

And then?

정점 쉐이더의 출력은 좌표계가 clip-space에 있어야하는데, 이는 우리가 변환 행렬로 했던 것과 같다. 그런 다음 OpenGL은 클립 공간 좌표를 투시-분할해 정규화된 장치 좌표로 변환한다. 그런 다음 OpenGL은 glViewPort의 매개 변수를 사용해 정규화된 장치 좌표를 화면 좌표의 한 지점에 해당하는 화면 좌표에 매핑한다. 이 프로세스를 뷰포트 변환이라고 한다.

 이것은 이해하기 어려운 주제이므로 각 공간의 용도에 대해 아직 확실하지 않은 경우 걱정할 필요가 없다. 


아래에서는 이러한 좌표 공간을 실제로 사용할 수 있는 방법을 살펴보고 충분한 예제가 이 튜토리얼에 나와있다.





Going 3D


 이제는 3D 좌표를 2D 좌표로 변환하는 방법을 알았으므로 이제까지 보았던 절름발이 2D 평면 대신 실제 3D 객체로


객체를 표시할 수 있다.



 3D 드로잉을 시작하기 위해 먼저 모델 행렬을 만든다. 모델 행렬은 모든 오브젝트의 정점을 global world 공간으로 변환하기


위해 적용하고자 하는 translation, scaling 및 또는 회전으로 구성된다. 바닥에 놓여있는 것처럼 보이도록 x축에서 회전시켜


비행기를 조금 변형해보자. 모델 행렬은 다음과 같다.


glm::mat4 model;
model = glm::rotate(model, glm::radians(-55.0f), glm::vec3(1.0f, 0.0f, 0.0f)); 

 정점 좌표에 이 모델 행렬을 곱해 정점 좌표를 world 좌표로 변환한다. 바닥에 약간 있는 우리 비행기는 지구 세계에서


비행기를 대표한다.



 다음으로 뷰 매트릭스를 생성해야한다. 우리는 물체가 보이도록 (물체가 원점(0, 0, 0)에 있을 때) 장면에서 약간 후방으로


이동하려고 한다. 현장을 돌아다니며 다음을 생각해보자.


    - 카메라를 뒤로 이동하려면 전체 장면을 앞으로 이동하는 것과 같다.


 그것이 바로 뷰 매트릭스가 하는 것이다. 우리는 전체 장면을 반전시켜 카메라를 움직이기 원하는 위치로 이동시킨다.


우리는 뒤로 이동하기를 원하기 때문에 OpenGL은 right-handed 시스템이므로 z축의 양의 방향으로 이동해야한다.


장면을 음의 z축 방향으로 변환하면 된다. 이것은 우리가 뒤로 움직이고 있다는 느낌을 준다.

Right-handed system

관례상 OpenGL은 Right-handed 시스템이다. 이것은 기본적으로 양의 x축은 오른쪽으로, 양의 y축은 위로, 양의 z축은 뒤로 향하는 것이다. 화면은 3축의 중심이고 양의 z축은 화면을 통해 사용자쪽으로 향하게된다. 축은 다음과 같이 그려진다.
Right-handed를 이해하기 위해서는 다음을 수행해라:

    - 손을 위로 하고 y축을 따라 오른쪽 팔을 위로 뻗어라.

    - 엄지 손가락을 오른쪽으로 향하게 해라.

    - 손가락으로 가리켜라.

    - 이제 가운데 손가락을 90도 아래로 구부려라.

 올바르게 했다면 엄지 손가락은 양의 x축을 가리키고, 가리키는 손가락은 양의 y축을 향하고 가운데 손가락은 양의 z축을 향해야한다. 왼팔로 이 작업을 수행하면 z축이 뒤바뀌는 것을 볼 수 있다. 이것은 left-handed 시스템으로 알려져있고 일반적으로 DirectX에서 사용된다. 정규화된 장치 좌표에서 OpenGL은 실제로 left-handed 시스템을 사용한다.

 다음 튜토리얼에서 장면을 보다 자세히 이동하는 방법에 대해 설명한다. 현재 view matrix는 다음과 같다:

glm::mat4 view;
// note that we're translating the scene in the reverse direction of where we want to move
view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f)); 

 마지막으로 정의해야 할 것은 projection 행렬이다. 장면에 투시 투영법을 사용하기 때문에 다음과 같이 투영 행렬을 선언하자.

glm::mat4 projection;
projection = glm::perspective(glm::radians(45.0f), screenWidth / screenHeight, 0.1f, 100.0f);

 이제 변환 행렬을 만들었다. 우리는 이를 쉐이더에 전달해야한다. 


먼저, 변형 행렬을 정점 쉐이더에서 유니폼으로 선언하고 정점 좌표를 곱한다.

#version 330 core
layout (location = 0) in vec3 aPos;
...
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    // note that we read the multiplication from right to left
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    ...
}

 행렬을 쉐이더에 보내야한다. (변환 행렬이 많이 변경되는 경향이 있으므로 대개 각 반복을 수행한다)

int modelLoc = glGetUniformLocation(ourShader.ID, "model");
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
... // same for View Matrix and Projection Matrix

 이제 정점 좌표가 model, view, projection 행렬을 통해 변환되므로 최종 객체는 다음과 같아야한다.


    - 뒤쪽 바닥으로 기울어졌다.


    - 우리한테 조금 멀어졌다.


    - 원근법으로 표시된다.


 결과가 실제로 요구 사항들을 충족했는지 확인해라.







More 3D


 지금까지는 3D 공간에서도 2D 평면으로 작업했으므로 모험을 통해 2D 평면을 3D 큐브로 확장해보자.


큐브를 렌더링하려면 총 36개의 꼭지점이 필요하다. 36개의 꼭지점은 여기에서 확인해라.



 재미를 위해서 우리는 큐브가 시간이 지남에 따라 회전하도록하자:

model = glm::rotate(model, (float)glfwGetTime() * glm::radians(50.0f), glm::vec3(0.5f, 1.0f, 0.0f));  

  그리고 나서 우리는 glDrawArrays를 사용해 큐브를 그릴것이다. 하지만 이번에는 36개의 꼭지점이 있다.

glDrawArrays(GL_TRIANGLES, 0, 36);

 아마 다음과 비슷한 영상을 얻을 수 있을것이다:


 그것은 약간 cube를 닮았지만 어떠한 부분은 off 되어있다.


큐브의 일부면이 큐브의 다른면에 그려진다. 이것은 OpenGL에서 큐브를 triangle-by-triangle로 그릴 때 이전에 그려진


다른 것이 있더라도 픽셀을 덮어 쓰게 되므로 발생한다. 이 때문에 일부 삼각형은 겹치지 않는동안 서로 겹쳐서 그려진다.



 다행스럽게도 OpenGL은 z 버퍼라는 버퍼에 깊이 정보를 저장해 OpenGL이 픽셀을 그릴 때와 그렇지 않을 때를 결정할 수


있도록 한다. z 버퍼를 사용해 깊이 테스트를 수행하도록 OpenGL을 구성할 수 있다.





Z-buffer


 OpenGL은 모든 깊이 정보를 깊이 버퍼라고하는 z 버퍼에 저장한다. GLFW는 자동으로 출력 버퍼의 색상을 저장하는


버퍼를 가지고 있는 것처럼 버퍼를 자동으로 생성한다. 깊이는 각 프래그먼트 (프래그먼트의 z값)에 저장되고


프래그먼트가 색상을 출력하려고 할 때마다 OpenGL은 깊이 값을 z 버퍼와 비교하고 현재 프래그먼트가 다른 프래그먼트


뒤에 있으면 이를 버리고 그렇지 않으면 덮어쓴다. 이 과정을 깊이 테스트라고하며 OpenGL에 의해 자동으로 수행된다.



 그러나 OpenGL이 깊이 테스트를 실제로 수행하는지 확인하려면 먼저 OpenGL에 깊이 테스트를 사용하도록 지정해야한다.


기본적으로 비활성화되어 있다. glEnable을 사용해 깊이 테스트를 할 수 있다. glEnable 및 glDisable 함수를 사용하면


OpenGL에서 특정 기능을 활성화/비활성화 할 수 있다. 그런 다음 해당 기능을 enable/disable 하도록 다른 호출이


이루어질 때까지 해당 기능을 enable/disable 하도록 설정한다. 지금 우리는 GL_DEPTH_TEST를 활성화해 깊이 테스트를


사용하고자한다:

glEnable(GL_DEPTH_TEST);  

 깊이 버퍼를 사용하기 때문에 각 렌더링 반복 전에 깊이 버퍼를 지우고 싶다. 

 (그렇지 않으면 이전 프레임의 깊이정보가 버퍼에 그대로 유지된다)


 색상 버퍼를 지우는 것처럼 glClear 함수에서 DEPTH_BUFFER_BIT를 지정해 깊이 버퍼를 지울 수 있다:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

 프로그램을 다시 실행하고 OpenGL에서 깊이 테스트를 수행하는지 확인해보자:







More cubes!


 스크린에 10개의 큐브를 표시하려고 한다고 가정해보자. 각 큐브는 동일하게 보이지만 각 회전마다 다른 위치에 있을 때만 다르다.


큐브의 그래픽 레이아웃은 이미 정의되어 있으므로 더 많은 객체를 렌더링 할 때 버퍼 또는 속성 배열을 변경할 필요가 없다.


각 객체에 대해 변경해야하는 유일한 것은 큐브를 세계로 변형시키는 모델 행렬이다.



 먼저, 월드 공간에서의 위치를 지정하는 각 큐브에 대한 변환 벡터를 정의하자. glm::vec3 배열에 10개의 큐브 위치를 정의한다.

glm::vec3 cubePositions[] = {
  glm::vec3( 0.0f,  0.0f,  0.0f), 
  glm::vec3( 2.0f,  5.0f, -15.0f), 
  glm::vec3(-1.5f, -2.2f, -2.5f),  
  glm::vec3(-3.8f, -2.0f, -12.3f),  
  glm::vec3( 2.4f, -0.4f, -3.5f),  
  glm::vec3(-1.7f,  3.0f, -7.5f),  
  glm::vec3( 1.3f, -2.0f, -2.5f),  
  glm::vec3( 1.5f,  2.0f, -2.5f), 
  glm::vec3( 1.5f,  0.2f, -1.5f), 
  glm::vec3(-1.3f,  1.0f, -1.5f)  
};

 이제 게임 루프 내에서 glDrawArrays 함수를 10번 호출하려고 한다. 하지만 이번에 렌더링하기 전에 매번 다른 모델 행렬을 정점


쉐이더에 보낸다. 우리는 게임 루프 내에서 다른 모델 행렬로 객체를 10번 렌더링하는 작은 루프를 생성한다.


우리는 또한 각 컨테이너에 작은 회전을 추가한다.

glBindVertexArray(VAO);
for(unsigned int i = 0; i < 10; i++)
{
  glm::mat4 model;
  model = glm::translate(model, cubePositions[i]);
  float angle = 20.0f * i; 
  model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
  ourShader.setMat4("model", model);

  glDrawArrays(GL_TRIANGLES, 0, 36);
}

 이 snippet코드는 새 큐브를 그릴 때마다 모델 행렬을 업데이트하고, 총 10번 수행한다. 지금 우리는 기이하게 회전된


10개의 큐브로 가득찬 세계를 조사해야한다.



완벽하다!





** ** ** ** ** ** ** ** ** **


이번 튜토리얼도 무사히 마쳤다. 유니티로 게임 개발할때 공부했던 좌표와 뷰에 관한 지식들을 다시 한 번 되새김질하는 좋은 단원이었다.


이제 저 스마일을 하도 많이 보니까 빡친다. 빨리 다른거로 넘어가서 스마일을 안 봐야지.