Direct3D 12 尝鲜(四): 旋转的彩色立方体

来源:互联网 发布:nginx hls 配置 编辑:程序博客网 时间:2024/05/16 11:22
(转载请注明出处)

有点时间没更新了,原因是找了一个多星期的bug结果发现是少了一次加法运算。。。。
Orz
不过反正没人看也就没影响啦!_(:3」∠)_


这次的目的是做一个旋转的彩色立方体:
color cube

这是自己在学D3D11的时候做过的一个例子,现在搬到D3D12上, 这次增加的内容有:

  • 常量缓存
  • 深度缓存
  • 顶点/索引缓存

    1. 常量缓存, 常量缓存可以放一些GPU只读的数据, 这里就是存放的是转换矩阵,D3D12中,常量缓存是以256字节对齐的(D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT), 所以请注意.
      这样可以创建一个常量缓存并创建CBV绑定到一个DESCRIPTOR上
    // 创建常量缓存    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();    }

更新常量缓存直接修改即可, 会自动上传的:
cube


代码下载地址: 点击这里


0 1
原创粉丝点击