[DirectX11] 15. 카메라( View & Projection ) – Constant Buffer 활용하기 2편.



카메라변환, 즉 ViewProjection변환에 대해 알아보자. 이 두개의 요소 역시 Constant Buffer를 활용해서 구현할 것이다. 이전까지의 Transform변환은 로컬 공간에서의 모양을 결정하는 정점들이 월드 공간으로의 변환을 해주는 것이였다. 여기에 추가로 해주는 View변환은 카메라 공간으로의 변환을 해주는 것이고, Projection변환은 투영 공간으로의 변환을 해주는 것이다. 각각 어떤것인지 한 번 알아보자.



View 변환


View변환은 카메라 공간으로의 변환을 해주는 것이라고 하였다.

View변환 Matrix는 카메라의 3가지 요소를 이용해서 만들 수 있다. EyePosition, FocusPosition, UpDirection이다. 즉 카메라의 위치, 카메라가 바라보는 곳, 카메라의 위쪽 방향을 가지고 View Matrix를 생성할 수 있다. 이 부분은 DirectXTK에 포함되어 있는 함수를 사용하기로 한다.


C++
const matrix& viewMatrix = XMMatrixLookAtLH( EyePosition, FocusPosition, UpDirection );

XMMatrixLookAtLHpostfixLH인데, 이 것은 LeftHand 즉, 왼손 좌표계 기준으로 생성하는 것이다. 결과적으로 위 그림처럼 물체가 카메라 공간 바깥에 있다면 보이지 않게 된다.



Projection 변환


Projection변환은 투영 공간으로의 변환을 해주는 것이다. 투영 방법에는 OrthographicPerspective방식이 있는데 각각 직교 투영원근 투영이라 부른다. 직교 투영에 대해서는 나중에 한번 다시 다루기로 하고 이번에는 원근 투영에 대해서만 다룬다.

원근 투영은 원근감을 표현하기 위한 투영이다. 이 원근감은 시야 절두체라는 개념에 의해 표현된다. 원근 투영 공간을 위 그림처럼 시야 절두체 모양으로 바꿔볼 수 있는데 near면에 가까운 물체( 녹색 )는 스크린에 가득차 보일것이고, far면에 가까운 물체( 흰색 )는 스크린에서 작게 보일것이다. 두 물체가 같은 크기, 같은 모양이라도 카메라로부터의 거리에 따라 크기가 다르게 표현되는 것이다. 이 시야 절두체를 벗어난 물체는 렌더링 과정은 거치지만 결과적으로 클리핑처리되기 때문에 백 버퍼에서의 결과로는 보이지 않게 된다.


Perspective변환 matrix역시 DirectXTK에 포함되어 있는 함수를 이용해서 생성할 수 있다.

C++
const matrix& projectionMatrix = XMMatrixPerspectiveLH( ViewWidth, ViewHeight, NearZ, FarZ );

ViewWidth, ViewHeightnear면에 있는 크기의 너비와 높이이다.


C++
CRVertex GCRVRect[ 4 ] =  
{  
    { .Position = CRVector( -0.5f,  0.5f, 0.0f )
    { .Position = CRVector(  0.5f,  0.5f, 0.0f )
    { .Position = CRVector( -0.5f, -0.5f, 0.0f )
    { .Position = CRVector(  0.5f, -0.5f, 0.0f )
};

정점들의 범위에 대입하면 이 사각형이 스크린에 보이려면 ViewWidth, ViewHeight0.5크기의 수준이여야 한다는 뜻이다. 뷰포트 크기( 1920 * 1080 )로 지정하게 되면 이 사각형은 너무 작게 렌더링되어 보이지 않는다. 나는 FOV( Field of View )를 사용하는 원근 투영을 할 것이므로 XMMatrixPerspectiveLH대신 XMMatrixPerspectiveFovLH를 사용한다.


C++
const matrix& projectionMatrix = XMMatrixPerspectiveFovLH( FovAngleY, AspectRatio, NearZ, FarZ );

이 함수는 세로 범위의 시야각 값종횡비를 통해 near면의 크기가 결정된다.

원근 투영에 대한 수학적 설명은 여기 링크에 자세히 설명되어 있다.



CRCamera


카메라 관련 클래스를 추가하였다. 이 클래스는 3D 공간에서의 Transform정보를 가지며, 위에서 설명한 View, Projection매트릭스를 반환할 수 있도록 여러 프로퍼티들을 가진다.

C++
EProjectionType ProjectionType  = EProjectionType::Perspective;  
CRVector        LookAtDirection = CRVector::Forward;  
CRVector        Up              = CRVector::Up;  
float           FieldOfView     = 90.0f;  
float           ViewWidth       = 1920.f;  
float           ViewHeight      = 1080.f;  
float           NearDistance    = 0.1f;  
float           FarDistance     = 10000.f;

위와 같은 프로퍼티들이 있다. 이 프로퍼티들을 통해 변환에 사용할 View, Projection매트릭스를 반환한다.


C++
CRMatrix CRCamera::GetViewMatrix() const  
{  
    const CRVector& location = Transform.GetLocation();  
    const CRVector& lookAt   = CRVector::Transform( LookAtDirection, Transform.GetRotation() );  
    return DirectX::XMMatrixLookAtLH( location, location + lookAt, Up );  
}

View매트릭스를 반환하는 함수이다. Transform정보를 통해 이 카메라의 위치를 Eye로 간주하고 카메라가 바라보는 방향을 회전시킨다. 이를 통해 View변환 매트릭스를 반환한다.


C++
CRMatrix CRCamera::GetProjectionMatrix() const  
{  
    return DirectX::XMMatrixPerspectiveFovLH( DirectX::XMConvertToRadians( FieldOfView ), ViewWidth / ViewHeight, NearDistance, FarDistance );  
}

Projection변환 매트릭스는 따로 추가 가공하는 것은 없고 캐슁중인 카메라 프로퍼티를 통해 매트릭스를 생성하여 반환한다.



Constant Buffer 추가하기 ( CRD11BindingConstantBuffer )


Constant Buffer관련 코드는 재사용성이 높아 템플릿 헬퍼 클래스를 하나 추가했었다.

C++
template< typename T >  
class CRD11BindingConstantBuffer  
{  
private:  
    CRName                     Name;  
    CRD11ConstantBufferWPtr    ConstantBufferPtr;  
    ED11RenderingPipelineStage Stage = ED11RenderingPipelineStage::Max;  
    unsigned int               Slot  = 0;
}   

위와 같은 프로퍼티를 가지며 템플릿 인자에 따라 버퍼의 내용을 업데이트하고 렌더링 파이프라인에 연결한다.
이 헬퍼 클래스를 이용해서 View, Projection매트릭스를 Constant Buffer를 생성하고 연결할 것이다.


그 외 생성 및 렌더링 파이프라인 연결, 버퍼 내용 업데이트는 기존 [DirectX11] 12. 변환 – Constant Buffer에 대해 알아보자.에서 사용했던 코드들과 동일하다.



Renderer에서의 View, Projection Constant Buffer


View, Projection Constant Buffer는 렌더러의 특성에 따라갈 수 있으므로 렌더러 내부에서 버퍼를 선언하고 사용한다.

C++
class CRD11Renderer : public ICRRHIRenderer  
{  
public:  
    struct CRViewProjection  
    {  
        CRMatrix View;  
        CRMatrix Projection;  
    };  
  
private:  
    CRD11BindingConstantBuffer< CRViewProjection > ViewProjectionBuffer;

public:
    virtual void UpdateViewProjectionBuffer( const CRMatrix& ViewMatrix, const CRMatrix& ProjectionMatrix )
    {
        CRViewProjection viewProjection;  
        viewProjection.View       = ViewMatrix.Transpose();  
        viewProjection.Projection = ProjectionMatrix.Transpose();  
          
        ViewProjectionBuffer.Update( viewProjection );
    }



카메라를 통해 View, Projection 매트릭스 업데이트


일단 카메라 초기화시 함께 View, Projection버퍼의 내용을 업데이트한다.

C++
GCamera.Initialize( CRCamera::EProjectionType::Perspective, 90.0f, width, height, 0.1f, 1000.0f );  
GCamera.SetLookAtDirection( 0.f, 0.f, 1.f );  
GCamera.Transform.SetLocation( 0.f, 0.f, -15.5f );  

GRHI.GetRenderer()->UpdateViewProjectionBuffer( GCamera.GetViewMatrix(), GCamera.GetProjectionMatrix() );

이 후에는 카메라 관련 프로퍼티들이 변경되었을 때 업데이트할 수 있도록 수정할 예정이다.



이전글 : [DirectX11] 14. ImGUI 설치하기
다음글 : [DirectX11] 16. FBX File로부터 모델 데이터 Import하기.



Leave a Comment