OpenGL 쉐이더 프로그래밍 - 뷰포트 개념

타입·2026년 1월 3일

컴퓨터 그래픽스

목록 보기
24/24

NDC와 WC

NDC (Normalized Device Coordinate)

Canonical View Volume이 정의되는 좌표계
x, y, z: [-1, +1], [-1, +1], [-1, +1]

WC (Window Coordinate)

Window 설정용 integer 좌표계
window size = wwinhwinw_{\text{win}} * h_{\text{win}}

NDC와 WC

마지막 출력을 NDC를 거쳐 WC로 보내는 이유는 정규화된 구간에 그려진 물체를 모든 윈도우 사이즈에 대응 가능하기 때문

정확한 크기의 렌더링 후 해상도에 따라 매핑관계만 정의해주면 됨
NDC의 좌표를 대응되는 WC의 좌표로 계산이 필요함

xwin=(xndc+1.0)wwin2+0.0x_{\text{win}} = (x_{\text{ndc}} + 1.0) \frac{w_{\text{win}}}{2} + 0.0
ywin=(yndc+1.0)hwin2+0.0y_{\text{win}} = (y_{\text{ndc}} + 1.0) \frac{h_{\text{win}}}{2} + 0.0

마지막에 0.0을 더해주는 이유는 뷰포트 설정 시 다른 값이 올 수도 있기 때문, 윈도우 전체에 출력할 땐 무시해도 됨

Viewport 설정

Viewport: OpenGL 출력 영역

보통은 window 전체를 쓰지만,
window 내의 일부 직사각형 영역으로 설정 가능

Canonical View Volume의 좌표들을 window의 뷰포트 내의 좌표로 변환

Depth Range 설정

Depth Range 값의 계산

  • z좌표의 변환
    • Canonical View Volume
      zndc:[1.0,+1.0]z_{\text{ndc}}: [-1.0, +1.0] 구간
    • z-buffer
      zwin:[0.0,1.0]z_{\text{win}}: [0.0, 1.0] 구간
    • [1.0,+1.0]ndc[-1.0, +1.0]_{\text{ndc}}[0.0,+1.0]win[0.0, +1.0]_{\text{win}} 대응

z-buffer의 일부 구간만 사용하는 것도 가능

Viewport 프로그램

  1. 전체 화면에 출력
  2. 미니맵 출력
    윈도우의 25% 크기
    위치는 (70%, 5%)
  • drawFunc()
    미니맵을 위해 뷰포트 설정 후 다시 전체화면에 그리기 위해 리셋하는 작업이 필요함을 주의
void drawFunc(void) {
	...
	// clear in gray color
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	// 뷰포트 세팅 1
	glViewport(0, 0, WIN_W, WIN_H);
    ...
	glDrawArrays(GL_TRIANGLES, 0, 18); // 피라미드
    ...
	glDrawArrays(GL_TRIANGLES, 0, 36); // 큐브
    
	// 미니맵 위치 및 크기 설정
	GLint map_x = (GLint)(WIN_W * 0.70F);
	GLint map_y = (GLint)(WIN_H * 0.05F);
	GLsizei map_w = (GLsizei)(WIN_W * 0.25F);
	GLsizei map_h = (GLsizei)(WIN_H * 0.25F);
	// 뷰포트 세팅 2
	glViewport(map_x, map_y, map_w, map_h);
	glDrawArrays(GL_TRIANGLES, 0, 36); // 큐브
    ...
}

우측 하단에 미니맵으로 큐브가 그려지는 것을 확인

Scissor Box 프로그램

Scissor Box 설정

Viewport: NDC 전체 영역이 mapping 되는 직사각형 영역 설정
Scissor Box: window 좌표계 내에서 OpenGL 출력이 허용되는 직사각형 영역 설정

Viewport와 Scissor Box가 서로 겹치는 영역에 화면이 업데이트

  • drawFunc()
void drawFunc(GLFWwindow* window) {
	...
	// 윈도우 사이즈를 가져옴
	glClearColor(0.5F, 0.5F, 0.5F, 1.0F);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	int win_w, win_h;
    // Screen Coordinates 사용 (원점이 좌상단)
	glfwGetWindowSize(window, &win_w, &win_h);
	glViewport(0, 0, win_w, win_h);
    ...
    
	GLint map_x = (GLint)(win_w * 0.70F);
	GLint map_y = (GLint)(win_h * 0.05F);
	GLsizei map_w = (GLsizei)(win_w * 0.25F);
	GLsizei map_h = (GLsizei)(win_h * 0.25F);
	glViewport(map_x, map_y, map_w, map_h);
    
    // Scissor Box 활성화
	glEnable(GL_SCISSOR_TEST);
	glScissor(map_x, map_y, map_w, map_h);
    
	glClearColor(0.5F, 0.5F, 1.0F, 1.0F);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glDrawArrays(GL_TRIANGLES, 0, 36); // 36 vertices
    
    // Scissor Box 비활성화
	glScissor(0, 0, win_w, win_h);
	glDisable(GL_SCISSOR_TEST);
    ...
}

Scissor Box가 적용된 미니맵 영역만 glClear()가 적용되어 배경색이 바뀐 것을 확인

Viewport 자동 설정

Window resize 처리

Viewport의 Aspect Ratio(종횡비)는 유지하고 윈도우에 여백을 표현
aspect ratio = width / height

  • drawFunc()
    의도한 종횡비를 벗어나면 뷰포트를 중앙으로 이동시키는 프로그램
void drawFunc(GLFWwindow* window) {
	...
	int win_w, win_h;
	glfwGetWindowSize(window, &win_w, &win_h);
	GLint vp_x, vp_y;
	GLsizei vp_w, vp_h;
    
    // aspect ratio: 320 / 240 = 1.333
	GLfloat aspect = (GLfloat)WIN_W / (GLfloat)WIN_H;
    
    // vp_w : vp_h = WIN_W : WIN_H
	if (win_w < win_h * aspect) { // portrait case
		vp_w = win_w;
		vp_h = (GLsizei)(win_w / aspect);
		vp_x = 0;
		vp_y = (win_h - vp_h) / 2;
	}
	else { // landscape case
		vp_h = win_h;
		vp_w = (GLsizei)(win_h * aspect);
		vp_y = 0;
		vp_x = (win_w - vp_w) / 2;
	}
    
    // 종횡비대로 뷰포트 설정
	glViewport(vp_x, vp_y, vp_w, vp_h);
	glEnable(GL_SCISSOR_TEST);
	glScissor(vp_x, vp_y, vp_w, vp_h);
	glClearColor(0.6F, 0.6F, 0.6F, 1.0F);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glScissor(0, 0, win_w, win_h); // turn-off scissor test, double check
	glDisable(GL_SCISSOR_TEST);
    
    ...
    
	// 미니맵도 위치를 이동시킴
	GLint map_x = vp_x + (GLint)(vp_w * 0.70F);
	GLint map_y = vp_y + (GLint)(vp_h * 0.05F);
	GLsizei map_w = (GLsizei)(vp_w * 0.25F);
	GLsizei map_h = (GLsizei)(vp_h * 0.25F);
	glViewport(map_x, map_y, map_w, map_h);
    
	glEnable(GL_SCISSOR_TEST);
	glScissor(map_x, map_y, map_w, map_h);
    
	glClearColor(0.5F, 0.5F, 1.0F, 1.0F);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glDrawArrays(GL_TRIANGLES, 0, 36); // 36 vertices
    
	glScissor(0, 0, win_w, win_h);
	glDisable(GL_SCISSOR_TEST);
    ...
}

윈도우 사이즈를 변경시켜도 뷰포트의 종횡비가 유지되어 중앙에 위치하는 것을 확인

profile
주니어 언리얼 프로그래머

0개의 댓글