이 글과 관련된 이전 글
2011/02/09 - 내 맘대로 엔진 제작기 - 29 ( 우버 쉐이더 Uber Shader )
2010/06/17 - 내 맘대로 엔진 제작기 - 10 ( 쉐이더 Shader )
쉐이더 관련 데이터를 어떻게 관리해야 편할까 많이 고민 중입니다. 이것저것 많이 시도하고 있는 중인데, 현재 적용시킨 부분을 간단히 적어볼까 합니다. 대부분의 내용은 이전 포스트에 적었던 우버 쉐이더 관련 PT에서 많은 힌트를 얻어 구현 하였습니다.
데이터 포맷은 작업시 가독성과 편집이 용이한 XML을 이용하였습니다. 우버 쉐이더 작성을 위한 Define 리스트를 적습니다.
<Defines> <Define>_SHADER_USE_DIFFUSE_MAP_</Define> <Define>_SHADER_USE_NORMAL_MAP_</Define> <Define>_SHADER_USE_SKINNING_</Define> </Defines>위의 경우 DiffuseMap과 NormalMap, Skinning을 사용하는 쉐이더라는 것을 알 수 있습니다. DiffuseMap과 NormalMap을 사용하니 해당 텍스쳐 경로도 같이 넣어줍니다.
<Texture> <DiffuseMap0 FileName=".\Resource\Texture\cobblestonesDiffuse.tga"></DiffuseMap0> <NormalMap FileName=".\Resource\Texture\cobblestonesNormal.tga"></NormalMap> </Texture>그 다음 파라미터 값입니다. 쉐이더에는 많은 양의 파라미터 값을 지정해줘야합니다. 간단히 메시를 화면에 출력해주기 위해서는 월드행렬, 뷰행렬, 투영행렬이 필요하고, 텍스쳐를 입히기 위해서는 텍스쳐가 필요하고, 각종 효과를 주기위해서는 그 에따른 추가 파라미터 값들이 필요합니다. 그리고 이 파라미터 값을 지정해주기 위해서는 해당 파라미터의 핸들이 필요합니다.
한가지 염두하고 있는 것이 코드 수정의 최소화입니다. 쉐이더의 파라미터가 추가되거나 삭제가 되더라도 그 에 따른 코드 수정이 적어야 한다는거죠. 데이터 중심의 개발( Data-Driven Development )이 되어야 합니다. 쉐이더 변수명이나 Semantics를 이용하면 쉐이더 변수의 핸들을 얻어올수 있는데, 이것을 이용하여 쉐이더 파라미터를 맵핑합니다.
<Semantics> <Semantic Name="WORLD"/> <Semantic Name="WORLDVIEW"/> <Semantic Name="VIEWPROJECTION"/> <Semantic Name="AMBIENTCOLOR" ValueCount="4" Value1="255" Value2="255" Value3="255" Value4="255" /> <Semantic Name="DIFFUSEMAP0"/> <Semantic Name="NORMALMAP"/> </Semantics>위의 경우 월드행렬, 월드뷰행렬, 뷰프로젝션행렬, 환경광등을 정의하는 부분입니다. 이것을 파싱하여, 해당 쉐이더 효과를 사용시 자동으로 파라미터 값을 설정하도록 합니다.
typedef void (ev_CEffectApply::*fpParameterFunction)( const stSemanticData& ); typedef stdext::hash_map< wstring, fpParameterFunction > EffectParameterFunc; m_EffectParameterFunc_HashMap.insert( make_pair( L"WORLD", &ev_CEffectApply::ApplyWorldMatrix ) ); m_EffectParameterFunc_HashMap.insert( make_pair( L"WORLDVIEW", &ev_CEffectApply::ApplyWorldViewMatrix ) ); m_EffectParameterFunc_HashMap.insert( make_pair( L"VIEWPROJECTION", &ev_CEffectApply::ApplyViewProjMatrix ) ); m_EffectParameterFunc_HashMap.insert( make_pair( L"WORLDVIEWPROJECTION", &ev_CEffectApply::ApplyWorldViewProjMatrix ) ); m_EffectParameterFunc_HashMap.insert( make_pair( L"DIFFUSEMAP0", &ev_CEffectApply::ApplyDiffuseMap0 ) ); m_EffectParameterFunc_HashMap.insert( make_pair( L"DIFFUSEMAP1", &ev_CEffectApply::ApplyDiffuseMap1 ) ); m_EffectParameterFunc_HashMap.insert( make_pair( L"NORMALMAP", &ev_CEffectApply::ApplyNormalMap ) ); m_EffectParameterFunc_HashMap.insert( make_pair( L"SPECULARMAP", &ev_CEffectApply::ApplySpecularMap ) ); m_EffectParameterFunc_HashMap.insert( make_pair( L"AMBIENTCOLOR", &ev_CEffectApply::ApplyAmbientColor ) );
파라미터 설정하는 부분은 콜백 함수를 사용해보기로 했습니다. 쉐이더 파라미터를 할당 해주는 클래스를 하나 만들어 각 파라미터를 설정해주는 함수를 만듭니다. 그리고 각 함수를 Semantic을 키 값으로 하여, 콜백 함수로 정의합니다. 이렇게 해서 파싱된 Semantic 리스트를 통해 자동으로 파라미터 할당 함수가 호출 되도록 구현하였습니다.
// 파싱된 semantics 을 순차 실행 for each( stSemanticData semantic in semantics_vector ) { EffectParameterFuncIT it = m_EffectParameterFunc_HashMap.find( semantic.strSemantic ); if( m_EffectParameterFunc_HashMap.end() != it ) { // 콜백 함수 호출 (this->*it->second)( semantic ); } }
아 이 캐 치