1.引擎初始化
Factory:
CreateDXGIFactory(IID_PPV_ARGS(&DXGIFactary))
Device:创建Fence
HRESULT D3dDeviceResult = D3D12CreateDevice(NULL, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&D3DDevice));
if (FAILED(D3dDeviceResult))
{
//warp:高级光栅化平台
//如果硬件适配器创建失败就创建软件适配器。
ComPtr<IDXGIAdapter> WARPAdapter;
ANALYSIS_HRESULT(DXGIFactary->EnumWarpAdapter(IID_PPV_ARGS(&WARPAdapter)));
ANALYSIS_HRESULT(D3D12CreateDevice(WARPAdapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&WARPAdapter)));
}
1.1交换链
解决了画面撕裂问题
主要是前置缓冲区和后置缓冲去的交换
数量可以自定义,一般为2个。
//写在宏ANALYSIS_HRESULT里可能引起崩溃,所以用Result
Result = DXGIFactary->CreateSwapChain(CommandQueue.Get(), &SwapChainDesc, SwapChain.GetAddressOf());
ANALYSIS_HRESULT(Result);
1.2深度缓存区
像素上和缓冲区(交换链提的缓冲区)一 一对应,用来判断遮蔽和距离摄像头的距离(范围在0-1)。
1.3Fence(围栏)
控制CPU和GPU,防止它们相互等待
ANALYSIS_HRESULT(D3DDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&D3DFence)));
1.4命令队列和列表、分配器
每个GPU都有一个命令队列(ComPtr<ID3D12CommandQueue> CommandQueue;)
CPU是命令列表(ComPtr<ID3D12GraphicsCommandList> GraphicsCommandList;),负责将命令提交到队列中;
命令储存在分配器中(ComPtr<ID3D12CommandAllocator> CommandAllocator;),前两者的交互都要通过适配器
参数说明:
Queue
D3D12_COMMAND_QUEUE_DESC:
.Type
D3D12_COMMAND_LIST_TYPE
{
D3D12_COMMAND_LIST_TYPE_DIRECT//直接会被GPU执行的命令
D3D12_COMMAND_LIST_TYPE_BUNDLE//运行外部程序对少量API命令打包,使得前面DIRECT声明的命令可以重复使用,减少命令列表对CPU的负载
D3D12_COMMAND_LIST TYPE COMPUTE//指定用于计算的命令缓冲区
D3D12_COMMAND_LIST TYPE_COPY//指定用于复制的命令缓冲区
D3D12_COMMAND_LIST_TYPE_VIDEO_DECODE//指定用于视频解码的命令缓冲区
D3D12_COMMAND LIST TYPE VIDEO PROCESS//指定用于处理视频的命令缓冲区
D3D12_COMMAND_LIST TYPE_VIDEO_ENCODE//指定用于视频编码的命令缓冲区
}
.Priority:队列优先级
// D3D12_COMMAND_QUEUE_PRIORITY_NORMAL = 0,
// D3D12_COMMAND_QUEUE_PRIORITY_HIGH = 100,
// D3D12_COMMAND_QUEUE_PRIORITY_GLOBAL_REALTIME = 10000
.NodeMask:指示命令队列在哪个GPU节点执行,默认0,只有一个GPU时不用设置
D3D12_COMMAND_QUEUE_DESC QueueDesc = {};
QueueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;//直接
QueueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;//不知道命令执行需要多长时间,最好设置不限时间
ANALYSIS_HRESULT(D3DDevice->CreateCommandQueue(&QueueDesc, IID_PPV_ARGS(&CommandQueue)));
ANALYSIS_HRESULT(D3DDevice->CreateCommandAllocator(
D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(CommandAllocator.GetAddressOf())
));
ANALYSIS_HRESULT(D3DDevice->CreateCommandList(
0,//默认单GPU
D3D12_COMMAND_LIST_TYPE_DIRECT,
CommandAllocator.Get(),//关联
NULL,//ID3D12PipelineState
IID_PPV_ARGS(GraphicsCommandList.GetAddressOf())
));
GraphicsCommandList->Close();
1.5多重采样
解决锯齿问题
缓冲区四倍于正常缓冲区
SSAA(超级采样):假设像素大小(1280x720),简而言之先乘以4(变成4个像素表示前面的一个像素),再取4个像素的平均值,最后压缩(除4).可以得到一个很好的图形,但是消耗很大
MSAA(多重采样):每个像素分成四个格子,(相比SSAA不用每个像素计算一次,只采样中心点),经过深度测试或者模板测试,和子像素的多边形是在里面还是外面求一个中间值,最后将这些像素分配到周围的像素格子里
//多重采样
D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS QualityLevels;
QualityLevels.SampleCount = 4;
QualityLevels.Flags = D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE;
QualityLevels.NumQualityLevels = 0;
ANALYSIS_HRESULT(D3DDevice->CheckFeatureSupport(
D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
&QualityLevels,
sizeof(QualityLevels)
));
M4XQualityLevels = QualityLevels.NumQualityLevels;
//采样描述 DXGI_SWAP_CHAIN_DESC SwapChainDesc;
SwapChainDesc.SampleDesc.Count = bMSAA4XEnabled ? 4 : 1;
SwapChainDesc.SampleDesc.Quality = bMSAA4XEnabled ? (M4XQualityLevels - 1) : 0;
1.6纹理
DXGI_FORMAT
//DXGI_SWAP_CHAIN_DESC SwapChainDesc;
SwapChainDesc.BufferDesc.Format = BufferFormat;//纹理
1.7资源描述符 相比DX9直接绑定对象,使得DX12更加轻量级
RTV、DSV:交换链渲染目标和深度/模板资源
//描述符和堆
ComPtr<ID3D12DescriptorHeap> RTVHeap;
ComPtr<ID3D12DescriptorHeap> DSVHeap;
//资源描述符
//
//RTV
// D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV //CBV常量缓冲视图 着色器资源视图 无序视图
// D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER //采样视图
// D3D12_DESCRIPTOR_HEAP_TYPE_RTV //渲染目标资源试图
// D3D12_DESCRIPTOR_HEAP_TYPE_DSV //深度/模板的视图资源
//
D3D12_DESCRIPTOR_HEAP_DESC RTVDescriptorHeapDesc;
RTVDescriptorHeapDesc.NumDescriptors = FEngineRenderConfig::GetEngineRenderConfig()->SwapChainCount;
RTVDescriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
RTVDescriptorHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
RTVDescriptorHeapDesc.NodeMask = 0;//默认
ANALYSIS_HRESULT(D3DDevice->CreateDescriptorHeap(
&RTVDescriptorHeapDesc,
IID_PPV_ARGS(RTVHeap.GetAddressOf())));
D3D12_DESCRIPTOR_HEAP_DESC DSVDescriptorHeapDesc;
DSVDescriptorHeapDesc.NumDescriptors = 1;
DSVDescriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
DSVDescriptorHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
DSVDescriptorHeapDesc.NodeMask = 0;//默认
ANALYSIS_HRESULT(D3DDevice->CreateDescriptorHeap(
&DSVDescriptorHeapDesc,
IID_PPV_ARGS(DSVHeap.GetAddressOf())));
1.8后台缓冲区绑定到渲染流水线
(缓冲区和资源描述符的关系就像商品筐和商品标签一样)
//描述符和堆
ComPtr<ID3D12DescriptorHeap> RTVHeap;
ComPtr<ID3D12DescriptorHeap> DSVHeap;
//后台缓冲区
vector<ComPtr<ID3D12Resource>> SwapChainBuffer;
ComPtr<ID3D12Resource> DepthStencilBuffer;
UINT RTVDescriptorSize;
RTV
//拿到描述Size
RTVDescriptorSize = D3DDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
D3D12_CPU_DESCRIPTOR_HANDLE HeapHandle = RTVHeap->GetCPUDescriptorHandleForHeapStart();
HeapHandle.ptr = 0;
for (int i = 0; i < FEngineRenderConfig::GetEngineRenderConfig()->SwapChainCount; i++)
{
//后台缓冲绑定到渲染流水线_ComPtr<IDXGISwapChain> SwapChain;
SwapChain->GetBuffer(i, IID_PPV_ARGS(&SwapChainBuffer[i]));
//SwapChainBuffer[i].Get():指定缓冲区
//nullptr:默认 后台缓冲区格式_ComPtr<ID3D12Device> D3DDevice;
D3DDevice->CreateRenderTargetView(SwapChainBuffer[i].Get(), nullptr, HeapHandle);
HeapHandle.ptr += RTVDescriptorSize;
}
//DSV
D3D12_RESOURCE_DESC ResourceDesc;
ResourceDesc.Width = FEngineRenderConfig::GetEngineRenderConfig()->ScreenWidth;
ResourceDesc.Height = FEngineRenderConfig::GetEngineRenderConfig()->ScreenHight;
ResourceDesc.Alignment = 0;
ResourceDesc.MipLevels = 1;
ResourceDesc.DepthOrArraySize = 1;
ResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
ResourceDesc.SampleDesc.Count = bMSAA4XEnabled ? 4 : 1;
ResourceDesc.SampleDesc.Quality = bMSAA4XEnabled ? (M4XQualityLevels - 1) : 1;
ResourceDesc.Format = DXGI_FORMAT_R24G8_TYPELESS;
ResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
ResourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
D3D12_CLEAR_VALUE ClearValue;
ClearValue.DepthStencil.Depth = 1;
ClearValue.DepthStencil.Stencil = 0;
ClearValue.Format = DepthStencilFormat;
D3D12_HEAP_PROPERTIES D3DHeapProperties;
D3DHeapProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
D3DDevice->CreateCommittedResource(
&D3DHeapProperties,//CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT)
D3D12_HEAP_FLAG_NONE, &ResourceDesc,
D3D12_RESOURCE_STATE_COMMON, &ClearValue,
IID_PPV_ARGS(DepthStencilBuffer.GetAddressOf())//指定资源提交到的缓冲区
);
D3D12_DEPTH_STENCIL_VIEW_DESC DSVDesc;
DSVDesc.Format = DepthStencilFormat;
DSVDesc.Texture2D.MipSlice = 0;
DSVDesc.Flags = D3D12_DSV_FLAG_NONE;
DSVDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
D3DDevice->CreateDepthStencilView(DepthStencilBuffer.Get(), &DSVDesc, DSVHeap->GetCPUDescriptorHandleForHeapStart());
1.9添加D3DX12.h
//深度模板
typedef
enum D3D12_HEAP_TYPE
{
D3D12_HEAP_TYPE_DEFAULT = 1,//只有GPU访问
D3D12_HEAP_TYPE_UPLOAD = 2,//GPU所需要的资源不需要经过CPU上传
D3D12_HEAP_TYPE_READBACK = 3,//内容都需要CPU读取
D3D12_HEAP_TYPE_CUSTOM = 4//自定义
} D3D12_HEAP_TYPE;
1.10为什么要ResourceBarrier?
目的:资源同步和资源表达。(类似读写锁)
CD3DX12_RESOURCE_BARRIER ResourceBarrier = CD3DX12_RESOURCE_BARRIER::Transition(DepthStencilBuffer.Get(),
D3D12_RESOURCE_STATE_COMMON,
D3D12_RESOURCE_STATE_DEPTH_WRITE);
GraphicsCommandList->ResourceBarrier(
1,
&ResourceBarrier
);
GraphicsCommandList->Close();
//提交命令
ID3D12CommandList* CommandList[] = { GraphicsCommandList.Get() };
CommandQueue->ExecuteCommandLists(_countof(CommandList), CommandList);
1.11Debug
//Debug
ComPtr<ID3D12Debug> D3D12Debug;
if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&D3D12Debug))))
{
D3D12Debug->EnableDebugLayer();
}
2.基础
用4x4矩阵表示3维向量,3x3矩阵表示2维向量...
2.1三维空间转化到屏幕向量
m:物体在空间中相对摄像机的位置(WorldMatrix\ViewMatrix)
V、P、P:视口空间、投影空间、透视空间。统称:MVP。
三维空间的一个点到屏幕空间的过程:
M*V*P把 世界空间转化为齐次裁剪空间,再映射到NDC空间/w,最后映射到屏幕空间。(NDC空间在OpenGL中为[-1,1])
一、求WorldMatrix
通过Matrix计算位移
摄像机在零点时的Matrix:
位移矩阵
两者点成后得到位移后的Matrix:
P={X,Y,Z}
所以新的位置坐标为={R. P, U.P, F.P};
缩放矩阵:
结果:
图解:到近剪裁面
FOV:顶角
n:顶点到近剪裁面的距离
f:顶点到远剪裁面的距离
W/H:屏幕的宽高比
假设,
,
侧视图:
,
,
,
近剪裁面映射到NDC
概念:对于远剪裁面,相当于是把所有点挤压到一个和近剪裁面等大的正方形再进行正交映射 。因为距离足够远时,物体的大小是一样的
NDC是一个[-1,1]的立方体
光栅化:把东西画屏幕上(采样:判断像素中心和三角形的关系)
采样:
判断一个点是否在三角形内:三条边分别用起点和该点组成的向量叉乘,都为正(或者负)即在三角形内(右手,手指方向就是正,如>0)
问题:锯齿和走样
走样的解决方法:先模糊再采样
概念:
1.时域的图片通过傅里叶变换成频域的图。
2.时域的卷积=频域的乘积 ,时域的乘积=频域的卷积
3.采样就是重复原始信号的频谱,因为采用率低,时谱间隔大,频谱间隔小,高频部分在频谱上发生重叠,所以会走样,反走样就是低频过滤(不要高频,即模糊处理)再进行采样
4.图像通过信号的形式传播,每一个像素对应一个值,合为一个函数。时谱采用就是对这个函数不同的X取样(乘冲击函数),对应频谱上这个函数卷积对应的冲击函数。
模糊处理前后对比(不要高频)
深度缓冲ZBuffer:
所以的光栅化都要深度测试。ZBuffer处理不了透明物体
着色shading:
材质和光照相互影响:光照使材质不同位置有明亮变化,材质对光进行了漫反射。
着色不考虑物体是否存在,在考虑点自己,所以着色不和阴影有关
Blinn-Phong Reflectance model:是经验形模型,不考虑损耗
Diffuse Reflection(漫反射)
一个屏幕上同一位置在平面角度不同时,明亮程度不同,因为反射角度不同导致的接收光能量不同。
点光源的能力传递:
多少光在shading point被接收?为什么有颜色?
:光的吸收率,0时为黑(全吸收),1为白色。用三维(RGB)表示时即可表示颜色(0-1).
:和V无关,因为漫反射是四面八方反射的
max:n点乘l为负数时无意义,所以取0
高光
高光和半程向量有关(本来是视线和反射角度的夹角,但是半程向量更加好算)。
:cos(点乘就是余弦值)的容忍度太大了,即使45°也是一个大于0.5的较大的树,所以求P次幂减低容忍度,在
几度时就会得到很小的值,不会产生高光(P是控制高光大小的,
是控制高光强度的)。
环境光
作用:让一些地方不会完全黑
假设Shading Point接收到各个方向的环境光是相同的
着色频率
Flat Shading:一个三角形只有一个颜色(逐个三角形)
Ground Shading:三角形三个顶点的颜色信息来插值(逐个顶点)
Phong Shading:逐个像素着色
求顶点法线:根据点周围三角形面按面积加权平均
纹理
UV:将三维物体每个点平铺在平面上有U和V表示。
重心坐标:为了三角形内插值使用,=3/1(A,B,C)
透视矩阵
推到参考:The Perspective and Orthographic Projection Matrix
Opengl中三行四列是-1,左右手系导致的
近剪裁面 左下角坐标:(l,b) 右下角坐标:(r,t)
r:right
l:left
t:top
b:bottom