이번 시간에는 카메라를 이동시킬 수 있는 기능을 구현해보자.
현재 우리의 프레임워크에서는 카메라의 좌표와 방향이 고정되어있는데, 이를 보다 유동적으로 동작할 수 있도록 수정해보자.
(사실 이 내용은 예전에 내가 블로그에서 다룬 적이 있는 내용이기도 하다. 참조)
스크린에 나타나는 물체의 위치는 물체와 카메라의 상대적 위치관계에 의해 결정되는 것을 알 수 있다. (2D 카메라의 경우부터 생각해보면 쉽다. 카메라가 위로 움직이면 물체는 아래로 움직인다)
때문에 카메라의 실제 좌표를 파이프라인 상에서 자유롭게 움직이게 하는 대신, 카메라를 고정시켜 두고 물체들이 카메라의 움직임에 맞추어 상대적으로 이동하게 만드는 방법을 사용할 수 있다.
(이렇게 하는 가장 큰 이유는 파이프라인에서 카메라의 좌표는 0,0,0이라고 고정한 채로 모든 수학적 연산들을 처리하기 때문이다.)
이 방법을 구체적으로 어떻게 구현할 것인가?
원리는 크게 어렵지 않다.
(앞서 말한 내가 정리해둔 블로그 글의 내용을 같이 참조하는 것을 권장한다)
카메라의 물리적 정보(position과 orientation. TR Matrix로 표현 가능하다. 다만 실제제로 프레임워크에서 구현할 때는 Translation과 rotation을 나눠서 별도의 행렬에 저장한다.)를 저장하고, 이 TR Matrix를 이용해 게임 공간에 존재하는 물체들의 좌표를 카메라에 대한 상대적 좌표계로 변환한 후, 이렇게 변환된 상대적 좌표들을 렌더링 파이프라인에 넘겨주면 된다.
코드와 함께 살펴보자.
사실 수정할 부분이 상당히 방대하고, 또 현재 학습하려는 camera view와 관련 없는 리팩토링도 꽤 있기 때문에, 필요한 부분에 대해서만 개괄적으로 다루곘다.
우선 SpecularPhongPointScene에서 카메라 이동과 관련된 변수들을 추가시켜주었다.
사실 이 값들은 카메라 객체를 따로 만들어서 별도로 관리해주는 편이 좋지만, 프레임워크 구축이 아닌 그래픽스 학습이 주 목적이기 때문에 굳이 별도로 빼주지는 않겠다.
camera view matrix가 추가되었는데, 이는 차후 파이프라인에서 사용해야 하므로 BindView()함수를 통해 별도로 파이프라인에 저장해준다.
camera view matrix는 world view matrix와 구분되어 저장된다.
또 카메라의 WASD 이동 및 마우스 조작이 가능하게 한다.
마우스 조작의 경우 원리는 다음과 같다.
우리의 카메라 fov가 pi/2(90도)이고 width가 640px이라고 가정하자. 마우스 커서를 스크린 상에서 가로로 절반만큼 이동시켰다면 이는 pi/2의 절반, 즉 pi/4만큼의 각으로 치환되고, 이는 곧 320px로 치환된다.
이러한 치환 과정을 편하게 하기 위해 우리는 위와 같은 간단한 수식으로 angles per pixel을 구할 수 있다.
(가로 말고 세로도 동일한 원리로 해주면 된다)
//SpecularPhongPointScene.h
// ...
while( !mouse.IsEmpty() )
{
const auto e = mouse.Read();
switch( e.GetType() )
{
case Mouse::Event::Type::LPress:
mt.Engage( e.GetPos() );
break;
case Mouse::Event::Type::LRelease:
mt.Release();
break;
case Mouse::Event::Type::Move:
if( mt.Engaged() )
{
const auto delta = mt.Move( e.GetPos() );
cam_rot_inv = cam_rot_inv
* Mat4::RotationY( (float)-delta.x * htrack )
* Mat4::RotationX( (float)-delta.y * vtrack ); // 커서의 가로, 세로 이동은 각각 y,x축을 기준으로 회전해준다
/* 이떄 주의할 점은 카메라 자체의 position이 y,x축에 대해 평행하지 않을 수도 있다는 점이다.
쉽게 말해 마우스를 이리저리 움직여서 카메라가 정확히 가로축과 세로축에 대해 평행인 상태가 아니라면,
단순히 y,x축에 대해 마우스 이동한 만큼 회전시켜주는 것으로는 제대로된 마우스 조작이 불가능하다.
때문에 우리는 camera rotation 정보를 담고 있는 cam_rot_inv 라는 값에 마우스에 의한 카메라 회전을 나타내는 회전 행렬을 곱해줌으로써
상대적 y,x축에 대한 회전으로 처리해준다.
*/
}
break;
}
}
WASD 조작의 경우 단순히 카메라 포지션에 값을 더해주는 식으로 구현해서는 안된다. 카메라의 "앞"은 카메라의 현재 TR에 의해 달라지는 값이기 때문이다. 때문에 다음과 같은 구현이 올바른 방식이다.
//...
if( kbd.KeyIsPressed( 'W' ) )
{
cam_pos += Vec4{ 0.0f,0.0f,1.0f,0.0f } * !cam_rot_inv * cam_speed * dt;
}
if( kbd.KeyIsPressed( 'A' ) )
{
cam_pos += Vec4{ -1.0f,0.0f,0.0f,0.0f } * !cam_rot_inv * cam_speed * dt;
}
if( kbd.KeyIsPressed( 'S' ) )
{
cam_pos += Vec4{ 0.0f,0.0f,-1.0f,0.0f } * !cam_rot_inv * cam_speed * dt;
}
if( kbd.KeyIsPressed( 'D' ) )
{
cam_pos += Vec4{ 1.0f,0.0f,0.0f,0.0f } * !cam_rot_inv * cam_speed * dt;
}
실행시켜보면 잘 작동함을 알 수 있다.
'computer graphics > 3D Graphics' 카테고리의 다른 글
[3DGraphics] 21. Clipping (1) | 2022.10.28 |
---|---|
[3DGraphics] 20. Projection matrix (1) | 2022.10.11 |
[3DGraphics] 19. Implementing Vec4, Mat4 (2) | 2022.10.11 |
[3DGraphics] 18. Specular highlights (1) | 2022.10.11 |
[3DGraphics] 17. Phong shading & Per-pixel lighting (0) | 2022.09.09 |