Advanced OpenGL(1) - Depth test

흑빡·2026년 6월 16일

그래픽스

목록 보기
35/40
post-thumbnail

depth test

나만의 tiny renderer 만들기(4) - zbuffer, 애파인변환

여기서 설명하는 zbuffer의 원리를 이용한게
DepthBuffer(Z-Buffer)임

Depth test라는건
프래그먼트의 z를 z-buffer에 기록하기 위해 수행하는 연산을 의미함

이론적으로는
depth test는 fragment shader가 수행된 후,
screen space에서 stencil test이후 실행됨

위 링크에서 볼 수 있다싶이
depth test를 거치는 과정은 다음과 같음

  1. fragment가 z-buffer에 기록된 값보다 우선순위가 낮은 z값을 가지고 있으면 아무것도 안하고 출력 취소
  1. fragment가 z-buffer에 기록된 값보다 우선순위가 높은 z값을 가지고 있으면
  2. z-buffer를 현재 fragment로 갱신해주고,
  3. 현재 fragment를 렌더링

이때 fragment들에 대한 depth test는 픽셀단위로 됨
그리고 위의 링크에서는 uint8_t를 사용했지만
실제에서는 16, 24(정배), 32비트 부동소수점를 사용함

early depth test, early Z

이론상의 depth test는 fragment shader이후에 수행되어
출력되지 않을 fragment를 결정짓고 출려한다고 했음

근데 이렇게 되면
fragment shader할 필요가 없는 부분에 대하여 쉐이딩을 하게 되니
성능상의 이슈가 있을수밖에 없음

그래서 등장한 개념이 early depth test임

다시 이 사진을 보자

fragment shader에서는 주로 fragment의 색상을 결정지음
그리고 정점의 변환은 주로 vertex shader, geometry shader에서 일어남

그럼 정점 변환이 일어난 후에 depth test를 해버려서 무거운 색상 계산 shader인 fragment shader에서의 부담을 줄일 수 있지 않을까?

맞음!

그래서 등장한게 eraly depth test임

래스터라이저에서는 shape assembly에서 정점끼리 연결된 면이 넘어오면
그 면에 대한 각 픽셀의 fragment를 만들게 됨
그리고 early depth test를 수행시켜서
fragment shader이전에 버려야할 fragment를 버리는거지 ㅇㅇ

다만 early Z를 지양해할 경우도 있음

  1. fragment shader에서 직접적으로 fragment의 z를 수정할때
  2. RGBA로 alpha값이 보간되어야하는 fragment에 대해서는 동작하지 않을 수 있음

depth buffer 사용

depth buffer를 사용하는건 간단함

//rendering 전 준비단계
glEnable(GL_DEPTH_TEST);

//rendering단계
while(...)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    //optional
    //glDepthMask(GL_FALSE);
    //glDepthMask(GL_TRUE);
}

위에서 glDepthMask라는 옵션을 볼 수 있음

glDepthMask

glDepthMask는 depth test를 수행하지만,
프래그먼트의 z 값은 depth test이후의 z buffer를 갱신할때 사용하지 않을때 사용하는거임

예를들어 유리창이 있을수 있음

  1. 유리창이 있고, 유리창 뒤에 책상이 있음
  2. 유리창과 책상은 depth test를 통해 유리창이 더 앞에 있다는걸 확인함
  3. 유리창은 반투명한 물체로 다른 refraction, reflection등이 존재함
  4. z-buffer에 유리창의 z는 등록하지 않음으로 유리창 너머의 책상은 유리창의 쉐이더 로직을 통해 색상이 결정되어 보이게 됨
상태설명결과
GL_TRUE (기본값)깊이 버퍼 쓰기 허용테스트를 통과한 프래그먼트의 깊이 값이 깊이 버퍼에 저장
GL_FALSE깊이 버퍼 쓰기 차단테스트를 통과해도 깊이 버퍼는 업데이트되지 않움 (읽기 전용 모드)

따라서
GL_TRUE를 하게 되면
zbuffer에도 갱신이 일어나서 완전 불투명한 물체를 렌더링 할때 사용하게 되고
GL_FALSE를 하게되면
zbuffer에는 갱신이 되지 않지만 물체끼리의 z index비교는 수행하여 반투명한 물체의 back-to-front렌더링을 수행하게 됨


glDepthFunc

glDepthTest의 테스팅 방식을 결정짓는 메서드임

glDepthFunc(GL_LESS)가 기본값

함수설명
GL_ALWAYS깊이 테스트가 항상 통과
GL_NEVER깊이 테스트가 절대 통과되지 않음
GL_LESS프래그먼트의 깊이 값이 저장된 깊이 값보다 작으면 통과
GL_EQUAL프래그먼트의 깊이 값이 저장된 깊이 값과 같으면 통과
GL_LEQUAL프래그먼트의 깊이 값이 저장된 깊이 값보다 작거나 같으면 통과
GL_GREATER프래그먼트의 깊이 값이 저장된 깊이 값보다 크면 통과
GL_NOTEQUAL프래그먼트의 깊이 값이 저장된 깊이 값과 다르면 통과
GL_GEQUAL프래그먼트의 깊이 값이 저장된 깊이 값보다 크거나 같으면 통과

Z index 계산법

제일 위에서 z buffer의 z는 16, 24, 32비트 부동소수점 방식을 이용해 처리한다고 했음

그럼 z index는 어떻게 계산하는걸까?

기본적인 개념은

Fdepth=znearfarnearF_{depth} = \frac{z - near}{far - near}

그래픽스에서 이런 공식 많이 보이는데
정확히 뭐라부르는지 모르겠음...

대충 2개의 near, far절두체를 두고
near와 가까울때는 z = near
far와 가까울때는 z = far로
선형 보간이 이뤄지는 방식임

하지만 이것만으로는 부족함
실제로 여러 물체가 거~의 비슷한 z index를 가지고 있어서
특정 범위내의 물체까지는 z index를 더 정밀한 값으로 계산을 해야한다고 하면?

즉,
가까이 있는 물체는 더 높은 정밀도로 처리가 되고
멀리 있는 물체는 비교적 중요도가 낮으므로 낮은 정밀도로 처리되도록 하는거임

그 공식이 바로...
Fdeph=1/z1/near1/far1/nearF_{deph} = \frac{1/z - 1/near}{1/far - 1/near}

이렇게 z index가 낮을수록 더 높은 정밀도로 depth를 판별하게되고
z index가 높을수록 낮은 정밀도로 depth를 판별하게 됨

void main()
{             
    FragColor = vec4(vec3(gl_FragCoord.z), 1.0);
} 

fragment shader의 로직을 위처럼 바꿔보자
gl_FragCoord가 바로 z index를 나타내어주는 좌표계임

이렇게 멀때는 흰색이다가
가까워질수록 색이 검은색으로 급격하게 변하게 됨

이게 바로 gl의 depth test를 할때 z값 계산이
비선형적이라는 증거임

선형적으로 나타낼땐 어떻게 보이냐?

대충

float near = 0.1;
float far  = 10.0;
float z = gl_FragCoord.z * 2.0 - 1.0;
float depth = (2.0 * near * far / (far + near - z * (far - near))) / far;
FragColor = vec4(vec3(depth), 1.0);

이렇게 0~1범위가 되어있던 z값을 ndc좌표계인 -1~1범위로 바꿔주면...

요로코롬 선형적으로 바뀜~~~

Z fighting

이런걸 본적있을거임

이게 바로 물체간의 z값의 차이가 정말 계속 z buffer가 변경되며 생기는 문제인
z fighting임

이를 해결하는 방법이 몇가지 있음

  1. 두 물체를 완전히 겹치지 않도록 특정 물체의 y축을 살짝만 조정함
  2. near평면을 가능한 한 최대한 멀리 이동시키기
    • near평면에 z값이 가까울수록 더 높은 정밀도를 가지기 때문
    • 다만 이렇게 하면 아주 가까이에 있는 물체가 클리핑 될 수 잇음
  3. buffer를 기본 24비트 부동소수점이 아닌 32비트 부동소수점 등 성능을 약간 희생하고 더 높은 정밀도를 가지도록 하기
profile
그래픽스 하는 퍼그

0개의 댓글