编写DX12时使用的辅助类
有一段时间没有学DX12,导致很多东西都忘了,跟着教程里写的东西还好,略看一遍教程就想起来的,但是自己封装的很多类就算写了注释过了一段时间也基本忘光,而且翻来翻去的也不方便,为了快速记起来写一下实现思路。
Singleton
简介
需要进行显式初始化,并且可控制生命周期的模板单例类。
主要接口
Create
template<typename... Args>
static void Create(Args... args);
显式初始化函数,可通过参数包创建一个全局实例。
ShutDown
static void ShutDown() noexcept;
销毁当前的实例对象,若是后续需要继续使用,需要调用Create
重新显式初始化。
主要变量
m_Instance
inline static T* m_Instance = nullptr;
当前的实例。
注意事项
使用前需要进行显式构造。同时由于父类无法访问子类的protected构造函数,所以若子类不是抽象类,则需要将Singleton设置为友元,例如:
class LightManager : public Singleton<LightManager>
{
protected:friend Singleton<LightManager>;
}
BaseImGuiManager
简介
Imgui管理抽象基类,继承自Singleton为单例类,为了让单例获取到的实例能够使用子类自定义的成员函数或变量,使用了奇异递归模板模式实现。
主要接口
InitImGui
virtual bool InitImGui(ID3D12Device* device, HWND hMainWnd, int bufferCount, DXGI_FORMAT bufferFormat);
使用默认模式初始化Imgui,子类可重写后自定义Imgui模式。
UpdateImGui
virtual void UpdateImGui(const CpuTimer& timer) = 0;
纯虚函数,子类通过该函数自定义GUI,会在公有成员函数Update中调用,每帧更新。
RenderImGui
virtual void RenderImGui(ID3D12GraphicsCommandList* cmdList);
使用命令列表设置描述符堆之后绘制Imgui。
LightManager
用于管理光源的单例类。
主要接口
LightManager
LightManager(ID3D12Device* device,UINT frameCount,int maxDirLight = 5,int maxPointLight = 5,int maxSpotLight = 5);
构造函数,需要传入D3D设备和帧资源的个数,同时还限定了光源的最大个数。
CreateLightBuffer
void CreateLightBuffer(ID3D12Device* device);
在构造函数中调用,在默认堆中根据帧资源的个数创建光源的常量缓冲区资源,后续若是实现堆管理则改为分配在自行管理的堆中。
Set-xxx-Light
void Set-xxx-Light(std::size_t index, const DirectionalLight& light);
三个设置不同光源的函数,xxx表示了方向光、点光、聚光灯三种灯光的一种,会检测当前的光源数量是否饱和,因此无需注意设置的光源是否达到上限。
GetGPUVirtualAddress
D3D12_GPU_VIRTUAL_ADDRESS GetGPUVirtualAddress() const;
获取当前帧对应的光源资源的GPU虚拟地址。
GetLightsShaderMacros
std::array<D3D_SHADER_MACRO, 4> GetLightsShaderMacros(const char* dirName,const char* pointName,const char* spotName) const;
获取三种光源数量的宏,需要输入在Shader中三个宏的名字。
UpdateLight
void UpdateLight();
更新官员,若是光源没有变化则只会更新光源资源的索引,不会发生拷贝。
主要变量
m_LightsUploaders
ComPtr<ID3D12Resource> m_LightsUploaders;
所有帧资源的光源的数据。
m_FrameCount
const UINT m_FrameCount;
帧资源个数。
m_NumFrameDirty
UINT m_NumFrameDirty;
用于判断当前数据是否变更,避免不必要的拷贝,当SetLight后,该数字变为帧资源个数。
GeometryMesh
简介
包含顶点及索引的几何网格数据。
主要接口
GetIndices16
std::vector<std::uint16_t>& GetIndices16() noexcept
获取每个索引为两字节的索引数据。
主要变量
m_Vertices
网格的所有顶点数据,其中顶点数据如下:
struct Vertex
{DirectX::XMFLOAT3 m_Position;DirectX::XMFLOAT3 m_Normal;DirectX::XMFLOAT4 m_Tangent;DirectX::XMFLOAT3 m_BiTangent;DirectX::XMFLOAT2 m_TexCoord;
};
m_Indices32
网格的所有索引,每个索引四个字节。
ModelMesh
简介
导入模型时使用的数据。
主要变量
struct ModelMesh
{std::string m_Name;Geometry::GeometryMesh m_MeshData;DirectX::BoundingBox m_BoundingBox;UINT m_MaterialIndex;
};
包含网格名字、网格的顶点及索引、包围盒、当前网格所使用的材质的索引。
Model
简介
模型类,包含模型名字、模型网格和材质。
主要接口
LoadModelFromFile
bool LoadModelFromFile(const std::string& filename);
从文件中加载模型,调用Assimp库获取模型数据,后将模型数据处理成自定义的模型网格和材质。
ProcessNode
void ProcessNode(aiNode* node, const aiScene* scene);
使用递归的方式处理模型数据中的所有节点,获取网格和材质。
ProcessMesh
ModelMesh ProcessMesh(aiMesh* mesh, const aiScene* scene);
提取Assimp中的网格及材质数据。
主要变量
m_Meshs
std::map<std::string, ModelMesh> m_Meshs;
所有的模型网格数据。
m_Materials
std::vector<Material> m_Materials;
所有的材质数据。
MeshData
简介
处理成ID3D12Resource
后的网格数据,同时包含了顶点缓冲区和索引缓冲区所需要的数据,储存了子网格的绘制索引。
主要方法
GetVertexBufferView
D3D12_VERTEX_BUFFER_VIEW GetVertexBufferView() const;
获取顶点缓冲区的资源视图。
GetIndexBufferView
D3D12_INDEX_BUFFER_VIEW GetIndexBufferView() const;
获取索引缓冲区的资源视图。
DisposeUploaders
void DisposeUploaders();
释放上传缓冲区的内存。
主要成员
struct MeshData
{// 几何体集合的名字,便于索引std::string m_Name;// 系统内存中的副本,顶点和索引可为范式类型,用Blob表示,可在特定的时候转换ComPtr<ID3DBlob> m_VertexBufferCPU = nullptr;ComPtr<ID3DBlob> m_IndexBufferCPU = nullptr;// GPU资源ComPtr<ID3D12Resource> m_VertexBufferGPU = nullptr;ComPtr<ID3D12Resource> m_IndexBufferGPU = nullptr;ComPtr<ID3D12Resource> m_VertexBufferUploader = nullptr;ComPtr<ID3D12Resource> m_IndexBufferUploader = nullptr;// 缓冲区相关数据UINT m_VertexByteStride = 0;UINT m_VertexBufferByteSize = 0;DXGI_FORMAT m_IndexFormat = DXGI_FORMAT_R16_UINT;UINT m_IndexSize = 0;UINT m_IndexBufferByteSize = 0;// 一个MeshData可以统一储存多个几何体,使用下列容器可单独绘制每个几何体std::map<std::string, SubmeshData> m_DrawArgs;
};