OpenGL 쉐이더 프로그래밍 - MVP 변환

타입·2025년 12월 15일

컴퓨터 그래픽스

목록 보기
23/24

카메라의 Tilt 설정

틸트 기법으로 원근감을 일부러 왜곡
Asymmetric View Frustum을 Symmetric View Frustum으로 바꿔야함

2D Shearing

  • 2D horizontal shear
    [xy]=[1m01][xy]=[x+myy]\begin{bmatrix} x' \\ y' \end{bmatrix} = \begin{bmatrix} 1 & m \\ 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix} = \begin{bmatrix} x + my \\ y \end{bmatrix}
  • 2D vertical shear
    [xy]=[10m1][xy]=[xmx+y]\begin{bmatrix} x' \\ y' \end{bmatrix} = \begin{bmatrix} 1 & 0 \\ m & 1 \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix} = \begin{bmatrix} x \\ mx + y \end{bmatrix}

3D Shear Matrix 계산

Asymmetric View Frustum의 기울어진 중심축을 Symmetric View Frustum으로 이동

  • 대응 관계
    (xmax+xmin2,ymax+ymin2,znear)( \frac{x_{\max} + x_{\min}}{2}, \frac{y_{\max} + y_{\min}}{2}, -z_{near} )
    ->(0,0,znear)(0, 0, -z_{near})
  • 행렬식
    [00znear1]=[10γ001δ000100001][xmax+xmin2ymax+ymin2znear1]\begin{bmatrix} 0 \\ 0 \\ -z_{\text{near}} \\ 1 \end{bmatrix} = \begin{bmatrix} 1 & 0 & \gamma & 0 \\ 0 & 1 & \delta & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} \frac{x_{\max} + x_{\min}}{2} \\ \frac{y_{\max} + y_{\min}}{2} \\ -z_{\text{near}} \\ 1 \end{bmatrix}

General View Frustum 처리

Shear Matrix H

[10xmax+xmin2001ymax+ymin2000100001]\begin{bmatrix} 1 & 0 & - \frac{x_{\text{max}} + x_{\text{min}}}{2} & 0 \\ 0 & 1 & - \frac{y_{\text{max}} + y_{\text{min}}}{2} & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}

General Perspective Matrix

비대칭인 View Frustum도 처리하는 원근 행렬

N: Canonical View Volume로 변환하는 Normalize 행렬
S: 정규화를 위한 Scale 행렬
H: Shear 행렬

Mperspective=NSH=(1000010000αβ0010)(2znearxmaxxmin00002znearymaxymin0000100001)(10γ001δ000100001)=(ea0fb0cd10)=(2znearxmaxxmin0xmax+xminxmaxxmin002znearymaxyminymax+yminymaxymin000zfar+znearzfarznear2zfarznearzfarznear0010)M_{\text{perspective}} = NSH = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \alpha & \beta \\ 0 & 0 & 1 & 0 \end{pmatrix} \begin{pmatrix} \frac{2z_{\text{near}}}{x_{\text{max}} - x_{\text{min}}} & 0 & 0 & 0 \\ 0 & \frac{2z_{\text{near}}}{y_{\text{max}} - y_{\text{min}}} & 0 & 0 \\ 0 & 0 & -1 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} 1 & 0 & \gamma & 0 \\ 0 & 1 & \delta & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} \\ = \begin{pmatrix} e & a & 0 \\ f & b & 0 \\ c & d \\ -1 & 0 \end{pmatrix} = \begin{pmatrix} \frac{2z_{\text{near}}}{x_{\text{max}} - x_{\text{min}}} & 0 & \frac{x_{\text{max}} + x_{\text{min}}}{x_{\text{max}} - x_{\text{min}}} & 0 \\ 0 & \frac{2z_{\text{near}}}{y_{\text{max}} - y_{\text{min}}} & \frac{y_{\text{max}} + y_{\text{min}}}{y_{\text{max}} - y_{\text{min}}} & 0 \\ 0 & 0 & -\frac{z_{\text{far}} + z_{\text{near}}}{z_{\text{far}} - z_{\text{near}}} & -\frac{2z_{\text{far}} z_{\text{near}}}{z_{\text{far}} - z_{\text{near}}} \\ 0 & 0 & -1 & 0 \end{pmatrix}

Tilted Camera 프로그램

  • Asymmetric View Volume 설정
    기존의 View Volume보다 각도를 좁히고 살짝 우측으로 틂
    x: -0.25 ~ +0.75
    y: -0.25 ~ +0.50

    • 기존 Symmetric View Volue
      카메라의 좌표: (0, 0, 2)
      바라보는 좌표: (0, 0, 0)
      zNear: +1.0
      zFar: +3.0
      x: -1 ~ +1
      y: -0.75 ~ +0.75
  • updateFunc()

void updateFunc(void) {
	...
	// projection matrix
	const GLfloat zoom = 0.5F;
	matProj = glm::frustumRH(
		-0.5F * zoom, +1.5F * zoom,
		-0.5F * zoom, +1.0F * zoom,
		+1.0F, +3.0F
	);
}

원근감을 가진 물체가 한쪽으로 치우쳐져 보이며 비대칭한 View Frustum을 가지는 것을 알 수 있음

Low-Level 구현

Perspective 행렬을 직접 설정

Mperspective=(2znearxmaxxmin0xmax+xminxmaxxmin002znearymaxyminymax+yminymaxymin000zfar+znearzfarznear2zfarznearzfarznear0010)M_{\text{perspective}} = \begin{pmatrix} \frac{2z_{\text{near}}}{x_{\text{max}} - x_{\text{min}}} & 0 & \frac{x_{\text{max}} + x_{\text{min}}}{x_{\text{max}} - x_{\text{min}}} & 0 \\ 0 & \frac{2z_{\text{near}}}{y_{\text{max}} - y_{\text{min}}} & \frac{y_{\text{max}} + y_{\text{min}}}{y_{\text{max}} - y_{\text{min}}} & 0 \\ 0 & 0 & -\frac{z_{\text{far}} + z_{\text{near}}}{z_{\text{far}} - z_{\text{near}}} & -\frac{2z_{\text{far}} z_{\text{near}}}{z_{\text{far}} - z_{\text{near}}} \\ 0 & 0 & -1 & 0 \end{pmatrix}
  • updateFunc()
    glm의 함수를 쓰는 것과 같은 결과
void updateFunc(void) {
	...
	const GLfloat zoom = 0.5F;
#if 0
	matProj = glm::frustumRH(
	              -0.5F * zoom, +1.5F * zoom,
	              -0.5F * zoom, +1.0F * zoom,
	              +1.0F, +3.0F
	          );
#endif
	GLfloat xmin = -0.5F * zoom;
	GLfloat xmax = +1.5F * zoom;
	GLfloat ymin = -0.5F * zoom;
	GLfloat ymax = +1.0F * zoom;
	GLfloat znear = +1.0F;
	GLfloat zfar = +3.0F;
    
	// projection matrix
	GLfloat a = (xmax + xmin) / (xmax - xmin);
	GLfloat b = (ymax + ymin) / (ymax - ymin);
	GLfloat c = -(zfar + znear) / (zfar - znear);
	GLfloat d = -2.0F * zfar * znear / (zfar - znear);
	GLfloat e = 2.0F * znear / (xmax - xmin);
	GLfloat f = 2.0F * znear / (ymax - ymin);
	matProj[0][0] = e; matProj[1][0] = 0; matProj[2][0] = a; matProj[3][0] = 0;
	matProj[0][1] = 0; matProj[1][1] = f; matProj[2][1] = b; matProj[3][1] = 0;
	matProj[0][2] = 0; matProj[1][2] = 0; matProj[2][2] = c; matProj[3][2] = d;
	matProj[0][3] = 0; matProj[1][3] = 0; matProj[2][3] = -1; matProj[3][3] = 0;
}

Field of View Approach

FOV: 카메라를 기준으로 대칭적으로 θ만큼의 각도를 보겠다는 것을 정의

  • Near와 Far 평면 설정
    원점에서 -Z 방향으로 카메라를 볼 때,
    Y 방향으로는 아래 위로 각각 θ/2만큼씩 봄

  • 관계식: tan(θ/2)=ymax/(znear)\tan(\theta/2) = y_{\text{max}} / (-z_{\text{near}})
    변환된 식: ymax=zneartan(θ/2)y_{\text{max}} = -z_{\text{near}} \tan(\theta/2)

  • 비례 관계: w/h=xmax/ymaxw/h = x_{\text{max}} / y_{\text{max}}
    변환된 식: xmax=(w/h)ymaxx_{\text{max}} = (w/h) y_{\text{max}}
    (가로 세로 비율을 이용하여 간접적으로 xmaxx_{\text{max}}를 계산)

  • xmin, yminx_{\text{min}},\ y_{\text{min}}은 대칭으로 계산

Field of View 프로그램

  • updateFunc()
// FOV를 Y방향으로 몇도인지 설정
// 2pi 라디안이 360도에 대응: radian = (pi/180) * degree
float fovy = ((GLfloat)M_PI / 180.0F) * 30.0F; // 30 degree

void updateFunc(void) {
	...
	// viewing transform
	const GLfloat radius = 2.0F;
	matView = glm::lookAtRH(
		glm::vec3(radius * sinf(theta), 20 * 0.05F, radius * cosf(theta)),
		glm::vec3(0.02F, 0.0F, 0.0F),
		glm::vec3(0.0F, 1.0F, 0.0F)
	);
	// projection matrix
	const GLfloat aspect = (GLfloat)WIN_W / (GLfloat)WIN_H; // 현재 윈도우의 Width/Height
	matProj = glm::perspectiveRH(fovy, aspect, +1.0F, +3.0F); // zNear: +1.0, zFar: +3.0
}
  • keyFunc()
    키보드 입력으로 FOV 각도를 증가/감소 시키도록 구현
void keyFunc(GLFWwindow* window, int key, int scancode, int action, int mods) {
	switch (key) {
	...
	case GLFW_KEY_J: fovy += ((GLfloat)M_PI / 180.0F) * 1.0F; break;
	case GLFW_KEY_K: fovy -= ((GLfloat)M_PI / 180.0F) * 1.0F; break;
	}
}

J를 누르면 FOV 각도가 증가하며 볼 수 있는 범위가 넓어져 물체는 작아보이고,
K를 누르면 FOV 각도가 감소하며 볼 수 있는 범위가 좁아져 물체는 커보임

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

0개의 댓글