OpenGL

[OpenGL] ※Depth와 Z 버퍼 알고리즘

usingsystem 2024. 4. 17. 10:07
728x90

Deopth Concepts의 OpenGL에서의 Z 축은 관찰자(카메라)에서 물체까지의 거리를 나타내는 데 사용됩니다. Z 축의 값이 작을수록 물체가 관찰자에 가깝고, 값이 클수록 물체가 관찰자에서 멀어집니다.

OpenGL에서 Z 축을 사용하여 깊이 버퍼(Depth Buffer)를 구현합니다. 깊이 버퍼는 각 픽셀의 깊이(즉, Z 값)를 저장하는 버퍼로, 렌더링 되는 모든 물체의 깊이를 추적합니다. 이를 통해 OpenGL은 후속 렌더링 프로세스에서 각 픽셀의 깊이를 확인하여 깊이 테스트를 수행하고, 화면에 가장 가까운 물체만을 표시할 수 있습니다.

 

3차원 장면에서 관찰자에게 보이는 표면 또는 표면의 일부를 결정하고, 다른 표면에 의해 가려진 표면을 식별합니다. 이를 통해 최종 이미지에 기여하는 표면만 렌더링 하여 렌더링 성능과 시각적 현실감을 향상합니다.

히든 서피스 제거에는 여러 가지 접근 방법이 있습니다.

  1. 뒷면 제거(Back-face Culling): 관찰자에서 멀어지는 표면(즉, 뒷면)은 렌더링 되지 않습니다. 이 기술은 개체가 닫혀 있고 뒷면이 절대적으로 보이지 않는다는 가정에 의존합니다.
  2. 깊이 버퍼링(Z-buffering): 장면이 렌더링 될 때 각 픽셀의 깊이(관찰자로부터의 거리)를 저장하는 깊이 버퍼(또는 Z-버퍼)를 유지합니다. 후속 개체를 렌더링 하는 동안 각 픽셀의 깊이는 버퍼 내의 깊이와 비교되고, 가장 가까운 개체의 픽셀만 그려집니다.
  3. 페인터의 알고리즘(Painter's Algorithm): 특정한 순서로 개체를 렌더링 하고, 새로운 개체는 이전에 그려진 개체 위에 그려집니다. 이 기술은 "페인터의 역설"과 같은 아티팩트를 유발할 수 있습니다. 여기서는 더 먼 객체에 의해 잘못 가려진 더 가까운 객체가 있습니다.
  4. 이진 공간 분할(Binary Space Partitioning, BSP): 장면이 볼록한 하위 공간으로 재귀적으로 세분화되어 바이너리 트리 구조를 생성합니다. 그런 다음 이 트리를 사용하여 효율적으로 가시성을 결정합니다. BSP 트리는 가시성 결정과 효율적인 렌더링 양쪽 모두에 사용될 수 있습니다.
  5. 스캔라인 알고리즘(Scanline Algorithm): 이 기술은 이미지를 가로로 스캔하면서 각 스캔라인에 대해 가시 표면을 결정합니다. 이는 종종 깊이 버퍼링과 같은 다른 기술과 결합되어 더 효율적인 렌더링을 달성합니다.
  6. 레이 캐스팅(Ray Casting)/레이 트레이싱(Ray Tracing): 각 픽셀에서 뷰어의 눈을 통해 장면에 광선을 쏩니다. 광선 교차 테스트를 사용하여 뷰어에게 보이는 가장 가까운 개체 또는 표면을 결정합니다. 레이 트레이싱은 계산 비용이 높지만 매우 현실적인 이미지를 생성합니다.

Z buffer 알고리즘

Z buffer 알고리즘은 현재 가장 널리 쓰이는 hidden surface removal method이다.

Z buffer은 픽셀 단위 하드웨어를 구현한다. (color값 rgva + depth값 z)

object를 그릴 때  pixel마다 depth를 test 한다.

예를 들어 새로운 object가 더 가까우면 새로운 object로 update 하고 멀다면 그리는 걸 무시한다.

장점

  • Object를 정렬할 필요 없음
  • 하드웨어 구현으로 매우 빠름

단점

  • framebuffer 크기 정도의 추가 메모리 필요(픽셀당 4바이트 r, g, b, a)
  • z-buffer(또는 depth buffer) 픽셀당 24bit or 32 bit가 추가되어야 하여 메모리 성능 문제가 있다.

Z buffer 설계

  • z의 크기는 24bit ~ 32bit이다.
  • 좌표로 볼 때는 -1.0~1.0까지 범위를 갖으며 window좌표로 넘어올 때는 0.0~1.0으로 실제 저장에는 0~(2^24-1)의 정수값을 갖는다.

OpenGL에서 Z-buffer 사용하기

OpenGL default는 Z-buffer를 사용하지 않는다. 그렇기 때문에 따로 설정을 해줘야 한다.

  1. Z-buffer turn on
    • glEnable(GL_DEPTH_TEST);
  2. Z-buffer value setting
    • glDepthRange(0.0, 1.0);
      • view volume의 z좌표(-1.0~1.0)을 매핑시키는 window 좌표값 설정
      • -1.0은 nearVal, 1.0은 falVal로 매핑된다.
      • 초기 값은 0.0~1.0
      • 반드시 nearVla<falVal일 필요는 없다.
    • glClearDepthf(1.0F);
  3. Z-buffer clear
    • glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
  1. nearVal:
    • nearVal은 카메라에서 뷰 프러스텀의 가까운 깊이 플레인의 위치를 나타냅니다.
    • 이 값은 일반적으로 0.1 또는 더 큰 양수로 설정됩니다.
    • 작은 값으로 설정하면 더 가까운 물체를 렌더링할 수 있지만, 너무 작은 값은 깊이 버퍼 해상도에 제한을 가할 수 있습니다.
    • 카메라에서 이 값보다 더 가까운 물체는 뷰 프러스텀에 포함되지 않으며 렌더링되지 않습니다.
  2. farVal:
    • farVal은 카메라에서 뷰 프러스텀의 먼 깊이 플레인의 위치를 나타냅니다.
    • 이 값은 일반적으로 카메라에서 멀리 떨어진 뷰의 최대 깊이를 나타내는 양수로 설정됩니다.
    • 큰 값으로 설정하면 멀리 있는 물체도 렌더링됩니다.
    • 카메라에서 이 값보다 더 멀리 떨어진 물체는 뷰 프러스텀에 포함되지 않으며 렌더링되지 않습니다.
glm::vec4 vertRed[] = { // will be painted in red
	{ -0.2F, -0.2F, 0.8F, 1.0F, },
	{ -0.2F, +0.8F, 0.8F, 1.0F, },
	{ +0.8F, -0.2F, 0.8F, 1.0F, },
};

glm::vec4 vertBlue[] = { // will be painted in blue
	{ +0.2F, +0.2F, 0.5F, 1.0F, },
	{ -0.8F, +0.2F, 0.5F, 1.0F, },
	{ +0.2F, -0.8F, 0.5F, 1.0F, },
};
void drawFunc(void) {
	glEnable(GL_DEPTH_TEST);
	glDepthRange(0.0F, 1.0F);
	glClearDepthf(1.0F);
	// clear in gray color
	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
	// provide the vertex attributes
	GLuint locPos = glGetAttribLocation(prog, "aPos");
	glEnableVertexAttribArray(locPos);
	GLuint locColor = glGetUniformLocation(prog, "uColor");
	// draw the blue triangle
	glVertexAttribPointer(locPos, 4, GL_FLOAT, GL_FALSE, 0, glm::value_ptr(vertBlue[0]));
	glUniform4f(locColor, 0.3F, 0.0F, 1.3F, 1.0F); // (light) blue
	glDrawArrays(GL_TRIANGLES, 0, 3);
	// draw the red triangle
	glVertexAttribPointer(locPos, 4, GL_FLOAT, GL_FALSE, 0, glm::value_ptr(vertRed[0]));
	glUniform4f(locColor, 1.0F, 0.3F, 0.3F, 1.0F); // (light) red
	glDrawArrays(GL_TRIANGLES, 0, 3);
	// done
	glFinish();
}
728x90