Direct3D 12 尝鲜(四): 旋转的彩色立方体
来源:互联网 发布:nginx hls 配置 编辑:程序博客网 时间:2024/05/16 11:22
(转载请注明出处)
有点时间没更新了,原因是找了一个多星期的bug结果发现是少了一次加法运算。。。。
不过反正没人看也就没影响啦!_(:3」∠)_
这次的目的是做一个旋转的彩色立方体:
这是自己在学D3D11的时候做过的一个例子,现在搬到D3D12上, 这次增加的内容有:
- 常量缓存
- 深度缓存
顶点/索引缓存
- 常量缓存, 常量缓存可以放一些GPU只读的数据, 这里就是存放的是转换矩阵,D3D12中,常量缓存是以256字节对齐的(D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT), 所以请注意.
这样可以创建一个常量缓存并创建CBV绑定到一个DESCRIPTOR上
- 常量缓存, 常量缓存可以放一些GPU只读的数据, 这里就是存放的是转换矩阵,D3D12中,常量缓存是以256字节对齐的(D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT), 所以请注意.
// 创建常量缓存 if (SUCCEEDED(hr)) { hr = m_pd3dDevice->CreateCommittedResource( &CD3D12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), D3D12_HEAP_MISC_NONE, &CD3D12_RESOURCE_DESC::Buffer(D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT), D3D12_RESOURCE_USAGE_GENERIC_READ, nullptr, IID_ID3D12Resource, reinterpret_cast<void**>(&m_pCBufferMatrix) ); } // 绑定到常量缓存视图 if (SUCCEEDED(hr)) { D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {}; cbvDesc.BufferLocation = m_pCBufferMatrix->GetGPUVirtualAddress(); cbvDesc.SizeInBytes = D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT; m_pd3dDevice->CreateConstantBufferView( &cbvDesc, m_aCpuHandleCSU[CSU_MatrixCBuffer] ); }
这部分代码中, 使用D3D12_HEAP_TYPE_UPLOAD是因为我们每帧都要改写,这样比较方便, 相应的, 效率可能就会损失点.
还有就是之前提到的把所有相同的DESCRIPTOR集中放到一起, 所以写了个简单的框架, 使用m_aCpuHandleXXX数组存放的.
我们在这里存放3个矩阵: 世界、视角、透视转换矩阵
2. 深度缓存, 深度缓存的作用就不用说了, 灵活使用可以获取到不错的效果, 创建DSV绑定到DESCRIPTOR. 这里不使用模板, 仅仅使用深度:
// 创建深度缓存 if (SUCCEEDED(hr)) { D3D12_RESOURCE_DESC resourceDesc = CD3D12_RESOURCE_DESC::Tex2D( DXGI_FORMAT_R32_TYPELESS, m_uBufferWidth, m_uBufferHeight, 1, 1, 1, 0, D3D12_RESOURCE_MISC_ALLOW_DEPTH_STENCIL, D3D12_TEXTURE_LAYOUT_UNKNOWN, 0 ); D3D12_CLEAR_VALUE dsvClearValue; dsvClearValue.Format = DXGI_FORMAT_D32_FLOAT; dsvClearValue.DepthStencil.Depth = 1.0f; dsvClearValue.DepthStencil.Stencil = 0; hr = m_pd3dDevice->CreateCommittedResource( &CD3D12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_MISC_NONE, &resourceDesc, D3D12_RESOURCE_USAGE_DEPTH, &dsvClearValue, IID_ID3D12Resource, reinterpret_cast<void**>(&m_pDepthBuffer) ); } // 绑定深度缓存到DSV if (SUCCEEDED(hr)) { D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = {}; dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; dsvDesc.Format = DXGI_FORMAT_D32_FLOAT; dsvDesc.Texture2D.MipSlice = 0; dsvDesc.Flags = D3D12_DSV_NONE; /*no return*/m_pd3dDevice->CreateDepthStencilView( m_pDepthBuffer, &dsvDesc, m_aCpuHandleDSV[DSV_MainDSV] ); }
感觉没什么可以说的, D3D12提供的Helper很方便, 默认值就行,详细的可以查看官方文档.
3. 顶点/索引缓存, 渲染一个立方体所需要的东西, 目前我们需要: 顶点颜色与顶点坐标, 索引就是三角索引了
// 创建带颜色的正方体auto SceneRenderer::CreateColoredCube( ID3D12Resource*& vibuffer, D3D12_VERTEX_BUFFER_VIEW& vbuffer_view, D3D12_INDEX_BUFFER_VIEW& ibuffer_view) noexcept -> HRESULT { HRESULT hr = S_OK; ID3D12Resource* pVIBuffer = nullptr; // 立方体的8个顶点 与相应颜色 VertexColor vertices[] = { { DirectX::XMFLOAT3(-1.f, -1.f, -1.f), DirectX::XMFLOAT4(0.f, 0.f, 0.f, 1.f) }, { DirectX::XMFLOAT3(-1.f, 1.f, -1.f), DirectX::XMFLOAT4(1.f, 0.f, 0.f, 1.f) }, { DirectX::XMFLOAT3(1.f, 1.f, -1.f), DirectX::XMFLOAT4(0.f, 1.f, 0.f, 1.f) }, { DirectX::XMFLOAT3(1.f, -1.f, -1.f), DirectX::XMFLOAT4(0.f, 0.f, 1.f, 1.f) }, { DirectX::XMFLOAT3(-1.f, -1.f, 1.f), DirectX::XMFLOAT4(0.f, 1.f, 1.f, 1.f) }, { DirectX::XMFLOAT3(-1.f, 1.f, 1.f), DirectX::XMFLOAT4(1.f, 1.f, 0.f, 1.f) }, { DirectX::XMFLOAT3(1.f, 1.f, 1.f), DirectX::XMFLOAT4(1.f, 0.f, 1.f, 1.f) }, { DirectX::XMFLOAT3(1.f, -1.f, 1.f), DirectX::XMFLOAT4(1.f, 1.f, 1.f, 1.f) } }; // 立方体 6个面 12个三角面 36个顶点 uint16_t indices[] = { 0, 1, 2, 0, 2, 3, 4, 5, 1, 4, 1, 0, 7, 6, 5, 7, 5, 4, 3, 2, 6, 3, 6, 7, 1, 5, 6, 1, 6, 2, 4, 0, 3, 4, 3, 7 }; // 创建顶点缓存-索引缓存共用缓冲区 if (SUCCEEDED(hr)) { hr = m_pd3dDevice->CreateCommittedResource( &CD3D12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), D3D12_HEAP_MISC_NONE, &CD3D12_RESOURCE_DESC::Buffer(sizeof(vertices)+sizeof(indices)), D3D12_RESOURCE_USAGE_GENERIC_READ, nullptr, IID_ID3D12Resource, reinterpret_cast<void**>(&pVIBuffer) ); } // 映射 void* buffer = nullptr; if (SUCCEEDED(hr)) { hr = pVIBuffer->Map(0, nullptr, &buffer); } // 复制-取消映射-设置 if (SUCCEEDED(hr)) { ::memcpy(buffer, vertices, sizeof(vertices)); ::memcpy( reinterpret_cast<uint8_t*>(buffer)+ sizeof(vertices), indices, sizeof(indices) ); pVIBuffer->Unmap(0, nullptr); // 设置 vbuffer_view.BufferLocation = pVIBuffer->GetGPUVirtualAddress(); vbuffer_view.StrideInBytes = sizeof(VertexColor); vbuffer_view.SizeInBytes = sizeof(vertices); ibuffer_view.BufferLocation = vbuffer_view.BufferLocation + sizeof(vertices); ibuffer_view.Format = DXGI_FORMAT_R16_UINT; ibuffer_view.SizeInBytes = sizeof(indices); vibuffer = ::SafeAcquire(pVIBuffer); } ::SafeRelease(pVIBuffer); return hr;}
得益于D3D12/Win10的虚拟GPU地址, 我们可以将顶点缓存缓存与索引缓存一起申请, 提高效率. 对应的输入布局:
// 输入布局 D3D12_INPUT_ELEMENT_DESC inputLayout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, sizeof(DirectX::XMFLOAT3), D3D12_INPUT_PER_VERTEX_DATA, 0 }, };
其实颜色也是可以用3个浮点数就行, 这样每个节点就能节约1个浮点了.对应的shader就是:
// C Buffer 0 : 储存转换矩阵cbuffer MatrixBuffer : register (b0) { matrix worldMatrix; matrix viewMatrix; matrix projectionMatrix;};// VS 输入struct VertexInputType { float4 position : POSITION; float4 color : COLOR;};// VS 输出struct PixelInputType { float4 position : SV_POSITION; float4 color : COLOR;};// 处理PixelInputType ColorVertexShader(VertexInputType input) { PixelInputType output; // 坐标转换 output.position = mul(float4(input.position.xyz, 1), worldMatrix); output.position = mul(output.position, viewMatrix); output.position = mul(output.position, projectionMatrix); // 直接输出 output.color = input.color; return output;}// 像素着色器处理float4 ColorPixelShader(PixelInputType input) : SV_TARGET { return input.color;}
请注意, 因为都算是“固定管线”,所以我们在着色器里面使用的一切都要说明:
这里使用了在b0寄存器上cbuffer.
毕竟输入小,管线占用资源就少, 序列化RootSignature时提供的参数D3D12_ROOT_SIGNATURE表明了这个:
D3D12_ROOT_SIGNATURE rootSigDesc = D3D12_ROOT_SIGNATURE(); D3D12_ROOT_PARAMETER params[1]; D3D12_DESCRIPTOR_RANGE descRange[1]; descRange[0].Init(D3D12_DESCRIPTOR_RANGE_CBV, 1, 0); params[0].InitAsDescriptorTable(lengthof(descRange), descRange); // 初始化 rootSigDesc.NumParameters = lengthof(params); rootSigDesc.pParameters = params; rootSigDesc.Flags = D3D12_ROOT_SIGNATURE_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
目前仅需一个DESCRIPTOR表即可,并且只有一个常量缓存视图(CBV), 所以才是descRange[0].Init(D3D12_DESCRIPTOR_RANGE_CBV, 1, 0);
这次清空命令多了个清空深度缓存:
} // 执行清空命令 if (SUCCEEDED(hr)) { this->SetResourceBarrier(m_pCmdClear, m_pTargetBuffer, D3D12_RESOURCE_USAGE_PRESENT, D3D12_RESOURCE_USAGE_RENDER_TARGET); m_pCmdClear->RSSetViewports(1, &view); float clearColor[4] = { 0.4f, 0.8f, 1.0f, 1.0f}; m_pCmdClear->ClearRenderTargetView( m_aCpuHandleRTV[RTV_MainRTV], clearColor, nullptr, 0 ); m_pCmdClear->ClearDepthStencilView( m_aCpuHandleDSV[DSV_MainDSV], D3D12_CLEAR_DEPTH, 1.0f, 0, nullptr, 0 ); this->SetResourceBarrier(m_pCmdClear, m_pTargetBuffer, D3D12_RESOURCE_USAGE_RENDER_TARGET, D3D12_RESOURCE_USAGE_PRESENT); hr = m_pCmdClear->Close(); }
刻画命令多了: 设置DESCRIPTOR表(对应管线), 与DESCRIPTOR堆(提供资源),以及设置顶点缓存和索引缓存:
// 执行命令 if (SUCCEEDED(hr)) { ID3D12DescriptorHeap* heaps[] = { m_pCSUDescriptors }; D3D12_RECT scissor = {}; scissor.right = m_uBufferWidth; scissor.bottom = m_uBufferHeight; // this->SetResourceBarrier(m_pCmdDraw, m_pTargetBuffer, D3D12_RESOURCE_USAGE_PRESENT, D3D12_RESOURCE_USAGE_RENDER_TARGET); m_pCmdDraw->RSSetViewports(1, &view); m_pCmdDraw->RSSetScissorRects(1, &scissor); m_pCmdDraw->SetRenderTargets(m_aCpuHandleRTV + RTV_MainRTV, true, 1, m_aCpuHandleDSV + DSV_MainDSV); m_pCmdDraw->SetGraphicsRootSignature(m_prsPipeline); m_pCmdDraw->SetDescriptorHeaps(heaps, lengthof(heaps)); m_pCmdDraw->SetGraphicsRootDescriptorTable(0, m_pCSUDescriptors->GetGPUDescriptorHandleForHeapStart()); m_pCmdDraw->SetPipelineState(m_pPipelineState); m_pCmdDraw->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); m_pCmdDraw->SetVertexBuffers(0, &m_cubeVBV, 1); m_pCmdDraw->SetIndexBuffer(&m_cubeIBV); m_pCmdDraw->DrawIndexedInstanced(36, 1, 0, 0, 0); this->SetResourceBarrier(m_pCmdDraw, m_pTargetBuffer, D3D12_RESOURCE_USAGE_RENDER_TARGET, D3D12_RESOURCE_USAGE_PRESENT); hr = m_pCmdDraw->Close(); }
更新常量缓存直接修改即可, 会自动上传的:
代码下载地址: 点击这里
- Direct3D 12 尝鲜(四): 旋转的彩色立方体
- Direct3D 彩色立方体 就是练习
- Direct3D 12 尝鲜: 基本呈现
- Direct3D 12 尝鲜(二): Fence
- 用webgl绘制一个彩色旋转立方体
- 【D3D11游戏编程】学习笔记八:绘图基础-旋转的彩色立方体
- 逐梦旅程学习笔记 DirectX开发入门02:旋转的彩色立方体
- 逐梦旅程学习笔记 DirectX开发入门03:旋转的彩色立方体进阶版
- Direct3D 12 尝鲜(三): Pipeline State Object
- Direct3D 12 尝鲜(五): SDK 更新
- 旋转的立方体
- 旋转的四个立方体
- (内部)小Y的彩色立方体
- OpenGL4-绘制旋转的立方体
- OpenGL旋转立方体的实现
- Opengl ES 1.x NDK实例开发之四:立方体的旋转
- Opengl ES 1.x NDK实例开发之四:立方体的旋转
- D3D绘制彩色立方体
- EM算法
- LabVIEW数据记录和存储[控件定值保存]
- Android中asm.jar工具的使用
- 智能手机进入开发者模式 小米2A 操作系统Android 4.4 开发者模式
- mysql索引技术
- Direct3D 12 尝鲜(四): 旋转的彩色立方体
- ARM指令后缀与常用读取指令
- Mac技巧之显示/隐藏苹果MacOSX系统下隐藏文件的终端命令
- 检测单向链表是否存在环
- (DT系列五)Linux kernel 是怎么将 devicetree中的内容生成plateform_device
- Fiddler 教程
- Java装箱、拆箱
- 人性
- android,编译源代码经常发生的几个异常