TheRedDog 2025-07-19 20:09 采纳率: 0%
浏览 6

模型加载失败,求调!

使用D3D12加载FBX模型时,总是获得一个空的窗口,在PIX分析大致确定问题出在光栅化阶段(顶点着色器输出正常,像素着色器只是返回CB中的颜色,应该没问题),PSO创建与核心渲染代码如下:

vertexShader = D3DTools::compileShaderFromFile(L"shaders.hlsl", "VS", "vs_5_0");
pixelShader = D3DTools::compileShaderFromFile(L"shaders.hlsl", "PS", "ps_5_0");
D3D12_GRAPHICS_PIPELINE_STATE_DESC desc = { 0 };
desc.pRootSignature = rootSignature.Get();
desc.VS = {
    reinterpret_cast<BYTE*>(vertexShader->GetBufferPointer()),
    vertexShader->GetBufferSize()
};
desc.PS = {
   reinterpret_cast<BYTE*>(pixelShader->GetBufferPointer()),
   pixelShader->GetBufferSize()
};
std::vector<D3D12_INPUT_ELEMENT_DESC> inputLayout = {
    {"POSITION",0,DXGI_FORMAT_R32G32B32_FLOAT,0,0,
    D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,0},
};
desc.InputLayout = { inputLayout.data(), static_cast<UINT>(inputLayout.size()) };
desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
desc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
desc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
desc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
desc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
desc.DepthStencilState.DepthEnable = FALSE;
desc.DSVFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
desc.SampleMask = UINT_MAX;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.NumRenderTargets = 1;
desc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;

HRESULT hr = device->CreateGraphicsPipelineState(&desc, IID_PPV_ARGS(&PSO));
if (FAILED(hr))
{
    MessageBox(0, L"ERROR: Cannot Create Pipeline State!", L"DXLP", MB_ICONERROR);
    exit(-1);
}
commandList->SetPipelineState(PSO.Get());

void MainGameWindow::Update()
{
    // Update the game state
    // Update the camera position and target
    D3DTools::ConstantPerPass cp;
    XMMATRIX view, proj;
    view = XMMatrixLookAtLH(XMLoadFloat3(&cameraPos), XMLoadFloat3(&cameraTarget), XMLoadFloat3(&cameraUp));
    proj = XMMatrixPerspectiveFovLH(XMConvertToRadians(45.0f), aspectRatio, 1.0f, 1000.0f);
    XMMATRIX viewProj = XMMatrixMultiply(proj, view);
    XMStoreFloat4x4(&cp.viewPorj, viewProj);
    /*std::cout << "view: " << view.r[0].m128_f32[0] << " ..." << std::endl;
    std::cout << "proj: " << proj.r[0].m128_f32[0] << " ..." << std::endl;*/
    frameResources[CurrFrameResourceIndex]->PassCB->CopyData(0, cp);
    // Update the constant buffer for each render item
    for (int i = 0; i < renderItems.size(); i++)
    {
        if (renderItems[i]->NumFramesDirty) {
            D3DTools::ConstantPerObject co;
            XMStoreFloat4(&co.color, XMVectorSet(1.0, 1.0, 1.0, 1));
            co.world = XMMatrixIdentity();
            frameResources[CurrFrameResourceIndex]->ObjectCB->CopyData(i, co);
            renderItems[i]->NumFramesDirty--;
        }
    }
}

void MainGameWindow::Render()
{
    //使用Direct3D 12渲染游戏内容
    CurrFrameResourceIndex = (CurrFrameResourceIndex + 1) % numFrameResources;
    if (frameResources[CurrFrameResourceIndex]->Fence != 0
        && fence->GetCompletedValue() < currFR->Fence)
    {
        HANDLE eventHandle = CreateEventEx(nullptr, nullptr, 0, EVENT_ALL_ACCESS);
        fence->SetEventOnCompletion(currFR->Fence, eventHandle);
        WaitForSingleObject(eventHandle, INFINITE);
        CloseHandle(eventHandle);
    }
    auto commandAllocator = frameResources[CurrFrameResourceIndex]->commandAllocator;
    commandAllocator->Reset();
    commandList->Reset(commandAllocator.Get(), PSO.Get());
    //for (int i = 0; i < renderItems.size(); i++)
    //{
    //    if (renderItems[i]->NumFramesDirty) {
    //        D3DTools::ConstantPerObject co;
    //        XMStoreFloat4(&co.color, XMVectorSet(0, 1, 0, 0));
    //        co.world = XMMatrixIdentity();
    //        frameResources[CurrFrameResourceIndex]->ObjectCB->CopyData(i, co);
    //        renderItems[i]->NumFramesDirty--;
    //    }
    //}
    Update();
    w = h = 800;
    CreateViewportAndScissorRect();
    commandList->RSSetViewports(1, &vp);
    commandList->RSSetScissorRects(1, &sciRect);
    target = CD3DX12_RESOURCE_BARRIER::Transition(
        CurrentBackBuffer(),
        D3D12_RESOURCE_STATE_PRESENT,
        D3D12_RESOURCE_STATE_RENDER_TARGET);
    commandList->ResourceBarrier(1, &target);
    FLOAT clear[] = { 0.0,0.0,0.0,1 };
    commandList->ClearRenderTargetView(
        CurrentBackBufferView(),
        clear,
        0,
        nullptr);
    commandList->ClearDepthStencilView(
        DSVHeap->GetCPUDescriptorHandleForHeapStart(),
        D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL,
        1.0f, 0, 0, nullptr);
    D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = CurrentBackBufferView();
    D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = DSVHeap->GetCPUDescriptorHandleForHeapStart();
    commandList->OMSetRenderTargets(1, &rtvHandle,
        true, &dsvHandle);
    ID3D12DescriptorHeap* heaps[] = { CBVHeap.Get() };
    commandList->SetDescriptorHeaps(_countof(heaps), heaps);
    commandList->SetGraphicsRootSignature(rootSignature.Get());
    //commandList->SetGraphicsRootDescriptorTable(0,
    //    CBVHeap->GetGPUDescriptorHandleForHeapStart());

    //D3D12_VERTEX_BUFFER_VIEW vbv = geo->GetVertexBufferView();
    //D3D12_INDEX_BUFFER_VIEW ibv = geo->GetIndexBufferView();
    //commandList->IASetVertexBuffers(0, 1, &vbv);
    //commandList->IASetIndexBuffer(&ibv);
    //commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    //commandList->DrawIndexedInstanced(3, 1, 0, 0, 0);
    auto pcbHandle = CD3DX12_GPU_DESCRIPTOR_HANDLE(
        CBVHeap->GetGPUDescriptorHandleForHeapStart());
    UINT passCBHeapIndex = numFrameResources * renderItems.size() + CurrFrameResourceIndex;
    pcbHandle.Offset(passCBHeapIndex, cbvUavSize);
    commandList->SetGraphicsRootDescriptorTable(1,
        pcbHandle);
    DrawRenderItems();
    present = CD3DX12_RESOURCE_BARRIER::Transition(
        CurrentBackBuffer(),
        D3D12_RESOURCE_STATE_RENDER_TARGET,
        D3D12_RESOURCE_STATE_PRESENT);
    commandList->ResourceBarrier(1, &present);
    commandList->Close();
    ID3D12CommandList* cmdLists[] = { commandList.Get() };
    commandQueue->ExecuteCommandLists(_countof(cmdLists), cmdLists);
    swapChain->Present(0, 0);
    CurrBackBuffer = (CurrBackBuffer + 1) % SwapChainBufferCount;
    // FlushCommandQueue();
    currFR->Fence = ++currFence;
    commandQueue->Signal(fence.Get(), currFR->Fence);
}
void MainGameWindow::DrawRenderItems()
{
    UINT objCBVSize = D3DTools::calcConstantBufferSize(sizeof(D3DTools::ConstantPerObject));
    for(int i = 0; i < renderItems.size(); i++)
    {
        RenderItem* item = renderItems[i].get();
        UINT cbvIndex = item->objCBIndex + renderItems.size() * CurrFrameResourceIndex;
        auto objCBVHandle = CD3DX12_GPU_DESCRIPTOR_HANDLE(
            CBVHeap->GetGPUDescriptorHandleForHeapStart());
        objCBVHandle.Offset(cbvIndex, cbvUavSize);
        commandList->SetGraphicsRootDescriptorTable(0, objCBVHandle);
        D3D12_VERTEX_BUFFER_VIEW vbv = item->geometry->GetVertexBufferView();
        D3D12_INDEX_BUFFER_VIEW ibv = item->geometry->GetIndexBufferView();
        commandList->IASetVertexBuffers(0, 1, &vbv);
        commandList->IASetIndexBuffer(&ibv);
        commandList->IASetPrimitiveTopology(item->primitiveType);
        commandList->DrawIndexedInstanced(item->indexCount, 1, item->startIndexLocation, item->baseVertexLocation, 0);
    }
}



PIX几处关键图像:

img

img

img


然后获得一个全黑的窗口
希望好心人们帮忙解决一下!

  • 写回答

6条回答 默认 最新

  • 檀越@新空间 2025-07-19 20:10
    关注

    晚上好🌙🌙🌙
    本答案参考通义千问

    在使用 D3D12 加载 FBX 模型 时,如果出现空窗口,即使 PIX 中渲染参数正确,这通常意味着模型加载失败或渲染管线配置不正确。以下是一些可能的原因及解决方案。


    一、问题分析

    1. 模型加载失败

    • FBX 文件未被正确解析。
    • 使用的 FBX SDK 版本与 D3D12 不兼容。
    • 模型数据未正确转换为 D3D12 可用的顶点/索引格式(如 Vertex 结构体)。

    2. 渲染管线配置错误

    • 顶点着色器/像素着色器未正确绑定
    • 输入布局未定义(例如:未声明顶点结构中的位置、法线等字段)。
    • 渲染目标视图(RTV)未正确设置,导致画面未输出到窗口。

    3. 帧资源管理异常

    • 帧资源未正确初始化,导致常量缓冲区(CBV)未被更新。
    • 同步机制(Fence、Command Queue)未正确处理,导致命令列表未执行。

    二、解决方案

    1. 确保 FBX 模型正确加载

    步骤 1:检查 FBXSDK 初始化

    确保你已正确初始化了 FBX SDK,并且加载了模型文件:

    #include <fbxsdk.h>
    using namespace fbxsdk;
    
    // 初始化 FBX SDK
    FbxManager* manager = FbxManager::Create();
    FbxScene* scene = FbxScene::Create(manager, "MyScene");
    FbxImporter* importer = FbxImporter::Create(manager, "");
    bool success = importer->Initialize("model.fbx", -1, manager->GetIOSettings());
    importer->Import(scene);
    importer->Destroy();
    

    注意: 如果模型加载失败,请检查文件路径和 SDK 版本是否兼容。

    步骤 2:将 FBX 数据转换为顶点/索引数据

    你需要从 FBX 场景中提取网格信息并构建顶点缓冲区和索引缓冲区。例如:

    void ExtractMeshData(FbxNode* node, std::vector<Vertex>& vertices, std::vector<unsigned int>& indices) {
        if (node->GetNodeAttribute() && node->GetNodeAttribute()->GetAttributeType() == FbxNodeAttribute::eMesh) {
            FbxMesh* mesh = (FbxMesh*)node->GetNodeAttribute();
            for (int i = 0; i < mesh->GetPolygonCount(); ++i) {
                for (int j = 0; j < 3; ++j) {
                    int index = mesh->GetPolygonVertex(i, j);
                    FbxVector4 pos = mesh->GetControlPointAt(index);
                    Vertex v;
                    v.position = XMFLOAT3(pos[0], pos[1], pos[2]);
                    // 假设没有法线、纹理坐标,可扩展
                    vertices.push_back(v);
                }
            }
        }
    
        for (int i = 0; i < mesh->GetPolygonCount(); ++i) {
            for (int j = 0; j < 3; ++j) {
                indices.push_back(mesh->GetPolygonVertex(i, j));
            }
        }
    }
    

    重要提示: 确保你已正确处理法线、UV、材质等信息。


    2. 确保渲染管线配置正确

    步骤 1:定义顶点结构体

    struct Vertex {
        DirectX::XMFLOAT3 position;
        DirectX::XMFLOAT3 normal;
        DirectX::XMFLOAT2 uv;
    };
    

    步骤 2:创建输入布局描述

    D3D12_INPUT_LAYOUT_DESC inputLayoutDesc = {};
    inputLayoutDesc.NumElements = 3;
    inputLayoutDesc.pInputElementDescs = new D3D12_INPUT_ELEMENT_DESC[3]{
        {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}
    };
    

    注意: 确保与顶点结构体匹配。

    步骤 3:创建顶点缓冲区和索引缓冲区

    D3D12_BUFFER_DESC vertexBufferDesc = {};
    vertexBufferDesc.Usage = D3D12_RESOURCE_USAGE_IMMUTABLE;
    vertexBufferDesc.ByteWidth = sizeof(Vertex) * vertices.size();
    vertexBufferDesc.CPUAccessFlags = D3D12_CPU_ACCESS_WRITE;
    vertexBufferDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
    
    ComPtr<ID3D12Resource> vertexBuffer;
    device->CreateCommittedResource(
        &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
        D3D12_HEAP_FLAG_NONE,
        &vertexBufferDesc,
        D3D12_RESOURCE_STATE_COPY_DEST,
        nullptr,
        IID_PPV_ARGS(&vertexBuffer)
    );
    
    // 将顶点数据上传到 GPU
    void* vertexData = nullptr;
    vertexBuffer->Map(0, nullptr, &vertexData);
    memcpy(vertexData, vertices.data(), sizeof(Vertex) * vertices.size());
    vertexBuffer->Unmap(0, nullptr);
    

    3. 确保帧资源管理正确

    步骤 1:初始化帧资源

    frameResources.resize(numFrameResources);
    for (size_t i = 0; i < numFrameResources; ++i) {
        frameResources[i].reset(new FrameResource(device, numObjectsPerFrame, numPasses));
    }
    

    步骤 2:确保常量缓冲区正确更新

    D3DTools::ConstantPerPass cp;
    XMMATRIX view = XMMatrixLookAtLH(...);
    XMMATRIX proj = XMMatrixPerspectiveFovLH(...);
    XMMATRIX viewProj = XMMatrixMultiply(proj, view);
    XMStoreFloat4x4(&cp.viewPorj, viewProj);
    frameResources[CurrFrameResourceIndex]->PassCB->CopyData(0, cp);
    

    注意: 确保 PassCB 是一个 ConstantBuffer 并且已正确绑定到着色器。


    4. 检查渲染目标视图(RTV)

    // 创建 RTV 描述符
    CD3DX12_DESCRIPTOR_HEAP_DESC rtvHeapDesc(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, numFrameResources);
    device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&rtvHeap));
    
    // 设置 RTV
    CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(rtvHeap->GetCPUDescriptorHandleForHeapStart());
    commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr);
    

    注意: 确保 RTV 已正确绑定,并且窗口大小匹配。


    三、代码修改建议

    修改 MainGameWindow::Render() 方法

    void MainGameWindow::Render()
    {
        CurrFrameResourceIndex = (CurrFrameResourceIndex + 1) % numFrameResources;
        if (frameResources[CurrFrameResourceIndex]->Fence != 0
            && fence->GetCompletedValue() < currFR->Fence)
        {
            HANDLE eventHandle = CreateEventEx(nullptr, nullptr, 0, EVENT_ALL_ACCESS);
            fence->SetEventOnCompletion(currFR->Fence, eventHandle);
            WaitForSingleObject(eventHandle, INFINITE);
            CloseHandle(eventHandle);
        }
    
        auto commandAllocator = frameResources[CurrFrameResourceIndex]->commandAllocator;
        commandAllocator->Reset();
        commandList->Reset(commandAllocator.Get(), PSO.Get());
    
        // 设置渲染目标
        CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(rtvHeap->GetCPUDescriptorHandleForHeapStart());
        commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr);
    
        // 设置视口和裁剪矩形
        CreateViewportAndScissorRect();
    
        // 绑定常量缓冲区
        commandList->SetGraphicsRootSignature(rootSignature.Get());
        commandList->SetPipelineState(PSO.Get());
        commandList->SetDescriptorHeaps(1, cbvHeap.GetAddressOf());
    
        // 绑定顶点缓冲区
        commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        commandList->IASetVertexBuffers(0, 1, &vertexBufferView);
        commandList->IASetIndexBuffer(&indexBufferView);
    
        // 绘制模型
        commandList->DrawIndexedInstanced(indices.size(), 1, 0, 0, 0);
    
        commandList->Close();
        commandQueue->ExecuteCommandLists(1, commandList.GetAddressOf());
    }
    

    四、调试建议

    1. 使用 PIX 进行调试

      • 确认模型数据是否被正确加载。
      • 检查渲染管线状态对象(PSO)是否正确。
    2. 检查 Direct3D 错误

      HRESULT hr = device->CreateCommittedResource(...);
      if (FAILED(hr)) {
          OutputDebugString("Failed to create resource!\n");
      }
      
    3. 打印日志

      std::cout << "Loaded " << vertices.size() << " vertices." << std::endl;
      std::cout << "Loaded " << indices.size() << " indices." << std::endl;
      

    五、总结

    | 问题 | 解决方案 | |------|----------| | 模型加载失败 | 检查 FBX SDK 初始化和模型数据提取逻辑 | | 渲染管线配置错误 | 确保输入布局、顶点缓冲区、索引缓冲区正确 | | 帧资源管理异常 | 检查常量缓冲区更新和同步机制 | | 空窗口 | 确保 RTV 正确绑定,视口和裁剪矩形设置正确 |


    如果你能提供完整的 FBXMeshLoader.cppMainGameWindow.cpp 的完整代码,我可以进一步帮你定位问题。

    评论

报告相同问题?

问题事件

  • 修改了问题 7月19日
  • 创建了问题 7月19日