프로그래밍 2013. 12. 8. 14:36
728x90


둘다 같은 구현입니다.

 

directx9 쉐이더 프로그래밍 책에서는 깊이 버퍼 그림자로,
쉐이더 프로그래밍 입문 책에서는 그림자매핑으로 이름짓고 있습니다.

위 두권의 책을 참고했습니다.

 

기본개념은

렌더타겟을 마련하여,
그림자를 만들고 싶은 오브젝트의 깊이값을 저장해두고,
(이 작업이 그림자맵 생성입니다.)


그림자가 드리워질 오브젝트를 그릴때
렌더타겟 텍스쳐로부터 깊이값을 가져와서
자신의(현재 그릴 오브젝트) 픽셀의 깊이값과 비교하여
자신의 깊이값이 렌더타겟에서 가져온 깊이값보다 큰 경우에는
자신의 픽셀이 렌더타겟의 픽셀에게 가리워져 있다라고 판단하여,
자신의 픽셀 색상을 어둡게 만들어 주는 것입니다.(그림자가 드리워진 것처럼)

 

비교대상인 깊이값은 라이트와의 거리가 저장된 것이라 볼수 있습니다.
내 픽셀의 라이트와의 거리가 렌더타겟에서 가져온 픽셀의 라이트와의 거리보다 멀다면,
이미 가려져 있다라고
판단하는 것이지요.

 

코드를 훑어보겠습니다.

 

1. 렌더타겟 생성과 설정

 

#define qRelease(p)         { if(p) { p->Release(); p=NULL; } }
#define zTex                    LPDIRECT3DTEXTURE9
#define zSurf                   LPDIRECT3DSURFACE9


 zTex m_pRenderTargetTex;
 zSurf m_pRenderTargetSurf;

 zSurf m_pOrigSurf; // ref
 zSurf m_pOrigStencil; // ref


void
stRShader::Create_Render_Target( const int _iW, const int _iH )
{
 DXOUT( D3DDEVICE->CreateTexture( _iW, _iH , 1, D3DUSAGE_RENDERTARGET, D3DFMT_R32F, D3DPOOL_DEFAULT, &m_pRenderTargetTex, NULL ) )
 DXOUT( D3DDEVICE->CreateDepthStencilSurface( _iW, _iH, D3DFMT_D24X8, D3DMULTISAMPLE_NONE, 0, TRUE, &m_pRenderTargetSurf, NULL ) )
}

 

void
stRShader::Delete_Render_Target()
{
 qRelease( m_pRenderTargetTex );
 qRelease( m_pRenderTargetSurf );
}

 

//--------------------------------------------------------------
//FUNC: Start_RT
//DESC: must call end_RT()
//--------------------------------------------------------------
void
stRShader::Start_RT()
{
 D3DDEVICE->GetRenderTarget( 0, &m_pOrigSurf );
 D3DDEVICE->GetDepthStencilSurface( &m_pOrigStencil );

 LPDIRECT3DSURFACE9 pTargetSurf;
 m_pRenderTargetTex->GetSurfaceLevel( 0, &pTargetSurf );
 D3DDEVICE->SetRenderTarget( 0, pTargetSurf );
 qRelease( pTargetSurf ); //must ref count dec

 D3DDEVICE->SetDepthStencilSurface( m_pRenderTargetSurf );
 
 //clear target buffer
 D3DDEVICE->Clear( 0, NULL, (D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER), 0xffffffff, 1.f, 0 );

}

 

//--------------------------------------------------------------
//FUNC: End_RT
//DESC: must call start_RT()
//--------------------------------------------------------------
void
stRShader::End_RT()

 D3DDEVICE->SetRenderTarget( 0, m_pOrigSurf );
 D3DDEVICE->SetDepthStencilSurface( m_pOrigStencil );
 qRelease( m_pOrigSurf ); //must ref count dec
 qRelease( m_pOrigStencil );
}


대략 코드를 보면, 이해되리라 생각됩니다.


Create_Render_Target 을 미리 한번만 실행하여, 렌더타겟이 될 텍스쳐와 서페이스를 생성해 둡니다.

 

그후는
필요할때마다
Start_RT()
End_RT()
를 호출하여 그림자를 남길 오브젝트를 렌더타겟에 렌더링 해줍니다.


호출순서는 이렇습니다.

 

 Start_RT()
 그림자를 남길 오브젝트.render() - 깊이정보를 남길 쉐이더코드가 포함됩니다.
 End_RT()

 

End_RT() 호출시 기본 D3D렌더타겟( 우리가 보통 장면을 그릴때 사용하는 타겟 )으로 다시 설정이 돌아갑니다.

 

 그림자가 드리워질 오브젝트.render() - 깊이 비교를 하고 그리는 쉐이더코드가 포함됩니다.

 

남기고자 하는 그림자가 제대로 출력되는지 확인해보려면
D3DXSaveTextureToFileA( stFull.c_str(), D3DXIFF_JPG, stRShader::GetInst()->Get_Render_Target_Tex(), NULL );


파일로 직접 저장해서 이미지를 확인해 보는 것이 좋습니다.

 

사용법은 대략
D3DXSaveTextureToFileA( 파일이름, 포맷, 렌더타겟텍스쳐, NULL );
이런식입니다.

 

단, 그냥 저장해서는 모두 흰색으로 나올 가능성이 큽니다.

그림자 생성의 픽셀 쉐이더에서 최종적인 컬러값을 약간 임의적으로 수정해 놓아야

모양이 제대로 출력될 것입니다.

 

Start_RT()
안에서 타겟버퍼를 클리어할때는
색상을 0xffffffff (최고 흰색)으로 주어야 합니다.

최고 흰색값은 깊이값에서 가장 큰값입니다.
그림자가 없는 부분은 이 최고 흰색값으로 채워줘야,
추후 깊이값 비교에서 오류가 생기지 않습니다.

 

내 픽셀의 컬러값에 그림자를 씌워주는 것은
내 픽셀의 깊이값이 렌더타겟의 픽셀의 깊이값보다 큰 경우일 뿐이기
때문입니다. ( 나보다 앞에 있는 것이 있으면 가려졌다고 판단 )

 

렌더타겟의 깊이값이 최고흰색이라면,
내 픽셀의 깊이값보다 작을 수가 없겠지요.
나를 가리고 있지 않습니다.
따라서 그림자가 생기지 않습니다.

 


 

728x90
posted by BK dddDang
: