OpenGL 쉐이더 프로그래밍 - 3차원 변환

타입·2025년 7월 25일

컴퓨터 그래픽스

목록 보기
15/24

3D 변환

3D Scaling

물체의 사이즈 변환
사이즈 값이 0보다 작으면 좌표축에 반전

호모지니어스 좌표가 들어감

3D Translation

물체의 평행이동

3D Rotation

회전축: 어느축을 기준으로 회전할지 결정
회전각: 회전축에서 얼마나 회전할지의 값
(2D 회전과 다르게 원점을 기준으로 회전하는 것이 아님)

회전방향 기준은 CCW (반시계방향)

  • 좌표축 기준 회전
    2D 회전과 동일한 방법
    • z축 기준 회전
    • x축 기준 회전
    • y축 기준 회전

3D 스케일링 프로그램

y축 기준으로 회전하는 피라미드 기반 프로그램
스케일링을 먼저 적용하고, y축 기준 회전 적용
키 입력으로 scale factor 변경

glm::vec3 vecScale = { 1.0F, 1.0F, 1.0F };
GLfloat mat[16] = { // 단위행렬
	1.0F, 0.0F, 0.0F, 0.0F,
	0.0F, 1.0F, 0.0F, 0.0F,
	0.0F, 0.0F, 1.0F, 0.0F,
	0.0F, 0.0F, 0.0F, 1.0F,
};

// 키 입력에 따른 scale factor 변경
void keyFunc(GLFWwindow* window, int key, int scancode, int action, int mods) {
	switch (key) {
    ...
	case GLFW_KEY_Q: vecScale.x += 0.05F; break;
	case GLFW_KEY_W: vecScale.y += 0.05F; break;
	case GLFW_KEY_E: vecScale.z += 0.05F; break;
	case GLFW_KEY_A: vecScale.x -= 0.05F; break;
	case GLFW_KEY_S: vecScale.y -= 0.05F; break;
	case GLFW_KEY_D: vecScale.z -= 0.05F; break;
	}
}
  • updateFunc()
    시간에 따라 회전하며 변경된 스케일도 적용
float theta = 0.0F;
system_clock::time_point lastTime = system_clock::now();

void updateFunc(void) {
	// update the rotation angle
	system_clock::time_point curTime = system_clock::now();
	milliseconds elapsedTimeMSEC = duration_cast<milliseconds>(curTime - lastTime); // in millisecond
	theta = (elapsedTimeMSEC.count() / 1000.0F) * (float)M_PI_2; // in <math.h>, M_PI_2 = pi/2
	// update the matrix
	mat[0] = vecScale.x * cosf(theta); mat[8] = vecScale.z * sinf(theta);
	mat[5] = vecScale.y;
	mat[2] = -vecScale.x * sinf(theta); mat[10] = vecScale.z * cosf(theta);
}

y축으로 스케일을 키운 모습

z축으로 스케일을 너무 키웠더니 물체가 잘려보임
canonical view volume을 벗어나서 나타난 현상

Euler Angles

3D 회전 시 좌표축을 기준으로 회전하는 건 모든 경우를 표현하지 못함

오일러 각: 좌표축 중심의 3D 회전을 차례로 3개 합성

오일러 회전

서로 다른 좌표축으로 세 번의 회전을 적용하는 방법

x축, y축, z축을 순서대로 3D 회전 진행: Rx(θ1) -> Ry(θ2) -> Rz(θ3)

오일러 각: (θ1, θ2, θ3)
각 축의 회전 각도 세개를 포함

  • 행렬식

  • 회전 적용 순서
    x-y-z 순서의 회전과, y-x-z 순서의 회전 결과는 전혀 다름

  • unique하지 않음
    전혀 다른 오일러 각으로 표현해도 회전 결과는 같을 수 있음
    (90°,90°,0°) == (0°,90°,90°)
    이에 대한 문제를 해결하기 위해 쿼터니언 도입

3D 변환의 합성

변환을 적용하는 가장 효과적인 방법
Scaling - Rotation - Translation 순서로 적용

만약 Translation 이후 Rotation을 하면 위치가 엇나가버려, Translation을 한 번 더 해줘야 원래 위치에서 회전한 결과가 나타남

Instance Transformation

3D 변환의 경우에도 마찬가지로 Scaling - Rotation - Translation 순서로 적용

p' = T(dx,dy,dz) ∘ Rz(θ3)∘Ry(θ2)∘Rx(θ1) ∘ S(sx,sy,sz) ∘ p

행렬 연결

주어진 좌표 p에 A-B-C 순서대로 변환 적용
p' = C B A p = (C (B (A p)))

행렬을 미리 계산하여 좌표에 연산해도 똑같은 결과
M = C B A
p' = M p

역변환

3D Scaling의 역변환

S(1/sx, 1/sy, 1/sz)

3D Translation의 역변환

T(-dx, -dy, -dz)

3D Rotation의 역변환

(스케일 변환은 없다고 가정)

호모지니어스 좌표의 4x4 행렬에서
주어진 좌표를 회전하는 부분은 좌상단 3x3
평행이동하는 부분은 우측 3x1

회전행렬을 R이라고 한다면,
호모지니어스 좌표 관점에서 벡터만 고려 시 w가 0이 되며
4x4 행렬에 벡터를 곱하는 것과 R 부분만 곱하는 것과 같음

  • 회전행렬 R의 특징
    • 변환 전: x,y,z의 basis vector가 만들어 내는 직교 좌표계
    • 변환 후: u,v,n의 새로운 basis vector가 만들어 내는 직교 좌표계

R 행렬에 unit vector를 곱하면 각 u축,v축,n축에 대한 새로운 unit vector가 나타남

  • u,v,n은 서로 직교하는 basis vector
    u∘u = v∘v = n∘n = 1
    u∘v = v∘n = n∘u = 0

  • R 행렬의 transpose 행렬
    R 행렬을 대각선으로 뒤집은 transpose 행렬은 R 행렬의 역행렬
    R^(-1) = R^T

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

0개의 댓글