이전 포스트에서 실행 기반을 다졌으니 이제 실제 동작을 위한 기본적인 렌더러를 제작하려고 합니다. 그러기 위해서 일단 렌더러의 추상화 작업을 해주어야 합니다. 

이렇게 추상화 작업을 해주어야지 구현과 사용이 분리되어 차후에 렌더러가 수정되더라도 사용자 부분에 영향이 안가고 유지보수가 좋아집니다. 또 한, DirectX 11 이든 OpenGL 이든 다른 렌더러가 추가 되더라도 사용자 부분에서는 구현 부분에 대해 알필요없이 사용이 가능하므로 확장성 또한 좋아지겠죠.

class ev_IRenderer
{
public:
	ev_IRenderer( void ) {};
	virtual ~ev_IRenderer( void ) {};

public:
	virtual HRESULT Initialize( HWND hRenderWnd ) = 0;
	virtual HRESULT Destroy( void ) = 0;
	virtual void Render( ev_CRenderNodePtr spRenderNode ) = 0;
};

대략 위와 같은 모습을 띄고 있습니다. 클래스 이름은 인터페이스를 뜻하는 I를 붙입니다. 그리고 순수 가상함수가 있구요(추상 클래스니까요). 눈여겨 볼점은 Render 함수에서 그려질 객체를 직접 받는다는 겁니다. 보통 DirectX 초중급 책자들을 보면 객체를 그릴때 객체 내에서 그리는 경우가 많습니다. 예로 메시를 그린다 하면 메시 클래스 내에 Render 함수가 있고 메시 내에서 Device를 얻어다가 직접 그리죠.

이렇게 될 경우 이 메시 클래스는 해당 렌더러에 완전 종속되게 되고 DirectX 11을 지원하게 되거나 OpenGL을 지원하거나 또는 같은 DirectX 9를 쓰더라도 렌더 방식이 달라지거나 하면 (지연 렌더링 같이) 하면 수정이 불가피해집니다. 게다가 동시에 여러 렌더러를 지원하기도 힘들어지죠.

그래서 그리는 것을 해당 렌더러에서 그리게 하고 메시 클래스는 렌더러에서 해당 객체를 그리기 위한 데이터만 제공하도록 할 예정입니다.

다음은 위의 추상 클래스를 상속받아 만든 DirectX 9 Deferred Renderer Class 의 대략적인 모습이니다.

class ev_CRendererDX9Deferred : ev_IRenderer
{
public:
	ev_CRendererDX9Deferred( void );
	virtual ~ev_CRendererDX9Deferred( void );

public:
	virtual HRESULT Initialize( HWND hRenderWnd );
	virtual HRESULT Destroy( void ) ;
	virtual void Render( ev_CRenderNodePtr spRenderNode );

private:
	LPDIRECT3D9			m_pD3D;
	LPDIRECT3DDEVICE9	m_pD3DDevice;
	D3DCAPS9			m_D3DCaps;

	bool				m_bIsWireFrame;
};

대략 이처럼 나오겠죠. 가상 함수들을 해당 렌더러에 맞게 내부를 구현해줍니다. 또 한, 외부에서는 해당 렌더러가 DirectX 9/10/11을 사용할지 OpenGL을 사용할지 알 수 없습니다. 그렇기 때문에 Matrix/Vector 같은 3D객체를 그리기 위한 데이터등은 모두 자체 포맷으로 제작을 합니다. DirectX의 의존성을 없애기 위해 D3D 데이터 포맷을 사용하지 않죠.

렌더러 클래스의 구현이 끝나면 이제 사용만 해주면 됩니다. 사용을 할때는 인터페이스 클래스격인 ev_IRenderer로 사용하면 됩니다.

// 대략 적인 흐름도

// 환경에 맞는 렌더러 생성
ev_IRenderer* m_pRenderer = (ev_IRenderer*)new ev_CRendererDX9Deferred();

// 렌더러 초기화 ( 화면을 그릴 윈도우의 핸들을 넘겨준다 )
m_pRenderer->Initialize( (HWND)hRenderView.ToPointer() );

// 메인 루프
MSG msg;
ZeroMemory( &msg, sizeof(msg) );
while( WM_QUIT != msg.message )
{
	// 종료 플래그 검사
	if( m_bExitFlag )
		break;

	// 메시지큐에 메시지가 있으면 메시지 처리
	if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
	{
		TranslateMessage( &msg );
		DispatchMessage( &msg );
	}
	else
	{
		// 객체 렌더링
		m_pRenderer->Render( /*그릴 객체를 넘겨줌*/ );
	}
}

// 렌더러 소멸
Safe_Delete( m_pRenderer );
실제로는 씬노드( SceneNode )와 씬노드를 관리할 씬 매니저를 통해 렌더러에 그려질 객체등을 넘겨주겠지만, 대략적인 흐름만 파악하기 위해 위 처럼 코딩하였습니다. 이 후에 씬노드쪽도 새로 구현이 되면 그에 관해서도 포스팅 하겠습니다.

댓글 유도를 위한 의미없는 짤방

  1. BlogIcon redmist 2010.11.16 10:14

    댓글 드...드리겠습니다!
    절대로 짤방 때문은 아닙니다.

  2. BlogIcon 정기 2010.11.16 13:56

    저도 저런 걸 생각하다가 렌더러가 동적을 계속해서 바뀌는 게 아닌데도 가상함수로서 구현해야하는지 의문이 들더라구요.사실 렌더러에 다형성이 필요한 것도 아닐 테지요. 더군다나 렌더러의 메소드들은 정말로 자주 불리는 메서드들인데 말입니다. 그러던 도중에 네뷸라 디바이스에서는 저 상속 구조를 거꾸로 뒤집는 것을 보았는데, 한 번 참고해보시는 것도 좋을 것 같습니다.

  3. 은령 2010.11.16 21:24

    MOKO군요.

    그럼 저 처자는 중국인이란 얘긴데.....

  4. BlogIcon 구차니 2010.11.20 09:32 신고

    으헉! 짤방에 승복하였습니다 OTL



    하아.. c++ 공부하다가 말고 또 다시 엉뚱한대로 새어버린 구차니군입니다 ㅠ.ㅠ
    vtigerCRM 한글번역중에 웹서버 구축중 ㅠ.ㅠ

  5. BlogIcon Opheliasong 2011.05.01 02:07

    하악 하악 짤방에 의해 댓글을 달게 되는군요!! ㅋㅋ

    D3D와의 연계성을 줄인다니 초보인 저로썬 생각도 못했는데 재미있는 자료를 구하게된거 같아 감사합니다 ^^

    • BlogIcon 친절한티스 2011.05.01 22:29 신고

      처음에는 별생각 없었지만, 요즘 DX11도 부상하고 있고, OpenGL도 해보고 싶은 욕심에 위와 같은 생각을 하게됐쬬.

+ Recent posts