程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C >> 關於C >> 使用純C語言開發簡單的Direct3D 12應用

使用純C語言開發簡單的Direct3D 12應用

編輯:關於C

由於微軟官方給出的D3D12的demo都經過C++層層封裝,即便是很簡單的畫三角形的程序都顯得比較復雜。因此筆者這裡就用純C語言來改寫畫三角形的簡單D3D12應用程序。這裡面不包含任何已被廢棄的D3DX的庫,所以可以直接拿來使用。

各位要做的准備工作是,先要有一部裝有Windows 10的PC。然後,在上面安裝Visual Studio 2015開發環境,筆者這裡用的是微軟免費的Visual Studio 2015 Express Edition for Desktop,可支持部分C99語法特性,這會使得C語言代碼更為精簡~

安裝完成之後,各位能夠在安裝目錄中看到,除了Visual Studio 2015之外,還出現了Windows Kit,這裡面就已經包含了Direct3D所需要的所有頭文件以及庫文件。

我們首先創建一個名為Direct3D12Test的工程,然後選擇Win32 Application,不需要勾選SDL選項。然後,在項目選項中C/C++一欄下的General中,Additional Included Directories一欄輸入D3D12所需要的頭文件路徑,筆者系統下是:C:\Program Files (x86)\Windows Kits\10\Include\10.0.14393.0\um;C:\Program Files (x86)\Windows Kits\10\Include\10.0.14393.0\shared

分號左邊的路徑是d3d12.h所在的目錄;分號右邊是dxgi相關的頭文件所在的目錄。

隨後在Linker下的Additional Library Directories中輸入我們所要連接的d3d12庫文件所在的路徑,筆者系統下是:C:\Program Files (x86)\Windows Kits\10\Lib\10.0.14393.0\um\x64

然後在Input一欄的Additional Dependencies中添加d3d12.lib;dxguid.lib;dxgi.lib;d3dcompiler.lib;

以上這些就是我們需要連接的靜態庫。然後,我們將Debug旁邊的x86改為x64,也就是說,我們這裡將構建64位應用程序。

接下來,我們把IDE自動生成的Direct3D12Test.cpp源文件先暫時移除,然後將文件名後綴改為.c,然後再添加到工程中去。

最後,我們在項目工程選項中,找到C/C++一欄下的Precompiled Header,我們將這裡面的Precompiled Header選項改為Not Using Precompiled Headers。如果開啟這個選項,由於在項目之前自動構建的時候用的是C++,我們再使用C源文件就會報錯。完成之後,我們就可以用以下代碼來替換掉原來該源文件中的內容了:

// Direct3D12Test.c : Defines the entry point for the application.
// 這是一個C語言源文件
#include "stdafx.h"
#include "Direct3D12Test.h"
#include 
#include 
#include  

#include 
#include 

#define MAX_LOADSTRING 100

// Global Variables:
static HINSTANCE hInst;                                // current instance
static WCHAR szTitle[MAX_LOADSTRING];                  // The title bar text
static WCHAR szWindowClass[MAX_LOADSTRING];            // the main window class name

// Forward declarations of functions included in this code module:
static ATOM                MyRegisterClass(HINSTANCE hInstance);
static HWND                InitInstance(HINSTANCE, int);
static LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
static INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

static const int s_windowWidth = 640;
static const int s_windowHeight = 480;

#define FRAME_COUNT     2

// Pipeline objects.
static D3D12_VIEWPORT s_viewport;
static D3D12_RECT s_scissorRect;
static IDXGISwapChain3 *s_swapChain;
static ID3D12Device *s_device;
static ID3D12Resource *s_renderTargets[FRAME_COUNT];
static ID3D12CommandAllocator *s_commandAllocator;
static ID3D12CommandQueue *s_commandQueue;
static ID3D12RootSignature *s_rootSignature;
static ID3D12DescriptorHeap *s_rtvHeap;
static ID3D12PipelineState *s_pipelineState;
static ID3D12GraphicsCommandList *s_commandList;
static uint32_t s_rtvDescriptorSize;

// App resources.
static ID3D12Resource *s_vertexBuffer;
static D3D12_VERTEX_BUFFER_VIEW s_vertexBufferView;

// Synchronization objects.
static uint32_t s_frameIndex;
static HANDLE s_fenceEvent;
static ID3D12Fence *s_fence;
static uint64_t s_fenceValue;

// 自己定制是否使用warp device
static const bool s_useWarpDevice = true;

/**
 加載渲染流水線
*/
static bool LoadPipeline(HWND hWnd)
{
    // 我們這裡采用調試模式
    ID3D12Debug *debugController;
    if (SUCCEEDED(D3D12GetDebugInterface(&IID_IDebug, &debugController)))
        debugController->lpVtbl->EnableDebugLayer(debugController);

    IDXGIFactory4 *factory;
    if (CreateDXGIFactory1(&IID_IDXGIFactory4, &factory) < 0)
        return false;

    if (s_useWarpDevice)
    {
        IDXGIAdapter *warpAdapter;
        if (factory->lpVtbl->EnumWarpAdapter(factory, &IID_IDXGIAdapter, &warpAdapter) < 0)
            return false;

        if (D3D12CreateDevice((IUnknown*)warpAdapter, D3D_FEATURE_LEVEL_11_0, &IID_ID3D12Device, &s_device) < 0)
            return false;
    }
    else
    {
        IDXGIAdapter1 *hardwareAdapter = NULL;
        for (int i = 0; i < 3; i++)
        {
            factory->lpVtbl->EnumAdapters1(factory, i, &hardwareAdapter);

            if (D3D12CreateDevice((IUnknown*)hardwareAdapter, D3D_FEATURE_LEVEL_11_0, &IID_ID3D12Device, &s_device) >= 0)
                break;

            hardwareAdapter->lpVtbl->Release(hardwareAdapter);
        }
        if (hardwareAdapter == NULL)
            return false;
    }

    // Describe and create the command queue.
    D3D12_COMMAND_QUEUE_DESC queueDesc = { 0 };
    queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
    queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
    if (s_device->lpVtbl->CreateCommandQueue(s_device, &queueDesc, &IID_ID3D12CommandQueue, &s_commandQueue) < 0)
        return false;

    // Describe and create the swap chain.
    DXGI_SWAP_CHAIN_DESC swapChainDesc = { 0 };
    swapChainDesc.BufferCount = FRAME_COUNT;
    swapChainDesc.BufferDesc.Width = s_windowWidth;
    swapChainDesc.BufferDesc.Height = s_windowHeight;
    swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
    swapChainDesc.OutputWindow = hWnd;
    swapChainDesc.SampleDesc.Count = 1;
    swapChainDesc.Windowed = TRUE;

    IDXGISwapChain *swapChain;
    if (factory->lpVtbl->CreateSwapChain(factory, (IUnknown*)s_commandQueue, &swapChainDesc, &swapChain) < 0)
        return false;

    s_swapChain = (IDXGISwapChain3*)swapChain;

    // This sample does not support fullscreen transitions.
    if (factory->lpVtbl->MakeWindowAssociation(factory, hWnd, DXGI_MWA_NO_ALT_ENTER) < 0)
        return false;

    s_frameIndex = s_swapChain->lpVtbl->GetCurrentBackBufferIndex(s_swapChain);

    // Create descriptor heaps
    // Describe and create a render target view (RTV) descriptor heap.
    D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = { 0 };
    rtvHeapDesc.NumDescriptors = FRAME_COUNT;
    rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
    rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
    if (s_device->lpVtbl->CreateDescriptorHeap(s_device, &rtvHeapDesc, &IID_ID3D12DescriptorHeap, &s_rtvHeap) < 0)
        return false;

    s_rtvDescriptorSize = s_device->lpVtbl->GetDescriptorHandleIncrementSize(s_device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV);

    // Create frame resources
    D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle;
    s_rtvHeap->lpVtbl->GetCPUDescriptorHandleForHeapStart(s_rtvHeap, &rtvHandle);

    // Create a RTV for each frame.
    for (int n = 0; n < FRAME_COUNT; n++)
    {
        if (s_swapChain->lpVtbl->GetBuffer(s_swapChain, n, &IID_ID3D12Resource, &s_renderTargets[n]) < 0)
            return false;

        s_device->lpVtbl->CreateRenderTargetView(s_device, s_renderTargets[n], NULL, rtvHandle);
        rtvHandle.ptr += s_rtvDescriptorSize;
    }

    if (s_device->lpVtbl->CreateCommandAllocator(s_device, D3D12_COMMAND_LIST_TYPE_DIRECT, &IID_ID3D12CommandAllocator, &s_commandAllocator) < 0)
        return false;

    return true;
}

/**
 等待上一幀處理完成
*/
static bool WaitForPreviousFrame(void)
{
    // WAITING FOR THE FRAME TO COMPLETE BEFORE CONTINUING IS NOT BEST PRACTICE.
    // This is code implemented as such for simplicity. The D3D12HelloFrameBuffering
    // sample illustrates how to use fences for efficient resource usage and to
    // maximize GPU utilization.

    // Signal and increment the fence value.
    const uint64_t fence = s_fenceValue;
    if (s_commandQueue->lpVtbl->Signal(s_commandQueue, s_fence, fence) < 0)
        return false;

    s_fenceValue++;

    // Wait until the previous frame is finished.
    if (s_fence->lpVtbl->GetCompletedValue(s_fence) < fence)
    {
        if (s_fence->lpVtbl->SetEventOnCompletion(s_fence, fence, s_fenceEvent) < 0)
            return false;

        WaitForSingleObject(s_fenceEvent, INFINITE);
    }

    s_frameIndex = s_swapChain->lpVtbl->GetCurrentBackBufferIndex(s_swapChain);

    return true;
}

/**
 加載本demo所需的物資
*/
static bool LoadAssets(void)
{
    // Create an empty root signature.
    D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc = { 0, NULL, 0, NULL, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT };

    ID3DBlob *signature;
    ID3DBlob *error;

    if (D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error) < 0)
        return false;

    if (s_device->lpVtbl->CreateRootSignature(s_device, 0, signature->lpVtbl->GetBufferPointer(signature),
        signature->lpVtbl->GetBufferSize(signature), &IID_ID3D12RootSignature, &s_rootSignature) < 0)
        return false;

    // Create the pipeline state, which includes compiling and loading shaders.
    ID3DBlob *vertexShader;
    ID3DBlob *pixelShader;

    // Enable better shader debugging with the graphics debugging tools.
    // 若不允許調試,則將compileFlags置為0即可
    uint32_t compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;

    if (D3DCompileFromFile(L"shaders.hlsl", NULL, NULL, "VSMain", "vs_5_0", compileFlags, 0, &vertexShader, NULL) < 0)
        return false;

    if (D3DCompileFromFile(L"shaders.hlsl", NULL, NULL, "PSMain", "ps_5_0", compileFlags, 0, &pixelShader, NULL) < 0)
        return false;

    // Define the vertex input layout.
    D3D12_INPUT_ELEMENT_DESC inputElementDescs[] =
    {
        { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
        { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
    };

    // Describe and create the graphics pipeline state object (PSO).
    D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = { 0 };
    psoDesc.InputLayout = (D3D12_INPUT_LAYOUT_DESC){ inputElementDescs, _countof(inputElementDescs) };
    psoDesc.pRootSignature = s_rootSignature;
    psoDesc.VS = (D3D12_SHADER_BYTECODE) { vertexShader->lpVtbl->GetBufferPointer(vertexShader),
        vertexShader->lpVtbl->GetBufferSize(vertexShader) };
    psoDesc.PS = (D3D12_SHADER_BYTECODE) { pixelShader->lpVtbl->GetBufferPointer(pixelShader),
        pixelShader->lpVtbl->GetBufferSize(pixelShader) };
    // 使用默認的光柵化狀態
    psoDesc.RasterizerState = (D3D12_RASTERIZER_DESC) { D3D12_FILL_MODE_SOLID, D3D12_CULL_MODE_NONE, FALSE, 0, 0.0f, 0.0f,
        TRUE, FALSE, FALSE, 0, D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF };
    psoDesc.BlendState = (D3D12_BLEND_DESC) { FALSE, FALSE, 
    { [0] = { FALSE, FALSE, D3D12_BLEND_SRC_ALPHA, D3D12_BLEND_INV_SRC_ALPHA, D3D12_BLEND_OP_ADD, D3D12_BLEND_ONE, D3D12_BLEND_ZERO,
        D3D12_BLEND_OP_ADD, D3D12_LOGIC_OP_NOOP, D3D12_COLOR_WRITE_ENABLE_ALL } }
    };
    psoDesc.DepthStencilState.DepthEnable = FALSE;
    psoDesc.DepthStencilState.StencilEnable = FALSE;
    psoDesc.SampleMask = UINT32_MAX;
    psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
    psoDesc.NumRenderTargets = 1;
    psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
    psoDesc.SampleDesc.Count = 1;
    if (s_device->lpVtbl->CreateGraphicsPipelineState(s_device, &psoDesc, &IID_ID3D12PipelineState, &s_pipelineState) < 0)
        return false;

    // Create the command list.
    if (s_device->lpVtbl->CreateCommandList(s_device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT, s_commandAllocator, s_pipelineState,
        &IID_ID3D12GraphicsCommandList, &s_commandList) < 0)
        return false;

    // Command lists are created in the recording state, but there is nothing
    // to record yet. The main loop expects it to be closed, so close it now.
    if (s_commandList->lpVtbl->Close(s_commandList) < 0)
        return false;

    // Create the vertex buffer.
    // Define the geometry for a triangle.
    const float aspectRatio = s_viewport.Height / s_viewport.Width;

    struct Vertex
    {
        float position[4];
        float color[4];
    } triangleVertices[] =
    {
        // Direct3D是以左手作為前面背面頂點排列的依據
        { { 0.0f, 0.75f * aspectRatio, 0.0f, 1.0f },{ 1.0f, 0.0f, 0.0f, 1.0f } },   // 中上頂點
        { { 0.5f, -0.75f * aspectRatio, 0.0f, 1.0f },{ 0.0f, 1.0f, 0.0f, 1.0f } },  // 右下頂點
        { { -0.5f, -0.75f * aspectRatio, 0.0f, 1.0f },{ 0.0f, 0.0f, 1.0f, 1.0f } }  // 左下頂點
    };

    const size_t vertexBufferSize = sizeof(triangleVertices);

    // Note: using upload heaps to transfer static data like vert buffers is not 
    // recommended. Every time the GPU needs it, the upload heap will be marshalled 
    // over. Please read up on Default Heap usage. An upload heap is used here for 
    // code simplicity and because there are very few verts to actually transfer.
    D3D12_HEAP_PROPERTIES heapProperties = { D3D12_HEAP_TYPE_UPLOAD, D3D12_CPU_PAGE_PROPERTY_UNKNOWN, D3D12_MEMORY_POOL_UNKNOWN,
        1, 1  };
    D3D12_RESOURCE_DESC resourceDesc = { D3D12_RESOURCE_DIMENSION_BUFFER, 0, vertexBufferSize, 1, 1, 1, DXGI_FORMAT_UNKNOWN,
    { 1, 0 }, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, D3D12_RESOURCE_FLAG_NONE };

    if (s_device->lpVtbl->CreateCommittedResource(s_device, &heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc,
        D3D12_RESOURCE_STATE_GENERIC_READ, NULL, &IID_ID3D12Resource, &s_vertexBuffer) < 0)
        return false;

    // Copy the triangle data to the vertex buffer.
    uint8_t* pVertexDataBegin;
    D3D12_RANGE readRange = { 0, 0 };   // We do not intend to read from this resource on the CPU.	
    if (s_vertexBuffer->lpVtbl->Map(s_vertexBuffer, 0, &readRange, &pVertexDataBegin) < 0)
        return false;
    memcpy(pVertexDataBegin, triangleVertices, sizeof(triangleVertices));
    s_vertexBuffer->lpVtbl->Unmap(s_vertexBuffer, 0, NULL);

    // Initialize the vertex buffer view.
    s_vertexBufferView.BufferLocation = s_vertexBuffer->lpVtbl->GetGPUVirtualAddress(s_vertexBuffer);
    s_vertexBufferView.StrideInBytes = sizeof(triangleVertices[0]);
    s_vertexBufferView.SizeInBytes = (uint32_t)vertexBufferSize;

    // Create synchronization objects and wait until assets have been uploaded to the GPU.
    if (s_device->lpVtbl->CreateFence(s_device, 0, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, &s_fence) < 0)
        return false;
    s_fenceValue = 1;

    // Create an event handle to use for frame synchronization.
    s_fenceEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (s_fenceEvent == NULL)
    {
        if (HRESULT_FROM_WIN32(GetLastError()) < 0)
            return false;
    }

    // Wait for the command list to execute; we are reusing the same command 
    // list in our main loop but for now, we just want to wait for setup to 
    // complete before continuing.
    WaitForPreviousFrame();

    return true;
}

/**
 填充命令隊列
*/
static bool PopulateCommandList(void)
{
    // Command list allocators can only be reset when the associated 
    // command lists have finished execution on the GPU; apps should use 
    // fences to determine GPU execution progress.
    if (s_commandAllocator->lpVtbl->Reset(s_commandAllocator) < 0)
        return false;

    // However, when ExecuteCommandList() is called on a particular command 
    // list, that command list can then be reset at any time and must be before 
    // re-recording.
    if (s_commandList->lpVtbl->Reset(s_commandList, s_commandAllocator, s_pipelineState) < 0)
        return false;

    // Set necessary state.
    s_commandList->lpVtbl->SetGraphicsRootSignature(s_commandList, s_rootSignature);
    s_commandList->lpVtbl->RSSetViewports(s_commandList, 1, &s_viewport);
    s_commandList->lpVtbl->RSSetScissorRects(s_commandList, 1, &s_scissorRect);

    // Indicate that the back buffer will be used as a render target.
    D3D12_RESOURCE_BARRIER barrier = { D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_BARRIER_FLAG_NONE,
        .Transition = {s_renderTargets[s_frameIndex], D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET} };
    s_commandList->lpVtbl->ResourceBarrier(s_commandList, 1, &barrier);

    D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle;
    s_rtvHeap->lpVtbl->GetCPUDescriptorHandleForHeapStart(s_rtvHeap, &rtvHandle);
    rtvHandle.ptr += s_frameIndex * s_rtvDescriptorSize;
    s_commandList->lpVtbl->OMSetRenderTargets(s_commandList, 1, &rtvHandle, FALSE, NULL);

    // Record commands.
    const float clearColor[] = { 0.0f, 0.2f, 0.4f, 1.0f };
    s_commandList->lpVtbl->ClearRenderTargetView(s_commandList, rtvHandle, clearColor, 0, NULL);
    s_commandList->lpVtbl->IASetPrimitiveTopology(s_commandList, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    s_commandList->lpVtbl->IASetVertexBuffers(s_commandList, 0, 1, &s_vertexBufferView);
    s_commandList->lpVtbl->DrawInstanced(s_commandList, 3, 1, 0, 0);

    // Indicate that the back buffer will now be used to present.
    D3D12_RESOURCE_BARRIER barrier2 = { D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_BARRIER_FLAG_NONE,
        .Transition = { s_renderTargets[s_frameIndex], D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT } };
   s_commandList->lpVtbl->ResourceBarrier(s_commandList, 1, &barrier2);

    if (s_commandList->lpVtbl->Close(s_commandList) < 0)
        return false;

    return true;
}

/**
 渲染場景
*/
static bool Render(void)
{
    // Record all the commands we need to render the scene into the command list.
    if (!PopulateCommandList())
        return false;

    // Execute the command list.
    ID3D12CommandList* ppCommandLists[] = { (ID3D12CommandList*)s_commandList };
    s_commandQueue->lpVtbl->ExecuteCommandLists(s_commandQueue, _countof(ppCommandLists), ppCommandLists);

    // Present the frame.
    if (s_swapChain->lpVtbl->Present(s_swapChain, 1, 0) < 0)
        return false;

    if (!WaitForPreviousFrame())
        return false;

    return true;
}

/**
 清除資源
*/
static void CleanupResource(void)
{
    if (s_fence != NULL)
        s_fence->lpVtbl->Release(s_fence);
    if (s_vertexBuffer != NULL)
        s_vertexBuffer->lpVtbl->Release(s_vertexBuffer);
    if (s_rootSignature != NULL)
        s_rootSignature->lpVtbl->Release(s_rootSignature);
    if (s_rtvHeap != NULL)
        s_rtvHeap->lpVtbl->Release(s_rtvHeap);
    for (int i = 0; i < FRAME_COUNT; i++)
    {
        if(s_renderTargets[i] != NULL)
            s_renderTargets[i]->lpVtbl->Release(s_renderTargets[i]);
    }
    if (s_pipelineState != NULL)
        s_pipelineState->lpVtbl->Release(s_pipelineState);
    if (s_commandList != NULL)
        s_commandList->lpVtbl->Release(s_commandList);
    if (s_commandAllocator != NULL)
        s_commandAllocator->lpVtbl->Release(s_commandAllocator);
    if (s_commandQueue != NULL)
        s_commandQueue->lpVtbl->Release(s_commandQueue);
    if (s_swapChain != NULL)
        s_swapChain->lpVtbl->Release(s_swapChain);
    if (s_device != NULL)
        s_device->lpVtbl->Release(s_device);
}

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: Place code here.

    // Initialize global strings
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_DIRECT3D12TEST, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // Perform application initialization:
    HWND hWnd = InitInstance(hInstance, nCmdShow);
    if (hWnd == NULL)
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_DIRECT3D12TEST));

    // 初始化s_viewport與s_scissorRect對象
    s_viewport.Width = (float)s_windowWidth;
    s_viewport.Height = (float)s_windowHeight;
    s_viewport.MaxDepth = 1.0f;

    s_scissorRect.right = (long)s_windowWidth;
    s_scissorRect.bottom = (long)s_windowHeight;

    if (!LoadPipeline(hWnd))
        return FALSE;

    if (!LoadAssets())
        return FALSE;

    if (!Render())
        return FALSE;

    MSG msg;

    // Main message loop:
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}



//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_DIRECT3D12TEST));
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_DIRECT3D12TEST);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
HWND InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // Store instance handle in our global variable

   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, s_windowWidth, s_windowHeight, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return NULL;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return hWnd;
}

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND  - process the application menu
//  WM_PAINT    - Paint the main window
//  WM_DESTROY  - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // Parse the menu selections:
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: Add any drawing code that uses hdc here...

            // 我們這裡再繪制一幀
            if(s_pipelineState != NULL)
                Render();

            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        // 清除D3D相關的資源
        CleanupResource();

        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

輸入完成之後,我們可以在菜單欄File下面找到Advanced Save Options...,可以將Encoding改為Unicode(UTF-8 without Signature),這樣我們就可以在所有操作系統上看到正常的中文漢字了。否則在不少系統上不支持GBK或GB2312,會導致漢字部分出現亂碼。

下面這個文件就是繪制三角形時所需要的HLSL文件,我們不能直接將它們添加到工程中,因為IDE會自動編譯它們,然後導致找不到main入口而出現連接錯誤。所以我們將這個文件存放到與上述源文件同一目錄下即可,然後再將它復制到與我們最後生成的exe可執行文件的同一目錄下,這樣就可以直接點擊加載應用了,而不需要通過Visual Studio來啟動。該文件名為shaders.hlsl。

//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License (MIT).
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

struct PSInput
{
	float4 position : SV_POSITION;
	float4 color : COLOR;
};

PSInput VSMain(float4 position : POSITION, float4 color : COLOR)
{
	PSInput result;

	result.position = position;
	result.color = color;

	return result;
}

float4 PSMain(PSInput input) : SV_TARGET
{
	return input.color;
}

全都完成之後,我們就可以編譯構建應用,然後會自動彈出窗口顯示一個彩色三角形了。我們還可以點擊窗口放大按鈕與還原按鈕,這些操作都能正常顯示。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved