본문 바로가기

Game/Graphics

Learn OpenGL - Advanced OpenGL : Face culling

link : https://learnopengl.com/Advanced-OpenGL/Face-culling


Face culling


 3D 큐브를 시각화하고 어느 방향에서나 볼 수 있는 최대 면을 계산해보아라. 상상력이 너무 창의적이지 않은 경우


최대 수는 3이 될 것이다. 어떤 위치 또는 방향에서도 큐브를 볼 수 있지만 3개 이상의 면을 볼 수는 없다.


그렇다면 왜 우리가 볼 수 없는 다른 세 면을 그리는 노력을 낭비할 것인가? 우리가 어떤 방법으로 그것들을


버릴 수 있다면 우리는 조각 쉐이더 실행의 50% 이상을 절약할 수 있을 것이다.

우리는 50% 대신 50% 이상이라고 말한다. 특정 가고에서 볼 때 2개 또는 심지어 1개의 면만이 보일 수 있기 때문이다. 이 경우 우리는 50% 이상을 절약할 수 있다.

 이것은 정말 좋은 생각이다. 그러나 우리가 해결해야 할 한 가지 문제가 있다. 객체의 면이 시청자의 관점에서 보이지 않는지


어떻게 알 수 있는가?


 닫힌 모양을 상상해보자. 각 면에는 양면이 있다. 각 면은 사용자를 대면하거나 사용자에게 보여줄 것이다. 보는 사람이 보는 면만


렌더링 할 수 있다면 어떨까?



 이것이 정확하게 face culling이 하는 일이다. OpenGL은 뷰어쪽으로 향하는 모든 면을 검사하고, 그 면을 렌더링하면서


모든 면을 폐기하면서 조각 쉐도우 호출을 많이 절약하다. (이 것의 비용은 비싸다) OpenGL에게 우리가 사용하는 면 중 실제로


앞면인지 뒤면인지를 알려줄 필요가 있다. OpenGL은 정점 데이터의 와인딩 순서를 분석해 이를 위해 영리한 트릭을 사용한다.







Winding order


 삼각형 정점 세트를 정의 할 때 시계 방향 또는 시계 반대 방향으로 특정 와인딩 순서를 정의한다. 각각의 삼각형은 3개의 꼭지점으로


이루어져 있으며 우리는 삼각형의 중심에서 볼 때 와인딩 순서로 3개의 꼭지점을 지정한다.


Winding order of OpenGL triangles


 이미지에서 볼 수 있듯이 먼저 정점 1을 정의한 다음 정점 2 또는 3을 정의 할 수 있으며 이 선택 사항은 이 삼각형의 와인딩 순서를 정의한다.


다음 코드는 이것을 설명한다:

float vertices[] = {
    // clockwise
    vertices[0], // vertex 1
    vertices[1], // vertex 2
    vertices[2], // vertex 3
    // counter-clockwise
    vertices[0], // vertex 1
    vertices[2], // vertex 3
    vertices[1]  // vertex 2  
};

 따라서 삼각형 프리미티브를 형성하는 3개의 정점 세트에는 와인딩 순서가 포함된다. OpenGL은 삼각형이 앞면인지 아니면 뒤쪽면인지를


결정하기 위해 프리미티브를 렌더링 할 때 이 정보를 사용한다. 기본적으로 반 시계 방향 정점으로 정의된 삼각형은 정면 삼각형으로 처리된다.



 정점 순서를 정의 할 때 해당 삼각형이 사용자를 향한 것처럼 시각화하므로 사용자가 지정하는 삼각형은 그 삼각형을 직접 마주하는


것처럼 시계 반대 방향이어야한다. 이와 같이 모든 정점을 지정하는 멋진 점은 rasterization 단계에서 실제 와인딩 순서가 계산된다는 것이다.


따라서 정점 쉐이더가 이미 실행된 경우이다. 그런 다음 정점은 뷰어의 관점에서 볼 수 있다.



 뷰어가 직면하고 있는 모든 삼각형 정점은 실제로 우리가 지정한 순서대로 올바른 와인딩 순서로 되어 있지만 정육면체의 다른 면에 있는


삼각형의 정점은 이제 와인딩 순서가 바뀌는 방식으로 렌더링된다. 결과는 우리가 마주하고 있는 삼각형이 앞을 향한 삼각형으로 보이고 뒤의


삼각형이 뒤를 향하는 삼각형으로 보여진다는 것이다. 다음 이미지는 이 효과를 보여준다:


Image of viewer seeing front or back facing triangles


 정점 데이터에서 우리는 반 시계 방향으로 삼각형을 모두 정의했을 것이다. 그러나 뷰어의 현재 시점에서 1,2,3의 순서로 뒤쪽 삼각형을


그리면 뷰 백의 방향에서 뒤쪽 삼각형이 시계 방향으로 렌더링된다. 시계 반대 방향으로 뒤쪽 삼각형을 지정했지만 시계 방향으로 렌더링된다.


이것은 정확히 보이지 않는 면을 도려내는 것이다!






Face culling


 튜토리얼을 시작할 때 우리는 OpenGL이 삼각형 프리미티브를 버릴 수 있다고 말했다. 이제 정점의 와인딩 순서를 설정하는 방법을


알았으므로 기본적으로 비활성화된 OpenGL의 면 제거 옵션을 시작할 수 있다.



 마지막 튜토리얼에서 사용한 정육면체 정점 데이터는 반 시계 방향 와인딩 순서를 염두에 두고 정의되지 않았으므로 여기에서


복사 할 수 있는 반 시계 방향 와인딩 순서를 반영하도록 정점 데이터를 업데이트했다. 이러한 정점이 실제로 모든 삼각형에 대해


시계 반대 방향으로 모두 정의된다는 것을 시도하고 시각화하는 것이 좋다.



 Face culling을 사용하려면 OpenGL의 GL_CULL_FACE 옵션을 활성화하면 된다:

glEnable(GL_CULL_FACE);  

 이 시점부터 앞면이 아닌 모든 면이 버려진다. 현재 조각 렌더링시 성능의 50% 이상을 저장하지만 큐브와 같은 닫힌 모양에서만


작동하낟. 앞면과 뒷면이 보이기 때문에 앞의 튜토리얼에서 풀잎을 그릴 때 다시 면을 컬링 해제해야한다.



 OpenGL을 사용하면 추려내고 싶은 면 유형을 변경할 수 있다. 우리가 앞면과 뒷면을 컬하지 않으려면 어떨까?


glCullFace를 호출해 이 동작을 정의 할 수 있다:

glCullFace(GL_FRONT);  

 glCullFace 함수에는 세 가지 옵션이 있다.


- GL_BACK : 뒷면만 제거한다.


- GL_FRONT : 앞면만 제거한다.


- GL_FRONT_AND_BACK : 앞면과 뒷면을 모두 컬한다.



 glCullFace의 초기 값은 GL_BACK이다. 외면을 제외하고 우리는 OpenGL에게 glFrontFace를 통해 반 시계 방향 면 대신 정면으로


시계 방향 면을 선호한다고 말할 수 있다:

glFrontFace(GL_CCW);  

 기본 값은 반 시계 방향 순서를 나타내는 GL_CCW이며, 다른 옵션은 시계 방향 순서를 나타내는 GL_CW이다.


 간단한 테스트로서 우리는 OpenGL에게 front face가 반 시계 방향으로 정렬하는 대신 시계 방향으로 정렬됨을 알려줌으로써


와인딩 순서를 바꿀 수 있다:

glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glFrontFace(GL_CW);  

 결과적으로 뒷면만 렌더링 된다:


Image of faceculling with clockwise ordering, only culling the front faces


반 시계 방향의 기본 순서로 전면을 컬링해 동일한 효과를 만들 수 있다:

glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);  

 보시다시피, face culling은 최소한의 노력으로 OpenGL 응용 프로그램의 성능을 향상시키는 훌륭한 도구이다. 어떤 개체가 실제로


도형 제거의 이점을 누리고 어떤 개체를 제거해서는 안되는지 추적해야한다.





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


성능의 이점을 주는 챕터였다. 실제로 프로젝트를 진행할 때 꼭 다시 봐야할 챕터!


내 목표는 이번 주 안에 Advanced OpenGL 챕터를 끝내는 것이기 때문에 내일도 달린다!! 화이팅!