프로그래밍 2013.12.08 14:36


둘다 같은 구현입니다.

 

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 (최고 흰색)으로 주어야 합니다.

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

 

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

 

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

 


 

저작자 표시 비영리 변경 금지
신고
posted by 죠운죠운

댓글을 달아 주세요

  1.  Addr  Edit/Del  Reply namoeye

    궁금한 점이 있는데요..
    왜 꼭 깊이 버퍼 그림자를 만들때는
    CreateDepthStencilSurface 이함수로 왜 깊이버퍼 텍스쳐를 만들어야 하는지 모르겠습니다.

    위에 렌더 타겟을 생성할때 R32F로 생성했으면...R에 단순히 깊이값을 저장하면 되는것 아닌가요..?

    추가로..
    최초에 D3D 디바이스 초기화 할때

    D3DPRESENT_PARAMETERS _rPP

    _rPP.AutoDepthStencilFormat = D3DFMT_D24X8;
    _rPP.EnableAutoDepthStencil = TRUE;

    이렇게 설정해서 사용해도 되는것 아닌가요..ㅜ 궁금합니다.

    2016.10.30 22:08 신고
  2.  Addr  Edit/Del  Reply Favicon of http://bkjcr.tistory.com BlogIcon 죠운죠운

    안녕하세요. 이야, 질문도 다 받아보고, 오래간 만이라 좋네요 ㅎㅎ ^^

    물론, 별도로 생성시켜야 하는 (CreateDepthStencilSurface 로 만들어진,) 써페이스가 반드시 필요하지는 않습니다,
    보통은 디바이스 생성시에 화면 크기와 동일한 뎁스를 만들어 둡니다,
    그런데, 기본적인 써페이스 크기가 RT 보다 작을수가 있습니다, 화면사이즈는 1920*1080 인데, 그림자 RT 는 2048*2048 크기라면, D3DDEVICE->Clear() 함수 호출하면서, RT 의 깊이를 초기화할때, 문제가 생깁니다, 크기가 안맞으니, 클리어되지 않는 부분이 생깁니다,
    그래서, 큰 텍스쳐RT를 만들때는 큰 텍스쳐 크기에 맞춘 써페이스를 별도로, 생성해서, 사용하게 됩니다,

    2016.10.31 17:39 신고
  3.  Addr  Edit/Del  Reply Favicon of http://bkjcr.tistory.com BlogIcon 죠운죠운

    위에 파라메터대로 사용하시는 것 물론 맞습니다, ㅎㅎ
    기본 화면에서 사용하는 써페이스를 생성하는 과정이라 보시면 되구요,
    화면해상도와 동일한 써페이스를 만들죠

    제 경우, 화면사이즈와 동일한 RT (후처리 다른거 할때)를 사용할때는
    따로 써페이스를 만들지 않았구요,
    그림자 경우는 화면 사이즈와 상관없이, 더 큰, 텍스쳐 크기를 사용할수 있습니다.
    1024, 2048, 4096 , 사이즈가 커질수록 그림자 퀄리티가 올라가니까요,

    2016.10.31 17:44 신고
  4.  Addr  Edit/Del  Reply Favicon of http://bkjcr.tistory.com BlogIcon 죠운죠운

    앗, 그러고보니,
    RT 에 z값을 기록하는데, 왜 별도의 뎁스서페이스가 필요한가?
    없어도 되지 않는가? 어차피 같은 값인데,

    저도 좀 헷갈리네요, ㅎㅎ
    그런데, 이 뎁스서페이스가 없으면, RT 에 무언가를 그릴수 없을 겁니다,

    뎁스서페이스는 device 에서 사용하는 영역으로,
    이 버퍼를 통해서, 은면제거, z 값 관련 특수 효과 등이 처리됩니다만,

    RT 에 렌더링을 걸때 최종 기록되는 값은
    device 에서 뎁스서페이스를 통해 걸러낸 값이 기록된다
    가 맞을 겁니다,
    그래서 뎁스서페이스가 없다면, 아무것도 그려지지 않게 될 것입니다,
    기록되는 값의 타입을 떠나, 무조건 필요한
    뭐 그런 개념일듯 합니다,

    일단 저는 이렇게 이해를 하고 있습니다만, 오류가 있을수도 있습니다,
    혹시 제가 잘못알고 있는 것이 있다면, 꼭 말씀주세요. 같이 알아야죠 ㅎㅎ


    2016.10.31 19:10 신고
  5.  Addr  Edit/Del  Reply Favicon of http://bkjcr.tistory.com BlogIcon 죠운죠운

    ㅎㅎ, 물론 상관은 없습니다만,
    linear depth ?? 라는 것은 처음들어봅니다만,
    아마 제가 모르는 것일듯 합니다 후후후,

    2016.10.31 19:35 신고
  6.  Addr  Edit/Del  Reply Favicon of http://bkjcr.tistory.com BlogIcon 죠운죠운

    ㅇㅇ, 찾아보니, G버퍼 사용관련한 내용이네요,
    저는 디퍼드 렌더링이 아니라서,,, 딱히 아는 내용은 아닙니다 ㅎㅎ
    저는 그냥 일반적인 렌더링을 사용합니다,

    2016.10.31 19:38 신고


티스토리 툴바