当前位置: 首页 > news >正文

第四章-初始化Direct3D

首先我们需要一个错误检测和抛出机制

inline std::string ToString(const HRESULT& result)
{char buffer[256];sprintf_s(buffer, "error code : 0x%08X\n", result);return std::string(buffer);
}class MyException : public std::runtime_error
{
public:MyException(const HRESULT& result): std::runtime_error(ToString(result)), m_Result(result){}
private:HRESULT m_Result;
};inline std::string ThrowIfFailed(const HRESULT result)
{if (!SUCCEEDED(result)){throw MyException(result);}
}

下面是我的整体结构

#pragma once
#include <d3d12.h>
#include <dxgi1_6.h>
#include <d3dcompiler.h>
#include <wrl.h>
#include "../d3dx12.h"
#pragma comment(lib, "d3dcompiler.lib")
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "d3d12.lib")
namespace REVIEW
{using Microsoft::WRL::ComPtr;class reviewDirect{public:void init();private:void createDevice();private:ComPtr<ID3D12Device> m_Device;};
}

创建设备

我们之前所说direct3D依赖与设备,比如创建命令队列等。设备代表着一个显示适配器,显示适配器是一种3D图形硬件。但是一个系统也能用软件显示来模拟3D图形硬件的功能。

HRESULT WINAPI D3D12CreateDevice(
_In_opt_ IUnknown* pAdapter,
D3D_FEATURE_LEVEL MinimumFeatureLevel,
_In_ REFIID riid, // Expected: ID3D12Device
_COM_Outptr_opt_ void** ppDevice );
  • pAdapter:指定了创建设备时所用的显示适配器,若将此参数设定为空指针,则使用主适配器。我们目前先设定主适配器
  • MinimumFeatureLevel:应用程序需要硬件所支持的最低功能级别,如果适配器不支持此功能级别,则设备创建失败。目前指定为D3D_FEATURE_LEVEL_11_0(即支持Direct3D 11的特性)
void reviewDirect::createDevice(const ComPtr<IDXGIFactory7>& factory){HRESULT hardwareResult = D3D12CreateDevice(nullptr,D3D_FEATURE_LEVEL_11_0,IID_PPV_ARGS(m_Device.GetAddressOf()));//如果默认的主适配器创建失败,回退到warp设备if (FAILED(hardwareResult)){ComPtr<IDXGIAdapter4> pAdapter;UINT i = 0;factory->EnumWarpAdapter(IID_PPV_ARGS(pAdapter.GetAddressOf()));ThrowIfFailed(D3D12CreateDevice(pAdapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_Device)));}}void reviewDirect::initBase()
{#if defined(DEBUG || defined(_DEBUG)//启动d3d12的调式层ComPtr<ID3D12Debug> pDebug;ThrowIfFailed(D3D12GetDebugInterface(IID_PPV_ARGS(pDebug.GetAddressOf())));pDebug->EnableDebugLayer();#endifComPtr<IDXGIFactory7> pFactory;ThrowIfFailed(CreateDXGIFactory(IID_PPV_ARGS(pFactory.GetAddressOf())));//尝试创建硬件设备createDevice(pFactory);
}
  • 为了进入调试模式,我们首先开启了调试层。随后,Direct3D便会开启额外的调试功能,并在错误发生时向VC++的输出窗口发送类似下面的调试信息

  • 当使用主适配器创建失败以后,程序将回退到软件适配器WARP设备(window advanced Rasterization Platform)

创建围栏并获取描述符的大小

创建好设备以后,就可以为cpu和gpu的同步创建围栏fence了。另外,之前我们说过pipeline使用计算机的资源都是用描述符去引用的,我们需要知道我们的设备可以创建多少个描述符

void reviewDirect::createFence()
{ThrowIfFailed(m_Device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(m_Fence.GetAddressOf())));
}void reviewDirect::calculateNumOfDescriptors()
{m_CbvSrvUavDescriptorSize = m_Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);m_RtvDescriptorSize = m_Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);m_DsvDescriptorSize = m_Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
}

检测对4x MSAA质量级别的支持

void reviewDirect::checkMSAASupport()
{D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msaaQualityLevels;msaaQualityLevels.Format = mBackBufferFormat;msaaQualityLevels.SampleCount = 4;msaaQualityLevels.NumQualityLevels = 0;msaaQualityLevels.Flags = D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE;ThrowIfFailed(m_Device->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,&msaaQualityLevels, sizeof(msaaQualityLevels)));m4xMssaaQuality = msaaQualityLevels.NumQualityLevels;assert(m4xMssaaQuality > 0 && "Unexpected MSAA quality level.");
}

创建命令队列和命令列表

void reviewDirect::createCommandQueueList()
{D3D12_COMMAND_QUEUE_DESC commandQueueDesc {};commandQueueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;commandQueueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;ThrowIfFailed(m_Device->CreateCommandQueue(&commandQueueDesc, IID_PPV_ARGS(m_CommandQueue.GetAddressOf())));ThrowIfFailed(m_Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(m_CommandAllocator.GetAddressOf())));ThrowIfFailed(m_Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_CommandAllocator.Get(), nullptr, IID_PPV_ARGS(m_CommandList.GetAddressOf())));//nodeMask:指定命令列表关联的物理适配器节点,在多 GPU 系统中使用。单 GPU 系统一般设为 0。
// type:D3D12_COMMAND_LIST_TYPE 枚举类型,指定命令列表的类型。常见取值如下:
// D3D12_COMMAND_LIST_TYPE_DIRECT:直接命令列表,可包含所有类型的命令,常用于渲染操作。
// D3D12_COMMAND_LIST_TYPE_COMPUTE:计算命令列表,专门用于计算着色器相关的命令。
// D3D12_COMMAND_LIST_TYPE_COPY:复制命令列表,主要用于数据的复制操作。
// pCommandAllocator:指向 ID3D12CommandAllocator 对象的指针。命令分配器负责管理命令列表所需的内存,每个命令列表都需要关联一个命令分配器。
// pInitialState:指向 ID3D12PipelineState 对象的指针,用于指定命令列表的初始管线状态。如果不需要初始状态,可以传入 nullptr。
// riid:请求的命令列表接口的 IID(接口标识符),通常使用 IID_PPV_ARGS 宏来获取,例如 IID_PPV_ARGS(&commandList)。
// ppCommandList:用于接收创建好的命令列表对象的指针。
}

目前还不会发起任何的绘制命令,所以不会用到流水线状态对象

描述创建交换链

交换链用于交换帧,首先需要填写一份DXGI_SWAP_CHAIN_DESC结构实例

typedef struct DXGI_SWAP_CHAIN_DESC{DXGI_MODE_DESC BufferDesc;DXGI_SAMPLE_DESC SampleDesc;DXGI_USAGE BufferUsage;UINT BufferCount;HWND OutputWindow;BOOL Windowed;DXGI_SWAP_EFFECT SwapEffect;UINT Flags;} 	DXGI_SWAP_CHAIN_DESC;

BufferDesc

  • 类型:DXGI_MODE_DESC
  • 作用:描述后台缓冲区的显示模式,涵盖分辨率、刷新率、像素格式等信息。DXGI_MODE_DESC 结构体定义
typedef struct DXGI_MODE_DESC {UINT Width;UINT Height;DXGI_RATIONAL RefreshRate;DXGI_FORMAT Format;DXGI_MODE_SCANLINE_ORDER ScanlineOrdering;DXGI_MODE_SCALING Scaling;
} DXGI_MODE_DESC;
  • WidthHeight:分别表示缓冲区的宽度和高度(以像素为单位)。
  • RefreshRate:表示屏幕的刷新率,是一个 DXGI_RATIONAL 类型,包含分子和分母,例如 60Hz 可表示为 {60, 1}
  • Format:指定像素格式,如 DXGI_FORMAT_R8G8B8A8_UNORM 代表 32 位的 RGBA 格式。
  • ScanlineOrdering:指定扫描线的排序方式。
  • Scaling:指定缩放模式。

SampleDesc

    • 类型:DXGI_SAMPLE_DESC
    • 作用:描述多重采样的相关信息。DXGI_SAMPLE_DESC 结构体定义如下:
typedef struct DXGI_SAMPLE_DESC {UINT Count;UINT Quality;
} DXGI_SAMPLE_DESC;
  • Count:表示每个像素的采样数量。
  • Quality:表示采样的质量级别。

BufferUsage

    • 类型:DXGI_USAGE
    • 作用:指定后台缓冲区的使用方式,可通过按位或操作组合多个标志。常见的标志有:
      • DXGI_USAGE_RENDER_TARGET_OUTPUT:表示缓冲区将用作渲染目标输出。
      • DXGI_USAGE_SHADER_INPUT:表示缓冲区可作为着色器的输入。

BufferCount

    • 类型:UINT
    • 作用:指定交换链中后台缓冲区的数量。通常为 2(双缓冲)或 3(三缓冲)。双缓冲模式下,一个缓冲区用于渲染,另一个用于显示;三缓冲模式可减少画面卡顿,提升流畅度。

OutputWindow

    • 类型:HWND
    • 作用:指定交换链要呈现到的窗口句柄。这是一个 Windows 窗口句柄,用于将渲染结果显示到指定的窗口中。

Windowed

    • 类型:BOOL
    • 作用:指定交换链是在窗口模式(TRUE)还是全屏模式(FALSE)下运行。

SwapEffect

    • 类型:DXGI_SWAP_EFFECT
    • 作用:指定交换链的呈现方式。常见的取值有:
      • DXGI_SWAP_EFFECT_DISCARD:每次呈现后丢弃旧的缓冲区内容,适用于大多数情况。
      • DXGI_SWAP_EFFECT_FLIP_DISCARD:使用翻转模型进行呈现,可提高性能,是推荐的方式。

Flags

    • 类型:UINT
    • 作用:指定交换链的额外标志,可通过按位或操作组合多个标志。例如 DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH 允许在窗口模式和全屏模式之间切换。
void reviewDirect::createSwapChain(const ComPtr<IDXGIFactory7>& factory)
{ComPtr<IDXGISwapChain> pSwapChain;DXGI_SWAP_CHAIN_DESC desc{};desc.BufferDesc.Width = m_Width;desc.BufferDesc.Height = m_Height;desc.BufferDesc.RefreshRate.Numerator = 60;desc.BufferDesc.Format = mBackBufferFormat;desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;desc.SampleDesc.Count = m4xMssaaQuality ? 4 : 1;desc.SampleDesc.Quality = m4xMssaaQuality ? m4xMssaaQuality - 1 : 0;desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;desc.BufferCount = m_SwapChainBufferCount;desc.OutputWindow = reviewWinApp::GetHwnd();desc.Windowed = true;desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;ThrowIfFailed(factory->CreateSwapChain(m_CommandQueue.Get(), &desc, pSwapChain.GetAddressOf()));pSwapChain.As(&m_SwapChain);}

pSwapChain 是一个 ComPtr<IDXGISwapChain> 类型的智能指针,m_SwapChain 可能是另一种类型的 ComPtr,比如 ComPtr<IDXGISwapChain3> 或者其他派生自 IDXGISwapChain 的接口类型。As 方法会尝试把 pSwapChain 所管理的 IDXGISwapChain 对象转换为 m_SwapChain 所对应的接口类型,若转换成功,m_SwapChain 就会持有转换后的接口指针。

创建描述符堆

我们的描述符需要通过创建描述符堆来存储,对此Direct3D 以ID3D12DescriptorHeap接口表示描述符堆,并用ID3D12Device::CreateDescriptorHeap方法来创建它。这里演示创建rtv的描述符堆(用来指向渲染目标资源)dsv(用来指向深度缓冲区资源)

typedef struct D3D12_DESCRIPTOR_HEAP_DESC
{
D3D12_DESCRIPTOR_HEAP_TYPE Type;
UINT NumDescriptors;
D3D12_DESCRIPTOR_HEAP_FLAGS Flags;
UINT NodeMask;
} 	D3D12_DESCRIPTOR_HEAP_DESC;
void reviewDirect::createDescriptorHeap()
{D3D12_DESCRIPTOR_HEAP_DESC rtvDesc{};rtvDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;rtvDesc.NumDescriptors = m_SwapChainBufferCount;rtvDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;rtvDesc.NodeMask = 0;D3D12_DESCRIPTOR_HEAP_DESC dsvDesc{};dsvDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;dsvDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;dsvDesc.NumDescriptors = 1;dsvDesc.NodeMask = 0;ThrowIfFailed(m_Device->CreateDescriptorHeap(&rtvDesc, IID_PPV_ARGS(m_RtvDescriptorHeap.GetAddressOf())));ThrowIfFailed(m_Device->CreateDescriptorHeap(&dsvDesc, IID_PPV_ARGS(m_DsvDescriptorHeap.GetAddressOf())));
}

书中用到的框架中有以下定义

UINT m_SwapChainBufferCount = 2;
int m_CurrentBackBufferIndex = 0;

其中m_CurrentBackBufferIndex是用来记录当前后台缓冲区的索引的,这样在交换渲染缓冲区和显示缓冲区的时候有用。

获取描述符

有了描述符堆以后,我们还需要通过描述符堆去访问所存的描述符,通过句柄来引用描述符,通过下面的方式来获取:

rtvhandle的获取,获取m_CurrentBackBufferIndex对应的描述符handle

D3D12_CPU_DESCRIPTOR_HANDLE reviewDirect::getCurrentBackBufferView() const
{return CD3DX12_CPU_DESCRIPTOR_HANDLE(m_RtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(),m_CurrentBackBufferIndex, m_RtvDescriptorSize);
}

dsvhandle获取

因为dsv目前也就一个描述符,所以可以直接获取

D3D12_CPU_DESCRIPTOR_HANDLE reviewDirect::getDepthStencilViewHandle() const
{return m_DsvDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
}

创建渲染目标视图

前面创建了描述符堆,描述符的获取,但是有两个问题,首先我们的描述符对应的资源没有创建,其次我们描述符没有绑定对应的资源

渲染目标视图是在swapchain中,因为swapchain管理的就是渲染目标

m_SwapChain->GetBuffer()

virtual HRESULT STDMETHODCALLTYPE GetBuffer( 
/* [in] */ UINT Buffer,
/* [annotation][in] */ 
_In_  REFIID riid,
/* [annotation][out][in] */ 
_COM_Outptr_  void **ppSurface) = 0;
  1. buffer:希望获得的特定的后台缓冲区的索引(有时后台缓冲区并不只一个,所以需要索引指明)
  2. riid:希望获得的id3d12Resource接口的COMID
  3. ppSurface:返回一个指向ID3D12Resource接口的指针,这便是希望获得的后台缓冲区

调用IDXGISwapchain::GetBuffer方法会增加相关后台缓冲区的COM引用计数,所以在每次使用后一定要将其释放。通过ComPtr可以自定的实现这一点

接下来,使用ID3D12Device::CreateRenderTargetView方法来为获取的后台缓冲区创建渲染目标视图(view)实际上也就是创建描述符handle/view 然后绑定后台缓冲区

m_Device->CreateRenderTargetView()

virtual void STDMETHODCALLTYPE CreateRenderTargetView( 
_In_opt_  ID3D12Resource *pResource,
_In_opt_  const D3D12_RENDER_TARGET_VIEW_DESC *pDesc,
_In_  D3D12_CPU_DESCRIPTOR_HANDLE DestDescriptor) = 0;

通过下面的方法就能为每个swapchain对应的buffer创建view(描述符handle)然后写入descriptor heap中

void reviewDirect::createRenderTargetView()
{CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_RtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(),m_CurrentBackBufferIndex, m_RtvDescriptorSize);for (UINT i = 0; i < m_SwapChainBufferCount; i++){ThrowIfFailed(m_SwapChain->GetBuffer(i, IID_PPV_ARGS(m_RenderBuffer[i].GetAddressOf())));m_Device->CreateRenderTargetView(m_RenderBuffer[i].Get(), nullptr, rtvHandle);rtvHandle.Offset(1, m_RtvDescriptorSize);}
}

pDesc 参数设置为 nullptr 时,DirectX 会根据资源的属性自动推断出渲染目标视图的配置,使用资源的默认设置来创建渲染目标视图。这里我们的swapchain已经设置好了其资源格式,就不需要再次创建了

创建深度/模版缓冲区及其视图

之前的swapchain得到的buffer实际上是一种资源, 之前所说深度缓冲区其实就是一种2D纹理。纹理是一种GPU资源因此我们需要填写D3D12_RESOURCE_DESC结构性来描述纹理资源,再用ID3D12Device::CreateCommitedResource方法来创建它。D3D12_RESOURCE_DESC结构体定义如下:

typedef struct D3D12_RESOURCE_DESC
{
D3D12_RESOURCE_DIMENSION Dimension;
UINT64 Alignment;
UINT64 Width;
UINT Height;
UINT16 DepthOrArraySize;
UINT16 MipLevels;
DXGI_FORMAT Format;
DXGI_SAMPLE_DESC SampleDesc;
D3D12_TEXTURE_LAYOUT Layout;
D3D12_RESOURCE_FLAGS Flags;
} 	D3D12_RESOURCE_DESC;
  1. Dimension资源纬度
typedef 
enum D3D12_RESOURCE_DIMENSION{D3D12_RESOURCE_DIMENSION_UNKNOWN	= 0,D3D12_RESOURCE_DIMENSION_BUFFER	= 1,D3D12_RESOURCE_DIMENSION_TEXTURE1D	= 2,D3D12_RESOURCE_DIMENSION_TEXTURE2D	= 3,D3D12_RESOURCE_DIMENSION_TEXTURE3D	= 4} 	D3D12_RESOURCE_DIMENSION;
  1. width,以纹素为单位来表示的纹理宽度。对于缓冲区资源,此项是缓冲区占用的字节数。height,以纹素为单位来表示的纹理高度

GPU的资源都存于堆(heap)中,其本质是具有特定的属性的GPU显存块。ID3D12Device::CreateCommitedResource方法将根据我们所提供的属性创建一个资源与一个堆,并将该资源提交到这个堆中。

virtual HRESULT STDMETHODCALLTYPE CreateCommittedResource( 
_In_  const D3D12_HEAP_PROPERTIES *pHeapProperties,
D3D12_HEAP_FLAGS HeapFlags,
_In_  const D3D12_RESOURCE_DESC *pDesc,
D3D12_RESOURCE_STATES InitialResourceState,
_In_opt_  const D3D12_CLEAR_VALUE *pOptimizedClearValue,
REFIID riidResource,
_COM_Outptr_opt_  void **ppvResource) = 0;typedef struct D3D12_HEAP_PROPERTIES{D3D12_HEAP_TYPE Type;D3D12_CPU_PAGE_PROPERTY CPUPageProperty;D3D12_MEMORY_POOL MemoryPoolPreference;UINT CreationNodeMask;UINT VisibleNodeMask;} 	D3D12_HEAP_PROPERTIES;
  1. pHeapProperties:(资源欲提交至的)堆所具有的属性。有一些属性是针对高级用法而设的。目前只需要关心type,而且这里可以用辅助函数来创建一个默认的。
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT)

type的类型包含

enum D3D12_HEAP_TYPE{//默认堆。向这堆里提交的资源,唯独GPU可以访问。比如GPU会读写深度模板缓冲区,但是CPU//不需要访问它,所以深度/模板缓冲区放默认堆中D3D12_HEAP_TYPE_DEFAULT	= 1,//上传堆。向此堆里提交的都是经CPU上传至GPU的资源D3D12_HEAP_TYPE_UPLOAD	= 2,//回读堆。向这种堆里提交的都是需要由CPU读取的资源D3D12_HEAP_TYPE_READBACK	= 3,//此成员应用于高级的场景D3D12_HEAP_TYPE_CUSTOM	= 4} 	D3D12_HEAP_TYPE;
  1. HeapFlags:与(资源欲提交至的)堆有关的额外选项标志。通常将它设为D3D12_HEAP_FLAG_NONE
  2. pDesc:指向一个D3D12_RESOURCE_DESC实例的指针,用它描述待建资源
  3. InitialResourceState资源的状态。不管什么时候,每个资源都会处于一种特定的使用状态。在资源创建时。需要用此参数来设置它的初始状态。对深度/缓冲区来说,通常设置为D3D12_RESOURCE_STATE_COMMON,在利用之前说的ResourceBarrier方法转换状态,比如转换为写的状态。
  4. pOptimizedClearValue:这个后续在渲染的时候会指定对应的值

void reviewDirect::createDepthStencilView()
{D3D12_RESOURCE_DESC desc{};desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;desc.Alignment = 0;desc.Width = m_Width;desc.Height = m_Height;desc.DepthOrArraySize = 1;desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;desc.SampleDesc.Count = m4xMssaaQuality ? 4 : 1;desc.SampleDesc.Quality = m4xMssaaQuality ? m4xMssaaQuality - 1 : 0;desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;D3D12_CLEAR_VALUE clearValue{};clearValue.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;clearValue.DepthStencil.Depth = 1.0f;clearValue.DepthStencil.Stencil = 0;ThrowIfFailed(m_Device->CreateCommittedResource(&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),D3D12_HEAP_FLAG_NONE,&desc,D3D12_RESOURCE_STATE_COMMON,&clearValue,IID_PPV_ARGS(m_DepthStencilBuffer.GetAddressOf())));
}

这里创建了depth的buffer资源,但是我们需要对depth资源在渲染的时候写入,需要转换资源类型,这里就可以用到临时的commandlist 来处理,首先我先创建了一个templecommandallocator,这样就可以临时的创建多个commandlist,相当于一个临时的公司

void reviewDirect::createTempleCommandAllocator()
{ThrowIfFailed(m_Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(m_TempleCommandAllocator.GetAddressOf())));
}

加上下面这段代码将缓冲区资源转为深度可写入的状态

ComPtr<ID3D12GraphicsCommandList> templeCommandList;
ThrowIfFailed(m_Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_TempleCommandAllocator.Get(),nullptr, IID_PPV_ARGS(templeCommandList.GetAddressOf())));templeCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_DepthStencilBuffer.Get(), D3D12_RESOURCE_STATE_COMMON,D3D12_RESOURCE_STATE_DEPTH_WRITE));

注意这里就必须进行一次命令的执行,因为离开了createDepthStencilView()后临时的命令列表就会释放。这里创建了堵塞cpu的fence使用

void reviewDirect::waitForFinish()
{UINT fenceValue = m_FenceValue;ThrowIfFailed(m_CommandQueue->Signal(m_Fence.Get(), fenceValue));if (m_Fence->GetCompletedValue() < fenceValue){HANDLE handEvent = CreateEventEx(nullptr, nullptr, 0, EVENT_ALL_ACCESS);ThrowIfFailed(m_Fence->SetEventOnCompletion(fenceValue, handEvent));WaitForSingleObject(handEvent, INFINITE);CloseHandle(handEvent);}
}

我们还需要加上描述符来指向这个资源,完整的代码:


void reviewDirect::createDepthStencilView()
{D3D12_RESOURCE_DESC desc{};desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;desc.Alignment = 0;desc.Width = m_Width;desc.Height = m_Height;desc.DepthOrArraySize = 1;desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;desc.SampleDesc.Count = m4xMssaaQuality ? 4 : 1;desc.SampleDesc.Quality = m4xMssaaQuality ? m4xMssaaQuality - 1 : 0;desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;D3D12_CLEAR_VALUE clearValue{};clearValue.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;clearValue.DepthStencil.Depth = 1.0f;clearValue.DepthStencil.Stencil = 0;ThrowIfFailed(m_Device->CreateCommittedResource(&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),D3D12_HEAP_FLAG_NONE,&desc,D3D12_RESOURCE_STATE_COMMON,&clearValue,IID_PPV_ARGS(m_DepthStencilBuffer.GetAddressOf())));CD3DX12_CPU_DESCRIPTOR_HANDLE viewHandle(m_DsvDescriptorHeap->GetCPUDescriptorHandleForHeapStart());m_Device->CreateDepthStencilView(m_DepthStencilBuffer.Get(), nullptr, viewHandle);ComPtr<ID3D12GraphicsCommandList> templeCommandList;ThrowIfFailed(m_Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_TempleCommandAllocator.Get(),nullptr, IID_PPV_ARGS(templeCommandList.GetAddressOf())));templeCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_DepthStencilBuffer.Get(), D3D12_RESOURCE_STATE_COMMON,D3D12_RESOURCE_STATE_DEPTH_WRITE));waitForFinish();ID3D12CommandList* commandLists[] = { templeCommandList.Get() };m_CommandQueue->ExecuteCommandLists(_countof(commandLists), commandLists);waitForFinish();
}

设置视口

我们通常会将3D场景绘制到与整个屏幕(在全屏模式下)或整个窗口工作区大小相当的后台缓冲区。但是,有时只是希望3D场景绘制到后台缓冲区的某个矩形子区域当中,如下图:

我们把后台缓冲区中的这种矩形子区域叫做视口,并通过下列结构描述它:

typedef struct D3D12_VIEWPORT
{
FLOAT TopLeftX;
FLOAT TopLeftY;
FLOAT Width;
FLOAT Height;
FLOAT MinDepth;
FLOAT MaxDepth;
} 	D3D12_VIEWPORT;
  1. FLOAT TopLeftX
    • 该成员表示视口矩形区域左上角的 X 坐标,以像素为单位。它指定了视口在屏幕上的水平起始位置。
  1. FLOAT TopLeftY
    • 此成员表示视口矩形区域左上角的 Y 坐标,同样以像素为单位。它指定了视口在屏幕上的垂直起始位置。
  1. FLOAT Width
    • 表示视口矩形区域的宽度,以像素为单位。这个值决定了视口在水平方向上的大小。
  1. FLOAT Height
    • 表示视口矩形区域的高度,以像素为单位。它决定了视口在垂直方向上的大小。
  1. FLOAT MinDepth
    • 定义了视口的最小深度值,范围通常是 [0.0f, 1.0f]。在深度测试时,只有深度值大于等于 MinDepth 的像素才会被考虑。
  1. FLOAT MaxDepth
    • 定义了视口的最大深度值,范围同样是 [0.0f, 1.0f]。在深度测试时,只有深度值小于等于 MaxDepth 的像素才会被考虑。
void reviewDirect::createViewPort()
{m_RenderViewPort.TopLeftX = 0;m_RenderViewPort.TopLeftY = 0;m_RenderViewPort.Width = m_Width;m_RenderViewPort.Height = m_Height;m_RenderViewPort.MinDepth = 0.0f;m_RenderViewPort.MaxDepth = 1.0f;
}

后续可以设置视口,这个我在后面在写

事实上,视口可以用作分屏游戏。首先创建两个视口,一个占屏幕左半部分,一个右半部分,接下来,在左视口中以玩家1的视角来绘制3D场景,在右视口中以玩家2的视角来绘制3D场景即可。

设置裁剪矩形

我们可以相对后台缓冲区定义一个裁剪矩形,在此矩形外的像素都将被剔除(即这些图像部分将不会被光栅化)至后台缓冲区。这个方法能用于优化程序的性能。例如,假设已知有一个矩形的UI元素覆盖于屏幕的某块区域的最上层,那么我们也就无须对覆盖区域绘制

void reviewDirect::createRECT()
{m_ScissorRect.left = 0;m_ScissorRect.top = 0;m_ScissorRect.right = m_Width;m_ScissorRect.bottom = m_Height;
}

相关文章:

  • Blink和V8的关系
  • 雅思阅读--重点短语/句式39个
  • Day 12 训练
  • PostgreSQL存储过程“多态“实现:同一方法名支持不同参数
  • C语言进阶—函数(static,递归,回调,指针,内联,变参,结构体尺寸)
  • DID在元宇宙的应用爆发:数字身份资产化与跨平台迁移——解析Decentraland等项目的虚拟身份全链路实现
  • 25G 80km双纤BIDI光模块:远距传输的创新标杆
  • 蓝桥杯-通电(最小生成树java)
  • 盛最多水的容器
  • UE5 MetaHuman眼睛变黑
  • 软件设计师-软考知识复习(3)
  • 【强化学习】什么是强化学习?2025
  • 解决 Exception in thread “main“ java.lang.NoClassDefFoundError
  • 【java】程序设计基础 八股文版
  • 深入理解 Web 架构:从基础到实践
  • 0506--01-DA
  • tinyrenderer笔记(Phong光照模型)
  • QML ProgressBar控件详解
  • C++高性能内存池
  • 逻辑越权--登录和支付数据篡改
  • 印官员称巴基斯坦在克什米尔实控线附近开火已致7死38伤
  • 吴清:全力支持中央汇金公司发挥好类“平准基金”作用
  • 长三角铁路五一假期发送旅客超2000万人次,同比增幅超一成
  • 非洲中青年军官代表团访华,赴北京、长沙、韶山等地参访交流
  • IPO周报|节后首批3只新股本周申购,色谱设备龙头来了
  • 伯克希尔董事会投票决定:阿贝尔明年1月1日起出任CEO,巴菲特继续担任董事长