개발하는 리프터 꽃게맨입니다.

[그래픽스] 삼각형 클리핑 본문

컴퓨터 그래픽스

[그래픽스] 삼각형 클리핑

파워꽃게맨 2024. 8. 19. 13:34

현재까지 만들어낸 렌더러에 존재하는 버그를 보여드리겠습니다.

 

 

오브젝트 위로 지나가면 선이 갑자기 늘어나거나 텍스처 맵핑이 잘못되는 문제가 발생합니다.

 

 

그 이유는 카메라 아래에 커다란 삼각형이 존재할 때, 투영이 이상하게 되는 현상이 발생하기 때문입니다.

카메라 뒷편에 있는 점도 투영되게 됩니다.

원래라면 카메라 앞에 있는 점만 투영이 되어야 하는데, 보이지 않는 뒷면의 정점까지 투영이 되면서 문제가 발생한다고 생각할 수 있습니다.

 

눈 앞에 있는 정점만 그리면 되는 않나? 라고 생각할 수 있지만, 점 2개로는 삼각형을 만들 수 없기 때문에 상당히 골치가 아픈 문제입니다.

 

이 경우, 큰 삼각형은 작은 삼각형으로 축소시킬 수 있다면 해결할 수 있습니다.

 

 

이와같은 삼각형이 있다면

 

이렇게 잘라내면 됩니다.

 

삼각형을 축소시키는 작업은 클립좌표계에서 수행합니다.

그 편이 계산량이 적고 간편하기 때문입니다.

 

삼각형을 축소시키는 방법은 절두체와 삼각형이 접하는 점을 P'2, P'3 라고 할 때,

P1, P'2, P'3 로 삼각형을 정의하면, 삼각형을 축소시킬 수 있습니다.

 

이제 접하는 점을 찾아보겠습니다.

문제를 단순화하여 근평면 입장에서만 보도록 합니다.

 

근평면은 클립좌표계에서 z = - w의 값을 가집니다.


점P1과 점P2사이에 존재하는 점P'2는 컨벡스 결합식으로 찾을 수 있습니다.

점 P에 대해서, Z값 그리고 W값만 분리해도 등식이 성립합니다.

 

근평면에서는 z = -w 이 성립하기 때문에

근평면에서는 위와 같은 등식이 성립하게 되고

 

 

최종적으로 t에 대한 식을 얻어낼 수 있습니다.

 

이를 코드로 나타내면 위와 같습니다.

t에 대한 식을 w와 z로 부터 얻어낼 수 있기 때문에

근평면과 선분 P1, P2 가 만나는 새로운 점을 계산할 수 있습니다.

 

가시부피는 6개의 평면으로 이루어져있기 때문에, 동일한 방법으로 총 6개의 정점 계산 함수를 만들어줍니다.

 

그리고 밖에 있는 점과 안에 있는 점을 판단할 수 있어야 하기에 내외부 판단식이 필요합니다.

 

판단식을 간단히 구할 수 있습니다.

 

판단식 함수들은 위와 같습니다.

판단함수와 정점 계산함수로 새로운 정점들을 얻었다면

그러면 새로운 점으로 작은 삼각형을 재정의해주면 됩니다.

 

위처럼 두 점이 밖에 있을 경우에는 이런 식으로 간단하게 축소할 수 있지만

 

 

한 점만 밖에 있는 경우에는 처리가 조금 까다롭습니다.

사각형으로 잘리기 때문에, 기존 삼각형 처리 함수로 메시를 그려낼 수 없습니다.

 

이 경우, 사각형을 삼각형 2개로 쪼개면 해결할 수 있습니다. 

 

삼각형 P1, P2, P3 -> P1, P2, P'1 으로 정의하고

새로운 삼각형 P2 -> P'2 -> P'1 을 정의해야 합니다.

 

여기서 중요한 점은 기존 삼각형과 정의하는 방향이 동일해야 한다는 겁니다.

시계방향으로 정의하냐, 반시계방향으로 정의하냐에 따라서 백페이스 컬링 알고리즘에 의해서 잘릴 수도 있기 때문에

새로운 삼각형의 정의는 기존 삼각형을 따라가야 합니다.

 

이를 토대로 삼각형 클리핑하는 함수를 작성해보겠습니다.

 

삼각형 클리핑 함수 본체입니다.

 

정점 버퍼, 테스트 함수 포인터, 정점 생성 함수 포인터

총 3개를 인자로 받습니다.

 

3개의 정점을 테스트 함수로 내외부판단을 한 다음

외부에 있는 정점과 내부에 있는 정점을 분리합니다.

 

FixedVector은 std::array랑 비슷한 자료구조인데, 그냥 정적 배열이라고 보시면 되겠습니다.

 

외부에 있는 점이 1개일 경우

새로운 정점 2개를 구해서

기존 정점을 수정하고, 정점 버퍼에 새로운 정점을 추가해줍니다.

 

만약, 두 점이 외부에 있을 경우

새로운 정점을 계산해서, 기존 정점을 수정합니다.

 

세 점이 외부에 있을 경우 false를 반환합니다.

 

 

6개의 면에 대해서 반복해야 하기에

위와같이 루프를 돌아서 처리해줬습니다.

 

 

 

NDC 좌표로 변환되기전에 클리핑을 수행할 수 있도록 하겠습니다.

 

 

결과물