怎么在百度做网站,wordpress的Portfolio,只做鱼网站,怎么做网站公众号一、前置要求
Windows 10及以上(安装有DirectX12)VisualStudio 2022
二、DirectX12入门
1.引用头文件
#includeWindows.h
#included3d12.h
#includedxgi1_4.h2.注册窗口类并初始化窗口
这里我们调用Windows API 通过应用程序的句柄来注册一个唯一…
一、前置要求
Windows 10及以上(安装有DirectX12)VisualStudio 2022
二、DirectX12入门
1.引用头文件
#includeWindows.h
#included3d12.h
#includedxgi1_4.h2.注册窗口类并初始化窗口
这里我们调用Windows API 通过应用程序的句柄来注册一个唯一的窗口类并创建窗口实例。我们可以根据需求去创建多个窗口或者唯一窗口。
注意这里并不是DX12的功能而是WinAPI的功能。
int Window::WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{//注册窗口类WNDCLASSEX wc { 0 };//结构大小wc.cbSize sizeof(WNDCLASSEX);//窗口样式 水平大小改变重绘|竖直大小改变重绘|窗口拥有自己的绘制设备|检测鼠标双击事件wc.style CS_HREDRAW | CS_VREDRAW|CS_OWNDC| CS_DBLCLKS;//这一句是窗口过程函数的初始化如果有需要可以在Window里定义一个静态函数//函数签名如下也可也不定义自己处理MSG(下文会提到)//LRESULT CALLBACK Window::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)//wc.lpfnWndProc Window::WndProc;//指向应用程序实例的句柄wc.hInstance hInstance;//指定窗口的鼠标样式wc.hCursor LoadCursor(NULL, IDC_ARROW);//设备窗口背景刷wc.hbrBackground (HBRUSH)COLOR_WINDOW;//唯一的窗口类名称确保窗口类在系统中是唯一的不会与其他窗口类冲突。wc.lpszClassName LDXRenderer;//注册窗口类在这里我们可以实现一个应用只能打开一次的效果if (!RegisterClassExW(wc)){MessageBox(NULL, L窗口类注册失败, L错误, MB_ICONERROR);}//创建窗口句柄HWND hwnd CreateWindow (LDXRenderer, LDXRenderer, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);//展示窗口nCmdShow有大量的宏标志可以使用表示不同的打开方式ShowWindow(hwnd, nCmdShow);//一般在Show后立刻调用一次立即更新窗口保证良好的响应UpdateWindow(hwnd);
}3.创建绘图设备接口(Device)
绘图设备接口是沟通GPU的重要接口是对硬件层的隔离和隔离并提供了更低级的GPU控制以及对资源的管理任务调度(可以创建多个命令队列以多线程方式进行绘图)。
在这一步我们不需要将Device绑定到窗口而是在后文通过交换链进行绑定。
//创建绘图设备
ID3D12Device* device nullptr;
D3D12CreateDevice(NULL, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(device));
4.创建Fence
Fence翻译为栅栏是CPU与GPU同步的主要手段Fence提供了跟踪GPU任务的能力。
//创建Fence
ID3D12Fence* fence nullptr;
UINT64 fenceValue 0;
d3d12Device-CreateFence(fenceValue, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(fence));5.获取RTV,DSV[CBV,SRV,UAV]描述符大小
RTV (渲染目标视图) 将渲染目标与图形渲染管线相关联。它允许图形程序将渲染的结果输出到一个或多个渲染目标上。DSV (深度/模板视图)通常与深度缓冲区和模板缓冲区相关联。深度缓冲区用于存储场景中各个像素的深度信息而模板缓冲区用于存储与像素相关的模板信息。DSV允许GPU读取或写入深度和模板信息CBV (常量缓冲视图) 用于将常量缓冲区与着色器绑定。常量缓冲区包含了常量数据如变换矩阵、材质属性等这些数据可以在渲染过程中传递给着色器以影响渲染结果。CBV允许GPU在着色器中访问这些常量数据。SRV (着色资源视图) 用于将纹理、缓冲区和其他资源与着色器绑定。它允许GPU在着色器中读取纹理数据、结构化缓冲区数据等。SRV是一种通用视图可以与各种不同类型的资源相关联。UAV (无序访问视图) 允许GPU在着色器中对缓冲区进行无序访问通常用于实现计算着色器中的并行计算任务。UAV允许着色器读取、写入和重新排序缓冲区中的数据而不需要显式的同步。 // 获取RTV描述符大小
UINT rtvSize d3d12Device-GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
// 获取DSV描述符大小
UINT dsvSize d3d12Device-GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
//获取CBV,SRV,UAV描述符大小
//在DX12中这三者的描述符都存储在相同的堆使用相同的API获取
UINT cbvSrvUavSize d3d12Device-GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
注意要保存这些大小并不是一次性就丢弃。
6.检测多重采样抗锯齿(MSAA)的支持性
MSAA的原理是在渲染图像时对每个像素位置进行多次采样。根据像素覆盖的多个采样点的值来计算最终像素的颜色。这些采样点通常位于像素中心和其周围的位置。
D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msaaQualityLevels;
// 根据你的渲染目标格式设置默认是32位RGBA
msaaQualityLevels.Format DXGI_FORMAT_R8G8B8A8_UNORM;
//采样数量
msaaQualityLevels.SampleCount 4;
//无特殊标志
msaaQualityLevels.Flags D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE;
//质量级别设置
msaaQualityLevels.NumQualityLevels 0;
//检测MSAA支持会修改NumQualityLevels的值如果不支持则为0
HRESULT hr d3d12Device-CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,msaaQualityLevels,sizeof(msaaQualityLevels)
);//根据MSAA支持来启用
if (SUCCEEDED(hr) msaaQualityLevels.NumQualityLevels 0)
{// MSAA支持msaaQualityLevels.NumQualityLevels表示支持的质量级别
}
else
{// 不支持MSAA
}7.创建命令队列(Command Queue)
命令队列是从属于Device的一个Device可以有多个命令队列(你可以创建不同的队列以负责不同类型的职责)CPU的可以通过任意线程把渲染命令添加到命令队列。
在这一部分我们不介绍如何把绘制命令添加到命令队列里。而是在后文交换链之后再进行介绍以拥有一个合适的基础。
//创建命令队列
ID3D12CommandQueue* commandQueue nullptr;
//创建命令队列描述信息
D3D12_COMMAND_QUEUE_DESC queueDesc {};
// 指定命令队列类型为图形命令队列
queueDesc.Type D3D12_COMMAND_LIST_TYPE_DIRECT;
// 队列优先级设为正常
queueDesc.Priority D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
// 无特殊标志
queueDesc.Flags D3D12_COMMAND_QUEUE_FLAG_NONE;
//为绘图设备创建命令队列
d3d12Device-CreateCommandQueue(queueDesc, IID_PPV_ARGS(commandQueue));8.创建命令分配器(Command Allocator)
在介绍命令分配器之前我们先介绍命令列表(Command List)在DX12等图形编程API中通常不允许直接向命令队列提交绘制命令一般只允许向命令队列提交命令列表在提交命令列表时会按照命令列表中相同命令顺序为命令队列添加命令。
而命令列表的创建和重用一般情况下依赖于命令分配器所以我们先创建一个命令分配器并在下一个步骤中进行命令列表的创建并对命令列表进行详细介绍。
//创建命令分配器
ID3D12CommandAllocator* commandAllocator;
HRESULT hr d3d12Device-CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(commandAllocator));9.创建命令列表(Command List)
通过命令列表我们对低级的指令进行抽象和封装使得功能变得更加易用。
每个命令分配器都与命令列表一一对应命令分配器为命令列表分配内存管理资源和状态。
命令列表可以重用具体的方式在后文解释同样的命令列表的提交也将在后文进行。 这些都需要交换链作为基础才能成功被绘制。
//创建命令列表
ID3D12GraphicsCommandList* commandList;
HRESULT hr d3d12Device-CreateCommandList(0, // NodeMask默认为0D3D12_COMMAND_LIST_TYPE_DIRECT, // 使用Direct Command ListcommandAllocator, // 命令分配器nullptr, // Pipeline State ObjectPSO可以稍后设置IID_PPV_ARGS(commandList)
);10.创建交换链(Swap Chain)
交换链的作用是在这一帧图像未渲染完毕时保留上一帧的图像等待这一帧渲染完毕后直接替换上一帧的画面。这样使得我们看到连续的画面而不是介于两帧之间的未渲染完成的画面。 一般来说如果不使用这种技术将导致画面闪烁图像撕裂等问题
同时交换链内维护有缓冲区存储渲染结果待合适时机一起输出同时这种缓冲区大小与Windows窗口紧密相关自动维护合适的大小。
SwapChain有多个版本这里以SwapChain1作为案例升级SwapChain3Windows版本不同可能对支持的交换链API有影响。
//创建交换链3
ComPtrIDXGISwapChain3 swapChain ;//交换链描述符
DXGI_SWAP_CHAIN_DESC1 swapChainDesc {};
//窗口大小信息
swapChainDesc.Width width; // 窗口宽度
swapChainDesc.Height height; // 窗口高度
//像素格式
swapChainDesc.Format DXGI_FORMAT_R8G8B8A8_UNORM; // 像素格式
//是否启用3D展示(通常用于3D眼镜需要硬件支持一般false)
swapChainDesc.Stereo false;
//不与MSAA冲突的多重采样
swapChainDesc.SampleDesc.Count 1; // 多重采样设置
swapChainDesc.SampleDesc.Quality 0;
//设置后台缓冲区的用途是作为渲染目标(还可以用作着色器读取)
swapChainDesc.BufferUsage DXGI_USAGE_RENDER_TARGET_OUTPUT;
//缓冲区数量
swapChainDesc.BufferCount bufferCount;
// 设置渲染目标与窗口大小不匹配时的缩放模式
//DXGI_SCALING_NONE不进行缩放
//DXGI_SCALING_STRETCH渲染目标将被拉伸以完全填充窗口
//DXGI_SCALING_ASPECT_RATIO_STRETCH保持渲染目标的宽高比的情况下将其拉伸以填充窗口
swapChainDesc.Scaling DXGI_SCALING_ASPECT_RATIO_STRETCH;
// 交换效果定义了在每次呈现后如何处理后台缓冲区并如何切换前台和后台缓冲区
swapChainDesc.SwapEffect DXGI_SWAP_EFFECT_FLIP_DISCARD;
//设置交换链如何处置透明像素 DXGI_ALPHA_MODE_PREMULTIPLIED 或 DXGI_ALPHA_MODE_STRAIGHT
swapChainDesc.AlphaMode DXGI_ALPHA_MODE_IGNORE;
//无标志
swapChainDesc.Flags 0;//创建交换链1(较为低级)
ComPtrIDXGISwapChain1 swapChain1;
ThrowIfFailed(dxgiFactory-CreateSwapChainForHwnd(commandQueue.Get(), // 命令队列hwnd, // 窗口句柄swapChainDesc, // 交换链描述符nullptr, // 全屏模式nullptr, // 允许输出swapChain1 // 交换链对象
));// 尝试升级到IDXGISwapChain3接口
ThrowIfFailed(swapChain1.As(swapChain));//升级成功
if(swapChain3)
{}
//升级失败
else
{}11.创建RTV/DSV描述符堆
这里假设我们只是双重缓冲(1个显示1个后缓冲)我们需要建立数量为2的RTV描述符。 并且我们需要一个深度缓冲描述符用于当前GPU计算使用
这里也假定交换链升级成功
//创建描述符堆的描述符并指定属性
D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc {};
rtvHeapDesc.NumDescriptors 2; // 描述符数量通常等于后台缓冲区的数量
rtvHeapDesc.Type D3D12_DESCRIPTOR_HEAP_TYPE_RTV; // 描述符堆类型RTV
rtvHeapDesc.Flags D3D12_DESCRIPTOR_HEAP_FLAG_NONE; // 无特殊标志ComPtrID3D12Resource backBuffer;
//获取缓冲区索引
UINT bufferIndex swapChain-GetCurrentBackBufferIndex();
//获取后台缓冲区
HRESULT hr swapChain-GetBuffer(bufferIndex, IID_PPV_ARGS(backBuffer.GetAddressOf()));//创建描述符堆
ComPtrID3D12DescriptorHeap rtvHeap;
HRESULT hr device-CreateDescriptorHeap(rtvHeapDesc, IID_PPV_ARGS(rtvHeap.GetAddressOf()));//创建RTV句柄
D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle rtvHeap-GetCPUDescriptorHandleForHeapStart();
// 假设 backBuffer 是后台缓冲区的资源对象
device-CreateRenderTargetView(backBuffer.Get(), nullptr, rtvHandle);
// 移动到下一个描述符
rtvHandle.ptr rtvDescriptorSize; // rtvDescriptorSize 是描述符的大小//创建DSV描述符堆的描述符
D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc {};
dsvHeapDesc.NumDescriptors 1; // 单个深度/模板视图
dsvHeapDesc.Type D3D12_DESCRIPTOR_HEAP_TYPE_DSV; // 描述符堆类型DSV
dsvHeapDesc.Flags D3D12_DESCRIPTOR_HEAP_FLAG_NONE; // 无特殊标志
//创建dsv描述符堆
ComPtrID3D12DescriptorHeap dsvHeap;
hr device-CreateDescriptorHeap(dsvHeapDesc, IID_PPV_ARGS(dsvHeap.GetAddressOf()));
D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle dsvHeap-GetCPUDescriptorHandleForHeapStart();//创建深度缓冲区资源对象 depthBuffer
UINT width 1920; // 深度缓冲区的宽度
UINT height 1080; // 深度缓冲区的高度
DXGI_FORMAT format DXGI_FORMAT_D24_UNORM_S8_UINT; // 深度/模板格式
UINT sampleCount 1; // 采样数通常为 1不使用多重采样
UINT sampleQuality 0; // 采样质量级别
//----------------------------------------------------
D3D12_RESOURCE_DESC depthDesc {};
depthDesc.Dimension D3D12_RESOURCE_DIMENSION_TEXTURE2D; // 资源维度
depthDesc.Width width; // 宽度
depthDesc.Height height; // 高度
depthDesc.DepthOrArraySize 1; // 深度或数组大小通常为 1
depthDesc.MipLevels 1; // Mip 层级数量
depthDesc.Format format; // 像素格式
depthDesc.SampleDesc.Count sampleCount; // 采样数
depthDesc.SampleDesc.Quality sampleQuality; // 采样质量级别
depthDesc.Layout D3D12_TEXTURE_LAYOUT_UNKNOWN; // 布局类型
depthDesc.Flags D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; // 资源标志允许深度/模板
//深度缓冲区清除值
D3D12_CLEAR_VALUE clearValue {};
clearValue.Format format; // 清除值的格式必须与深度缓冲区的格式匹配
clearValue.DepthStencil.Depth 1.0f; // 初始深度值通常为 1.0
clearValue.DepthStencil.Stencil 0; // 初始模板值通常为 0ComPtrID3D12Resource depthBuffer; // 用于存储深度缓冲区资源
HRESULT hr device-CreateCommittedResource(CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), // 堆属性D3D12_HEAP_FLAG_NONE, // 堆标志depthDesc, // 资源描述符D3D12_RESOURCE_STATE_COMMON, // 初始资源状态clearValue, // 清除值IID_PPV_ARGS(depthBuffer.GetAddressOf()) // 输出深度缓冲区资源对象
);// 假设 depthBuffer 是深度缓冲区的资源对象
device-CreateDepthStencilView(depthBuffer.Get(), nullptr, dsvHandle);
12.设置视口
D3D12_VIEWPORT viewport {}; // 创建视口对象
viewport.TopLeftX 0.0f; // 视口左上角的X坐标
viewport.TopLeftY 0.0f; // 视口左上角的Y坐标
viewport.Width static_castfloat(clientWidth); // 视口的宽度
viewport.Height static_castfloat(clientHeight); // 视口的高度
viewport.MinDepth 0.0f; // 最小深度
viewport.MaxDepth 1.0f; // 最大深度//在渲染过程中在每次渲染前将视口的属性设置到渲染管道中的命令列表Command List中以便将其传递给GPU。
commandList-RSSetViewports(1, viewport); // 设置视口
13.提交和重用命令列表
(1)重置
//命令列表的重置是可选的
commandList-Reset(commandAllocator.Get(), nullptr);
//重置分配器是必须的
commandAllocator-Reset();
(2)提交
// 记录需要的GPU命令
commandList-SetGraphicsRootSignature(rootSignature.Get());
commandList-SetPipelineState(pipelineState.Get());
commandList-SetGraphicsRootDescriptorTable(0, descriptorHeap.GetGPUDescriptorHandleForHeapStart());// 执行绘制操作
commandList-DrawIndexedInstanced(/*参数*/);//记录命令之后需要关闭
commandList-Close();
//commandQueue 是命令队列对象ppCommandLists 是指向命令列表指针的数组。
commandQueue-ExecuteCommandLists(1, CommandListType, ppCommandLists);
14.可运行时调整的设定 用来渲染到屏幕上的RTV本身的格式(也就是说我们如果需要更改渲染贴图的大小只需要在运行时直接更改RTV描述符并且替换掉原本的RTV) Swap Chain 也可以在运行时更改设定比如说运行时启用/停用MSAA