第二部分对应书本的Part 3。
这部分起主要是DirectX的几种常见的应用场景。因此实际上理论知识的部分将减少,更多关注算法和实现的部分。
因此笔记将结合实际的Demo代码来完成。
接下来的几篇文章中,每篇文章都将围绕一个或多个topic,根据书本和提供源码实习一个Demo,结合代码和书本来讲解相关的知识。
代码的地址依然是https://github.com/ZealotAlie/D11Demo0,所有的新Demo我都会放到同一个Solution中,选择不同的Project将可以运行不同的Demo。
为了避免每次新建Project都需要重新配置,我新建了一个项目配置表DemoPropertySheet,只需要在新建项目中使用配置好的项目配置表就可直接完成配置,极大的节省了时间。
项目配置表可以在View->OtherWindow->PropertyManager中打开
右键某个项目即可新建一个表,双击之后可以像一般项目一样编辑表的属性。当新建项目时,只需要右键新项目,选择Add Existing Property Sheet即可将配置表应用到新项目中,从而避免重复配置。
另外,由于接下来的Demo中会有许多新的shader,或者要使用到之前的shader,所以我在项目中添加了一个单例类用于管理所有的Effect/InputLayout/RenderState,具体实现看UnknownInstance.h。
class IUnknownInstance
{
public:
virtual ~IUnknownInstance(){}
};
template< class T, bool AUTO_INSTANCE = false, class TInstance = T >
class UnknownInstance : public IUnknownInstance
{
public:
UnknownInstance()
{
if( sm_pWarpInstance == nullptr )
{
sm_pWarpInstance = static_cast<T*>( this );
}
}
~UnknownInstance()
{
if( sm_pWarpInstance == this )
{
sm_pWarpInstance = nullptr;
}
}
static TInstance* Ptr()
{
return PtrWithInitialization< AUTO_INSTANCE >();
}
private:
template< bool DoInstance >
static TInstance* PtrWithInitialization()
{
return sm_pWarpInstance? static_cast<TInstance*>( sm_pWarpInstance->GetResource() ) : nullptr;
}
template<>
static TInstance* PtrWithInitialization<true>()
{
if( !sm_pWarpInstance )
{
sm_pWarpInstance = new T( D3DApp::GetInstance()->GetDevice() );
D3DApp::GetInstance()->AddUnknownInstance(sm_pWarpInstance);
}
return static_cast<TInstance*>( sm_pWarpInstance->GetResource() );
}
static T* sm_pWarpInstance;
};
基本思路是,在App中添加一个列表用于存放这些单例,在App关闭的时候释放它们的资源。
单例分为两类,一类是可以自动完成初始化的,这类资源直接在第一次调用Ptr()函数时就会自动初始化。
而如果你需要在初始化时使用一些额外参数(如effect需要路径信息),则需要手动用new构造一个实例并添加到D3DApp中
AddUnknownInstance( new BasicEffect( md3dDevice, L"FX/Basic.fx" ) );
所有的UnknownInstance都继承自IUnknownInstance。这个接口只定义了一个虚析构函数,因为我们需要在D3DApp中存放单例的指针,所以需要一个统一的基类,并且需要虚析构函数,否则资源将无法被正确释放(这里不能使用void*作为存放的指针,因为delete void* 是不会调用析构函数的)。