Direct12 如何渲染带纹理的立方体

如何使用Texturecube渲染一个带纹理的立方体?
Texturecube如何使用?
需要的纹理资源是什么样的,6副一样的图片传入同一个资源堆吗?

https://blog.csdn.net/u014038143/article/details/86656568

【以下回答由 GPT 生成】

首先,我们需要做几个准备工作来设置Direct3D 12并加载纹理资源。

  1. 设置Direct3D 12:
  2. 创建一个Win32窗口,并初始化相关变量(如窗口大小、标题等)。
  3. 创建Direct3D 12设备和命令队列。
  4. 创建交换链,并设置后台缓冲区。

  5. 加载纹理资源:

  6. 创建一个纹理资源堆。
  7. 加载纹理图片文件,得到6张纹理图片的像素数据。
  8. 创建纹理资源视图,并将其关联到纹理资源堆。

下面给出具体的代码实现。

#include <d3d12.h>
#include <dxgi1_6.h>
#include <DirectXMath.h>
#include <wincodec.h>
#include <wrl.h>

#pragma comment(lib, "d3d12.lib")
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "windowscodecs.lib")

using namespace Microsoft::WRL;

// ...

// Step 1: 设置Direct3D 12

HWND hWnd; // 窗口句柄
ID3D12Device* pDevice = nullptr; // Direct3D 12设备
ID3D12CommandQueue* pCommandQueue = nullptr; // 命令队列
IDXGISwapChain3* pSwapChain = nullptr; // 交换链

// 创建Win32窗口并初始化
void CreateWin32Window()
{
    // ...
}

// 创建Direct3D 12设备和命令队列
void CreateDeviceAndCommandQueue()
{
    D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&pDevice));

    D3D12_COMMAND_QUEUE_DESC queueDesc = {}; // 命令队列描述符
    queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; // 命令队列类型为直接
    queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; // 无特殊标志

    pDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&pCommandQueue));
}

// 创建交换链和后台缓冲区
void CreateSwapChainAndBackBuffers()
{
    ComPtr<IDXGIFactory4> pFactory;
    CreateDXGIFactory1(IID_PPV_ARGS(&pFactory));

    DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; // 交换链描述符
    swapChainDesc.BufferCount = 2; // 后台缓冲区数量
    swapChainDesc.Width = 800; // 窗口宽度
    swapChainDesc.Height = 600; // 窗口高度
    swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // 后台缓冲区格式
    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // 后台缓冲区用途为渲染目标输出
    swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; // 后台缓冲区交换方式为翻转丢弃
    swapChainDesc.SampleDesc.Count = 1; // 采样数

    DXGI_SWAP_CHAIN_FULLSCREEN_DESC fullScreenDesc = {}; // 全屏描述符
    fullScreenDesc.Windowed = TRUE; // 非全屏

    pFactory->CreateSwapChainForHwnd(pCommandQueue, hWnd, &swapChainDesc, &fullScreenDesc, nullptr, &pSwapChain);
}

// ...

// Step 2: 加载纹理资源

ID3D12Resource* pTexture = nullptr; // 纹理资源
ID3D12DescriptorHeap* pDescriptorHeap = nullptr; // 纹理资源堆

// 加载纹理图片文件并返回像素数据
void LoadTexture(const wchar_t* filePath, std::vector<BYTE>& pixels, UINT& width, UINT& height)
{
    ComPtr<IWICImagingFactory> pFactory;
    CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&pFactory));

    ComPtr<IWICBitmapDecoder> pDecoder;
    pFactory->CreateDecoderFromFilename(filePath, nullptr, GENERIC_READ, WICDecodeMetadataCacheOnLoad, &pDecoder);

    ComPtr<IWICBitmapFrameDecode> pFrame;
    pDecoder->GetFrame(0, &pFrame);

    UINT frameWidth, frameHeight;
    pFrame->GetSize(&frameWidth, &frameHeight);
    width = frameWidth;
    height = frameHeight;

    UINT stride = frameWidth * 4; // 4字节对齐

    std::vector<BYTE> tempPixels(stride * frameHeight);
    pFrame->CopyPixels(nullptr, stride, stride * frameHeight, tempPixels.data());

    // 转换像素数据格式
    UINT pixelSize = frameWidth * frameHeight;
    pixels.resize(pixelSize * 4);
    for (UINT i = 0; i < pixelSize; i++)
    {
        pixels[i * 4] = tempPixels[i * 4 + 2]; // B
        pixels[i * 4 + 1] = tempPixels[i * 4 + 1]; // G
        pixels[i * 4 + 2] = tempPixels[i * 4]; // R
        pixels[i * 4 + 3] = tempPixels[i * 4 + 3]; // A
    }
}

// 创建纹理资源堆
void CreateTextureHeap()
{
    D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {}; // 纹理资源堆描述符
    heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; // 堆类型为常量缓冲视图、着色器资源视图或无序访问视图
    heapDesc.NumDescriptors = 1; // 堆中纹理资源个数
    heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; // 堆在着色器可见

    pDevice->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&pDescriptorHeap));
}

// 创建纹理资源视图并关联到纹理资源堆
void CreateTextureView()
{
    D3D12_SHADER_RESOURCE_VIEW_DESC viewDesc = {}; // 纹理资源视图描述符
    viewDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // 视图格式与后台缓冲区格式一致
    viewDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE; // 纹理资源视图维度为TextureCube
    viewDesc.TextureCube.MipLevels = 1; // 使用的mip等级数量

    pDevice->CreateShaderResourceView(pTexture, &viewDesc, pDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
}

// 加载纹理资源并创建纹理资源视图
void LoadAndCreateTexture()
{
    UINT textureWidth, textureHeight;
    std::vector<BYTE> texturePixels;

    // 加载纹理图片文件
    LoadTexture(L"texture.png", texturePixels, textureWidth, textureHeight);

    // 计算纹理资源大小
    UINT textureSize = texturePixels.size();

    // 创建纹理资源
    D3D12_RESOURCE_DESC textureDesc = {}; // 纹理资源描述符
    textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; // 纹理资源维度为Texture2D
    textureDesc.Alignment = 0; // 默认对齐
    textureDesc.Width = textureWidth; // 纹理宽度
    textureDesc.Height = textureHeight; // 纹理高度
    textureDesc.DepthOrArraySize = 6; // 数组大小为6
    textureDesc.MipLevels = 1; // 使用的mip等级数量
    textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // 纹理格式
    textureDesc.SampleDesc.Count = 1; // 采样数
    textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; // 纹理布局
    textureDesc.Flags = D3D12_RESOURCE_FLAG_NONE; // 无特殊标志

    D3D12_HEAP_PROPERTIES heapProperties = {}; // 堆属性
    heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT; // 默认堆类型

    D3D12_CLEAR_VALUE clearValue = {}; // 清除值
    clearValue.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // 格式与纹理格式一致

    pDevice->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &textureDesc, D3D12_RESOURCE_STATE_COPY_DEST, &clearValue, IID_PPV_ARGS(&pTexture));

    // 创建纹理资源的上传堆
    ComPtr<ID3D12Resource> pUploadHeap;
    heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD; // 上传堆类型
    pDevice->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &textureDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&pUploadHeap));

    // 将纹理像素数据复制到上传堆
    D3D12_SUBRESOURCE_DATA textureData = {}; // 子资源数据
    textureData.pData = texturePixels.data(); // 纹理像素数据首地址
    textureData.RowPitch = textureWidth * 4; // 每行像素所占字节数
    textureData.SlicePitch = textureData.RowPitch * textureHeight; // 每个面的大小

    UpdateSubresources(pCommandList, pTexture, pUploadHeap.Get(), 0, 0, 1, &textureData);

    // 将纹理资源从COPY_DEST状态转换为PIXEL_SHADER_RESOURCE状态
    pCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(pTexture, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE));

    // 创建纹理资源堆
    CreateTextureHeap();

    // 创建纹理资源视图
    CreateTextureView();
}

// ...

// 渲染函数
void Render()
{
    // ...
}

接下来,我们将在渲染函数中实现绘制带纹理的立方体的代码。

// 渲染函数
void Render()
{
    // 设置渲染目标
    auto rtvHandle = pDescriptorHeap->GetCPUDescriptorHandleForHeapStart(); // 解析纹理资源堆句柄
    pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pRenderTarget)); // 获取后台缓冲区
    pDevice->CreateRenderTargetView(pRenderTarget, nullptr, rtvHandle); // 创建渲染目标视图

    // 清除渲染目标
    const float clearColor[] = { 0.0f, 0.0f, 0.0f, 1.0f };
    pCommandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);

    // 设置根签名和管道状态
    pCommandList->SetGraphicsRootSignature(pRootSignature);
    pCommandList->SetPipelineState(pPipelineState);

    // 设置视口和裁剪矩形
    D3D12_VIEWPORT viewport = { 0.0f, 0.0f, 800, 600, 0.0f, 1.0f };
    pCommandList->RSSetViewports(1, &viewport);

    D3D12_RECT scissorRect = { 0, 0, 800, 600 };
    pCommandList->RSSetScissorRects(1, &scissorRect);

    // 设置顶点缓冲区和索引缓冲区
    pCommandList->IASetVertexBuffers(0, 1, &vertexBufferView);
    pCommandList->IASetIndexBuffer(&indexBufferView);

    // 设置纹理资源堆和纹理资源视图
    pCommandList->SetDescriptorHeaps(1, &pDescriptorHeap);
    pCommandList->SetGraphicsRootDescriptorTable(0, pDescriptorHeap->GetGPUDescriptorHandleForHeapStart());

    // 绘制立方体
    pCommandList->DrawIndexedInstanced(36, 1, 0, 0, 0);

    // 切换后台缓冲区的状态为PRESENT
    pCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(pRenderTarget, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));

    // 完成命令列表的执行
    pCommandList->Close();

    // 将命令列表添加到命令队列中执行
    pCommandQueue->ExecuteCommandLists(1, ppCommandList);

    // 交换后台缓冲区和前台缓冲区
    pSwapChain->Present(1, 0);
}

至此,我们通过DirectX 12实现了在C++中渲染带纹理的立方体。在这个过程中,我们使用了TextureCube来存储6张纹理图片,并且只需要传入同一个纹理资源堆即可。


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^