KoreanFoodie's Study
[OpenGL ES] 7강 : 래스터라이저(Rasterizer), Clipping, Back-face culling 본문
[OpenGL ES] 7강 : 래스터라이저(Rasterizer), Clipping, Back-face culling
GoldGiver 2023. 4. 16. 22:51![](https://blog.kakaocdn.net/dn/dlPOWp/btsahz5DPYY/9X74FKozJbiBTFBjWxgsoK/img.jpg)
이 강의는 유투브에 무료로 공개되어 있는 한정현 교수님의 컴퓨터 그래픽스 강좌를 정리한 글입니다. 자세한 내용은 강의를 직접 들으시거나 책을 구입하셔서 확인해 보세요. 강의 자료는 깃헙 링크에 올라와 있습니다.
요약 :
1. Rasterizer(래스터라이저) 는 5가지 처리를 수행한다 : Clipping, Perspective division, Back-face culling, Viewport transform, Scan conversion 이 그것이다.
2. 클리핑은 클립 공간에 걸친 폴리곤의 경우, 클립 공간을 벗어나는 폴리곤을 일부를 잘라내는 것을 의미한다. Perspective division 은 Clip 공간을 NDC 로 변환하며, Back-face culling 을 통해 카메라에서 보이지 않는 폴리곤을 걷어내고, Viewport transform 으로 NDC 를 Screen space 로 변환한다. 마지막으로 Scan conversion 을 통해 삼각형 폴리곤 내부에 있는 각 픽셀들의 attributes(normal, texture coordinates 등)을 보간해 준다.
3. Rasterizer 의 처리는 하드웨어에서 처리해 주며, 일반적으로 프로그래머가 개입하는 부분은 없다. 😉
래스터라이저(Rasterizer)
![](https://blog.kakaocdn.net/dn/k9qMD/btsae5LeXEX/6HHHwUGZoGfPWJYNXtKp1K/img.jpg)
래스터라이저는 크게 다섯 가지 작업을 수행한다. 이 강에서는 이 5가지 작업 각각에 대해 자세히 알아볼 것이다.
- Clipping
- Perspective division
- Back-face culling
- Viewport transform
- Scan conversion
![](https://blog.kakaocdn.net/dn/K3OjZ/btr97o55XlD/3NQuMJ3X3VoTfU1kxdVbH1/img.jpg)
위의 그림을 보자. t1 은 그냥 간단하게 전부 제외시키고, t2 는 모두 포함시키면 되지만, t3 같은 경우는 일부만 잘라내어야 한다. 그런데 이런 작업은 하드웨어가 잘 처리해 주므로, 여기서는 그렇게 자세히 다루지는 않겠다 😅
![](https://blog.kakaocdn.net/dn/OA6kY/btsaeTK6Wtc/LFP1aZkZYZF9HhRG3AnfYk/img.jpg)
위의 M(proj) 행렬의 4번째 행을 보면 값이 -1 임을 알 수 있다. 이 값이 1 이 아니라 -1 인 이유는 무엇일까?
일단, 위의 행렬로 만들어진 동차 좌표계를 다시 카테시안 좌표계(데카르트 좌표계)로 변환하려면, 각 원소를 -z 로 나누어 주면 된다.
![](https://blog.kakaocdn.net/dn/nsZE7/btr96OYbtS1/xnA9uFMYJcGnyDB3sfIru0/img.jpg)
위에서 fovy 를 보자. 이 값은, view frustum 의 원점 (0, 0, 0) 에서 y 축의 양/음의 축을 얼만큼의 각도만큼 담아낼 것인지를 의미한다. 아래를 보면...
![](https://blog.kakaocdn.net/dn/H70P7/btsaqPgwhpC/9BeNCpw5p9b2y2MCCkFLH0/img.png)
fovy 는 90도 이고 aspect 는 1 이므로, x 축으로도 양/음 축으로 90 도 만큼의 공간에 있는 오브젝트들이 담길 것이다.
![](https://blog.kakaocdn.net/dn/qq7Z8/btsajdCNDtK/6OAjM12n46g0L66xv3VMJk/img.png)
위의 그림을 보면, 분홍색 라인이 fovy 에 정해진 만큼의 각도를 표시한 것이고, n 이 1 이므로 -z 축 좌표에서 -1 이 되는 지점에 nearest plane 이 생길 것이며, farthest plane 은 f 가 2 이므로 -2 인 지점에서 정의될 것이다!
그런데 l1 과 l2 를 보자. 해당 행렬에 Projection Matrix 인 M(proj) 를 곱하면 결과가 다음과 같은데...
![](https://blog.kakaocdn.net/dn/EG0mg/btsatU9GXBa/gmCzN2yCF0KBWjUWEzNPFk/img.png)
위의 변환된 좌표들을 그려보면 다음과 같다 :
![](https://blog.kakaocdn.net/dn/bdPWPH/btsamXFJpAO/jkQUUepqMZ3K8akdrDeNck/img.png)
View Frustum 에서는 l1 과 l2 의 길이가 같았지만, Clipping Space 로 넘어오면서 l1' 과 l2' 의 길이가 달라졌음을 알 수 있다. 이런 식으로 변화된 이유는... P1 과 Q1 의 행렬에서 빨간색 동그라미 친 부분에서 보이듯, w 값으로 행렬의 각 요소값을 나누어 주었기 때문이다(동차좌표계에서 데카르트 좌표계로의 변환). 해당 절두체를 (-1, 1) 의 x 좌표값을 가지는 클립 공간으로 변환하는 과정에서 이런 일이 발생한 것이다.
그렇다면 l1' 과 l2' 의 길이가 다르다는 것은 어떤 의미를 가질까? 이것이 바로 원근법 구현의 간단한 원리이다!
P1 과 Q1 은 w 의 값이 -2 인데, 만약 원점으로부터 P1, Q1 에 비해 더 멀리 떨어진(예를 들어 w 값이 -3인 점 두개가 이루는 선분이 있다고 하자) 점 두개가 있다면... 해당 두 점이 이루는 선분의 길이는 클립 공간에서는 절두체에서의 원래 길이보다 3배 짧아질 것이다!
-z 로 각 원소를 나누는 이 과정을 perspective disvision 이라고 부르며, 그 결과는 NDC(Normalized Device Coordinates)에 있다고 부른다.
![](https://blog.kakaocdn.net/dn/bl1dow/btsae6pRLJu/2DbB8GQnHExH0tr1KccuNK/img.jpg)
위의 그림에서 카메라가 구를 그려낸다고 해 보자. 위에서 t1 삼각형의 경우, 카메라와 반대되는 방향의 normal vector 를 갖고 있는데, 이러한 폴리곤을 back face 라고 부른다. back face 인 폴리곤은 렌더링되지 않을 것이다(컬링). 반면 카메라를 바라보고 있는 폴리곤을 front face 라고 한다. 그렇다면 어떤 폴리곤이 back face 인지 front face 인지 어떻게 구분할까?
![](https://blog.kakaocdn.net/dn/tpHMl/btsakfUPGLw/JrbyXVnimhw2Agx5vjI3T0/img.png)
우리는 내적을 이용해 face 들을 구분한다. t3 의 경우, c3 벡터와 수직이므로 내적값이 0 이다. 이 경우를 edge-on face 라고 한다. 반면, 내적값이 양수이면 front face, 음수면 back face 가 된다.
그런데 camera space 를 clip space 로 변환하면 카메라가 각 물체까지 뻗어나가는 벡터가 모두 같게 된다(이전에는 c1, c2, c3 로 각각 달랐지만). 이를 universal projection line 이라고 하고, 해당 변환에 따라 t1, t2, t3 또한 normal 이 조금 달라진다. 즉, camera space 에서 내적을 취해 c1, c2 ,c3 를 각각 따로 쓰는 것보다, NDC 공간에서 universal projection line 한 개를 재활용하는 것이 더 효율적일 것이다.
![](https://blog.kakaocdn.net/dn/bcOvZV/btsaeYk9GCv/SNBVRRwka4lzR2U9VlJOo1/img.jpg)
아래 사진처럼, xyz 축의 원점에 눈이 있다고 가정해 보자.
그럼 t2 삼각형을 관찰했을 때는 가운데 삼각형 모양처럼 v1 이 오른쪽에 위치해 있을 것이다. 그러므로, v1, v2, v3 로의 방향은 반시계가될 것이다.
그런데 t1 의 경우, 사정이 조금 다르다. 마치 우리는 구 안에서 삼각형 t1 을 관찰한 것처럼 될 것이므로, t1 삼각형의 경우 v1 이 왼쪽에 위치하게 된다.
![](https://blog.kakaocdn.net/dn/qo79H/btsal4r0rhZ/co0dUBPv6C6GfTMIoczkXK/img.png)
위 경우, t2 삼각형에서 벡터 v1->v2 와 v1->v3 의 외적을 취하면(즉, 넓이를 구하면) 양수가 되고, t1 삼각형의 경우 음수의 값이 나온다. 즉, 우리는 front face 의 넓이(외적)은 양수, back face 는 음수가 나온다는 것을 알 수 있다. 아래가 해당 식을 보여준다.
![](https://blog.kakaocdn.net/dn/b5nTGV/btsamYkJ17b/LEkHJHsawZLyg5EhfD9wWk/img.png)
양수가 나온다는 것은 CCW(반시계 방향), 음수가 나온다는 것은 CW(시계 방향) 으로 vertex 방향이 정의되었다는 것도 기억해 두자. 행렬식이 0 이면 해당 폴리곤은 edge-on face 라는 뜻이다.
![](https://blog.kakaocdn.net/dn/bOeokM/btsagOBXqmS/aCngGkriMLliVhLvsL5VrK/img.jpg)
그런데 만약 반투명한 구를 렌더링해야 한다면, back face 라고 해서 무조건 culling 을 시키면 안된다. 왜냐하면 back face 의 일부를 사용자에게 보여 주어야 하기 때문이다.
따라서 OpenGL 에서도 이를 조절할 수 있다. glEnable 의 경우, 컬링을 할 것인지 설정하는 데 사용되고, glCullFace 시 어떤 face 를 culling 시킬지 인자를 세팅할 수 있다. 물론 기본값은 back face 이다.
![](https://blog.kakaocdn.net/dn/Q4f2M/btsahLkFAJL/51bpSRplAZXVwxZjYbwvRK/img.jpg)
glFrontFace 는 front face 의 vertex order 를 선택할 수 있게 해 준다. 기본값은 우리가 배웠듯이 반시계(GL_CCW)이다.
![](https://blog.kakaocdn.net/dn/qUWTF/btsaeYk9GAj/Ou4mNFRGwmifKZR0otP7mK/img.jpg)
컴퓨터 스크린에서, 윈도우는 window space 나 screen space 로 구성된다.
Viewport 는 씬이 투영되는 스크린에서의 공간을 정의한다. 여기에는 minX, minY, w, h 값이 필요한데, 각각의 값은 그림으로 보는 게 이해가 더 빠르다. 또한, 해당 스크린 공간은 사실 3D 이다. 즉, 깊이 값이 있다는 뜻이다!
![](https://blog.kakaocdn.net/dn/ejHAGh/btsaffGVyNt/U1ZRIQ6CaIy53ABeKSkxu0/img.jpg)
NDC 는 이전에 2 x 2 x 2 사이즈의 공간(정육면체)라고 배웠다. 빨간 색으로 강조한 것은 실수가 아니다.
위 그림은 NDC(Clip space) 에서 screen space 로의 변환을 보여주고 있다. scaling 과 translation 에 해당하는 행렬이 왜 저런 값을 가지는지는... 그냥 논리적으로 생각하면 된다. 그냥.. scaling 으로 공간의 크기를 바꾸고, minX, minY 에 해당하는 점을 맞춰준 것 뿐이다.
![](https://blog.kakaocdn.net/dn/BkRc3/btsaeoEAQs6/EdqsKLTggDsNkfRNOcLOO0/img.jpg)
우리가 이전에 배웠던 vertex shader 의 OpenGL 에서의 구현을 다시 살펴 보자. 우리는 이전에 vertex shader 가 clip space 를 Rasterizer 에게 넘겨 준다고 배운 바 있다.
Rasterizer 는 이 clip space 를 Perspective Division 을 통해 NDC 로 변환하고, 이를 다시 Viewport Transform 을 이용해 Screen space 로 변환한다.
그런데 v_normal 과 v_texCoord 를 보자. Rasterizer 는 아직 해당 두 정보를 건드리지 않았는데... Rasterizer 는 이 두 녀석을 어떻게 처리할까?
![](https://blog.kakaocdn.net/dn/pBKdz/btsagOIHoz5/qVJlGOcxkQxukJpkq9qou1/img.jpg)
Scan Conversion 은 각각의 삼각형을 frament 의 셋으로 분해한다. 이 과정을 통해 삼각형 안에 어떤 픽셀들 포함되는지가 결정되며, 각 픽셀의 normal 과 texture coordinates 가 보간(interpolate)되어 계산되게 된다. 어렵게 표현했지만 보간이라는 건 뭐.. 그냥 평균값을 내서 대충 적절한 값을 넣어줬다고 봐도 좋다 😅
위의 삼각형에서 색상값들이 각 위치에 따라 어떻게 표현되는지를 보면 "interpolation across a triangle" 이 어떤 의미인지 확 와닿을 것이다 😆
![](https://blog.kakaocdn.net/dn/oC4AI/btsaeWU8U6s/T82LHJibeGQBF6GfepIvAK/img.jpg)
즉, 좌표 뿐만 아니라 RGB 색상같은 attributes 들도 쉽게 보간이 된다. 이는 고등학교... 아니 중학교 수준의, 선분의 내분 같은 개념이니 간단히 넘어가겠다.
![](https://blog.kakaocdn.net/dn/bjekzP/btsacgs8gkr/QSHKamrk6idLOdFUsihCmK/img.jpg)
위의 그림을 잠깐 보자. p 를 삼각형 내부의 임의의 한 점이라고 가정하고, u, v, w 를 각 꼭짓점 v1, v2, v3 (반대되는 면) 에 얼마나 치우처져 있는지를 의미하는 무게(weight) 값이라고 가정하자. 그럼 삼각형을 아래와 같이 표현해 볼 수 있다.
![](https://blog.kakaocdn.net/dn/yrkMg/btsaESqnnVR/84bZmVltFErkgjWkHWAkzk/img.png)
또한 u, v, w 는 원본 삼각형에서 각각 부분 삼각형의 넓이의 '비' 이므로, 합은 1 이 될 것이다.
한가지 신기한 성질은, 우리는 v1, v2, v3 좌표의 값을 알고 있으므로 u, v, w 값을 조절해 임의의 점 p 를 찍을 수 있고, 해당 점 p 의 값과 나머지 두 꼭짓점을 외적함으로써 각 부분 삼각형들의 넓이를 구할 수 있다는 것이다.
![](https://blog.kakaocdn.net/dn/ehKHqT/btsaewoUCLA/mB91o7ZOmnJtkHkBS6l6Ck/img.jpg)
(u, v, w) 로 만들어지는 p 의 좌표계는 v1, v2, v3 에 대해 barycentric coordinates 를 갖는다고 부른다. 좌표계라고 해서 거창한 것은 아니고, p 의 x, y, z 좌표계가 v1, v2, v3 꼭짓점의 값과 u, v, w 를 이용한 식으로 표현된다고만 이해하면 된다.
어쨌듯, 우리는 barycentric coordinates 를 이용해 임의의 점 p 에 대한 보간값(그것이 좌표이든 RGB 색상 같은 다른 정보이든 간에)을 세 꼭짓점에 대해 계산할 수 있게 되었다!
![](https://blog.kakaocdn.net/dn/Jdi6A/btsafQ1abEw/prWwdPBqn7AKixqZOKdlIk/img.jpg)
vertex attributes 는 RGB 색상을 포함하지 않는다. 그런데 대부분의 경우, normal 과 texture coordinates 값을 갖고 있다.
즉, 우리는 위에서 좌표와 RGB 색상에 보간 식을 들이댔던 것처럼, normal 과 texture coordinates 를 같은 방식으로 보간시키면 된다. 해당 계산식은 아래와 같다.
![](https://blog.kakaocdn.net/dn/ofgf4/btsakcKTpjA/LlVWMmu3gyHbXQrKJwJYqK/img.png)
다만 우리가 기억해야 할 것은, 각각의 픽셀은 일정한 간격에 찍힌 녀석이므로, 일정 간격에 맞게 보간이 된다는 것만 기억하자. scan line 을 각각의 점선... 정도로 이해하고, 해당 점선들을 따라서 각 픽셀에서의 보간이 이루어진다고 생각하면 된다.