헤더파일

HLSL 프로그래밍 - DXUT프레임워크 본문

DirectX

HLSL 프로그래밍 - DXUT프레임워크

헤더파일 2018. 2. 28. 18:24

HLSL 프로그래밍 [고급 셰이더 언어와 DirectX 11로 구현하는 3D 렌더링]를 보며 공부한 내용을 정리한 것입니다. 예제 소스코드는 http://acornpub.co.kr/book/hlsl-cookbook여기서 받을 수 있습니다. 

컴파일이 안되면 프로젝트 속성 -> 링크-> 입력 에서 라이브러리 종속성에 legacy_stdio_definitions.lib; 를 넣어주면 됩니다. 

DirectX SDK(June 2010)이 깔려있어야 합니다. 



셰이더만 작성하고 렌더몽키로 테스트하니까 한계가 있는 것 같아 책에 예제코드에 있는 DirectX코드와 같이 공부하려고 합니다. 예제코드는 DXUT로 작성되어 있습니다. DXUT는 마이크로소프트에서 다이렉트X 샘플코드를 만들기 위해 사용한 프레임워크로 다이렉트X를 초기화하고 윈도우 창을 만드는 복잡한 작업을 콜백함수를 써서 간단하게 사용할 수 있습니다.


Ambient.cpp 가 윈도우 진입점이 있는 소스파일입니다. 여기서 다른 클래스들의 객체를 생성하고 메인루프가 돕니다.

실행 순서

실행하면 WinMain으로 들어가게 됩니다.

DXUTSetCallbackMsgProc( MsgProc );
DXUTSetCallbackKeyboard( OnKeyboard );
DXUTSetCallbackMouse( OnMouse );
DXUTSetCallbackFrameMove( OnFrameMove );
DXUTSetCallbackDeviceChanging( ModifyDeviceSettings );

처음엔 IO이벤트(마우스, 키보드) 관련 콜백함수를 연결합니다.  콜백함수란 마우스 클릭, 창이동 등 이벤트가 있을 때 이 함수들을 DXUT에서 자동으로 불러주는 것을 이야기합니다. 



    DXUTSetCallbackD3D9DeviceAcceptable( IsD3D9DeviceAcceptable );


    DXUTSetCallbackD3D11DeviceAcceptable( IsD3D11DeviceAcceptable );

    DXUTSetCallbackD3D11DeviceCreated( OnD3D11CreateDevice );

    DXUTSetCallbackD3D11SwapChainResized( OnD3D11ResizedSwapChain );

    DXUTSetCallbackD3D11SwapChainReleasing( OnD3D11ReleasingSwapChain );

    DXUTSetCallbackD3D11DeviceDestroyed( OnD3D11DestroyDevice );

    DXUTSetCallbackD3D11FrameRender( OnD3D11FrameRender );


역시 DirectX초기화관련 여러 콜백함수를 연결합니다.



  InitApp();


화면 상의 UI를 만듭니다.



    DXUTInit( true, true, NULL ); 

    DXUTSetCursorSettings( true, true );

    DXUTCreateWindow( L"Ambient Light" );

    DXUTCreateDevice( D3D_FEATURE_LEVEL_11_0, true, 1024, 768 );


DXUT를 초기화하고 창을 만들고 위에서 연결한 콜백함수들로 다이렉트X11 디바이스를 만듭니다.



 DXUTMainLoop(); 


메인루프에 들어 가는데 콜백함수 OnD3D11FrameRender와 OnFrameMove 함수가 계속 불리게 됩니다.


사실상 다른 함수들은 고정되어 만질 필요가 없고 앞으로 계속 수정할 함수는 메인루프에서 화면을 그리는 OnD3D11FrameRender 함수와 OnFrameMove 함수입니다.


void CALLBACK OnFrameMove( double fTime, float fElapsedTime, void* pUserContext )

{

    // Update the camera's position based on user input 

    g_Camera.FrameMove( fElapsedTime );


// Pass HUD values to the systems

g_LightManager.SetAmbient(g_vAmbientLowerColor, g_vAmbientUpperColor);

g_LightManager.SetDirectional(g_vDirLightDir, g_vDirLightColor);

}



void CALLBACK OnD3D11FrameRender( ID3D11Device* pd3dDevice, ID3D11DeviceContext* pd3dImmediateContext, double fTime,

                                 float fElapsedTime, void* pUserContext )

{

    // If the settings dialog is being shown, then render it instead of rendering the app's scene

    if( g_SettingsDlg.IsActive() )

    {

        g_SettingsDlg.OnRender( fElapsedTime );

        return;

    }


    float ClearColor[4] = { 0.0f, 0.0, 0.0, 0.0f };

    ID3D11RenderTargetView* pRTV = DXUTGetD3D11RenderTargetView();

    pd3dImmediateContext->ClearRenderTargetView( pRTV, ClearColor );


    // Clear the depth stencil

    ID3D11DepthStencilView* pDSV = DXUTGetD3D11DepthStencilView();

    pd3dImmediateContext->ClearDepthStencilView( pDSV, D3D11_CLEAR_DEPTH, 1.0, 0 );


    // Get the projection & view matrix from the camera class

D3DXMATRIX mWorld; // No need for a real world matrix

D3DXMatrixIdentity(&mWorld);

    D3DXMATRIX mView = *g_Camera.GetViewMatrix();

    D3DXMATRIX mProj = *g_Camera.GetProjMatrix();

    D3DXMATRIX mWorldViewProjection = mView * mProj;


// Store the previous depth state

ID3D11DepthStencilState* pPrevDepthState;

UINT nPrevStencil;

pd3dImmediateContext->OMGetDepthStencilState(&pPrevDepthState, &nPrevStencil);


    // Set render resources

    pd3dImmediateContext->PSSetSamplers( 0, 1, &g_pSampLinear );


// Prepare for forward lighting

g_LightManager.ForwardSetup(pd3dImmediateContext);

g_SceneManager.RenderForward(pd3dImmediateContext);


// Resotre the previous depth state

pd3dImmediateContext->OMSetDepthStencilState(pPrevDepthState, nPrevStencil);

SAFE_RELEASE( pPrevDepthState );


if(g_bShowHud)

{

DXUT_BeginPerfEvent( DXUT_PERFEVENTCOLOR, L"HUD / Stats" );

g_HUD.OnRender( fElapsedTime );

g_SampleUI.OnRender( fElapsedTime );

RenderText();

DXUT_EndPerfEvent();

}


    static DWORD dwTimefirst = GetTickCount();

    if ( GetTickCount() - dwTimefirst > 5000 )

    {    

        OutputDebugString( DXUTGetFrameStats( DXUTIsVsyncEnabled() ) );

        OutputDebugString( L"\n" );

        dwTimefirst = GetTickCount();

    }

}



    float ClearColor[4] = { 0.0f, 0.0, 0.0, 0.0f };

    ID3D11RenderTargetView* pRTV = DXUTGetD3D11RenderTargetView();

    pd3dImmediateContext->ClearRenderTargetView( pRTV, ClearColor );


렌더타깃에 픽셀셰이더에서 반환한 픽셀 값을 그리게 됩니다. 새로 화면을 그릴 것이기 때문에 렌더타깃의 픽셀을 다 검은색으로 만들어 줍니다.



    ID3D11DepthStencilView* pDSV = DXUTGetD3D11DepthStencilView();

    pd3dImmediateContext->ClearDepthStencilView( pDSV, D3D11_CLEAR_DEPTH, 1.0, 0 );


뎁스 스텐실 뷰는 현재 렌더타깃에 그려진 물체의 깊이 값이 저장된 텍스쳐입니다.   이 값으로 어떤 물체가 앞에 있는지 판단해서 앞에 물체가 뒤에 물체를 가리게 됩니다. 새로 화면을 그릴 것이므로 이 뷰의 픽셀도 다 비워줍니다.


    D3DXMATRIX mWorld; 

    D3DXMatrixIdentity(&mWorld);

    D3DXMATRIX mView = *g_Camera.GetViewMatrix();

    D3DXMATRIX mProj = *g_Camera.GetProjMatrix();

    D3DXMATRIX mWorldViewProjection = mView * mProj;


물체의 월드행렬은 원점으로 잡을 것이므로 단위행렬을 씁니다. 뷰행렬과 투영행렬은 카메라에서 얻어옵니다.



ID3D11DepthStencilState* pPrevDepthState;

UINT nPrevStencil;

pd3dImmediateContext->OMGetDepthStencilState(&pPrevDepthState, &nPrevStencil);


        pd3dImmediateContext->PSSetSamplers( 0, 1, &g_pSampLinear );  


g_LightManager.ForwardSetup(pd3dImmediateContext);

g_SceneManager.RenderForward(pd3dImmediateContext);


전 뎁스 스텐실 상태를 기억해 놓습니다.  LightManager의 ForwardSetup에서 뎁스스텐실 상태를 변경할 수 있기 때문에 기본값을 백업해 놓는 것입니다.

픽셀셰이더에 ID3D11SamplerState 를 연결합니다. 이 상태는 셰이더에서 텍스쳐를 읽을 때 어떻게 읽을 지 결정하는데 쓰입니다.

LightManager와 SceneManager의 렌더링 함수를 호출합니다.


LightManager 클래스


셰이더객체, InputLayout, 상수버퍼, DepthStencilState 포인터, 조명 색깔을 갖고 있습니다.

콜백함수 OnD3D11ResizedSwapChain에서 Init()함수로 초기화 됩니다.



Init 함수


상수버퍼, 셰이더, InputLayout, DepthStencilState 객체를 생성합니다.


DeInit 함수


만든 상수버퍼, 셰이더, InputLayout, DepthStencilState 객체를 해제합니다.


ForwardSetup함수


Map함수로 셰이더에 실시간으로 상수버퍼값을 전달합니다. 현재 사용될 셰이더객체와 InputLayout, 상수버퍼를 연결합니다.

매 루프마다 함수가 호출됩니다. 상수버퍼값을 변경하므로서 조명 색깔, 조명 위치를 변경할 수 있습니다.


SceneManager 클래스 


모델의 메쉬데이터와 정점셰이더에 연결된 상수버퍼 객체를 갖고 있습니다.

콜백함수 OnD3D11ResizedSwapChain에서 Init()함수로 초기화 됩니다.


Init 함수

 

DXUT 내장 함수인 CDXUTSDKMesh::Create() 함수로 모델파일을 읽어옵니다. 모델파일안에는 정점, 노말, uv좌표등이 들어 있습니다.

이 함수에서 알아서 버택스버퍼와 인덱스버퍼를 만들어 줍니다. 

정점셰이더에 입력될 상수버퍼를 초기화합니다. 이 상수버퍼에는 월드행렬, 월드뷰투영행렬이 전달됩니다.


DeInit 함수


Init함수에서 만든 객체들을 해제합니다.


RenderForward 함수


매 루프마다 불리는 함수입니다.  extern키워드로 AmbientLight.cpp에 전역변수에 있는 카메라 객체를 받아와서

카메라 객체의 뷰 행렬과 투영행렬을 얻어옵니다. 월드행렬은 원점에 그릴거니까 일단 단위행렬로 설정합니다.

Map 함수로 상수버퍼에 값을 입력 후 정점셰이더에 전달합니다. 전달되는 행렬 값을 바꿔 카메라 이동, 투영 비율 수정,

모델 이동등을 할 수 있습니다.  CDXUTSDKMesh::Render() 함수로 모델객체를 그려줍니다. 

Comments