一个引擎(1)

一个引擎

这是一个使用C++和DirectX 12编写的简单的游戏引擎。关于DirectX 12的文章已经有很多,此处不再进行介绍。

交换链、设备、窗口

抛开所有,引擎工作需要在显卡上进行渲染,并将渲染的内容通过交换链呈现在一个窗口上。窗口是Windows系统上一个相当普遍的概念,而交换链是指工作在DX和Windows之间的一个抽象层,负责两者间的交互。DX12由于支持了Multi Adapter特性,设备是基于一个适配器创建的。一个适配器下可以是一个物理GPU,也可以是多个物理GPU,也可以是抽象的Warp显示设备,因此适配器是对物理设备的一个抽象。私以为交换链是DX和Windows的中间件,因此可以这样想:交换链的后端是DX,而交换链的前端可以是Win32窗口或UWP窗口,对应交换链创建的两个方法:CreateSwapChainForHwnd和CreateSwapChainForCoreWindow。

创建一个Win32窗口

该引擎还没有添加UWP支持,所以只创建一个Win32窗口。窗口创建方式和Win32应用一样。

#include"onwind.h"

WNDCLASSEX wnd;
HWND window;
int state;
int screenw;
int screenh;

LRESULT WINAPI WinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case(WM_CREATE):
    {
        LPCREATESTRUCT pCreateStruct = reinterpret_cast<LPCREATESTRUCT>(lParam);
        SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pCreateStruct->lpCreateParams));
        break;
    }
    case(WM_DESTROY):
    {
        PostQuitMessage(0);
        break;
    }
    case(WM_PAINT):
    {
        break;
    }
    case(WM_USER):
    {
        var cc = LOWORD(lParam);
        char* l = new char[1];
        l[0] = { (char)(cc + '0') };
        SetWindowText(hwnd, ToLPCWSTR(l));
        break;
    }
    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    std::wstring windowTitle(_T("AnEngine"));
    std::wstring windowClassName(_T("Window"));
    screenw = 1280;
    screenh = 720;

    int argc;
    LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
    LocalFree(argv);

    wnd = { 0 };
    wnd.cbSize = sizeof(WNDCLASSEX);
    wnd.style = CS_HREDRAW | CS_VREDRAW;
    wnd.lpfnWndProc = WinProc;
    wnd.hInstance = hInstance;
    wnd.hIcon = NULL;
    wnd.hCursor = LoadCursor(NULL, IDC_ARROW);
    wnd.lpszClassName = windowClassName.c_str();

    RegisterClassEx(&wnd);

    RECT windowRect = { 0, 0, static_cast<LONG>(screenw), static_cast<LONG>(screenh) };
    AdjustWindowRect(&windowRect, WS_OVERLAPPEDWINDOW, FALSE);

    window = CreateWindow(
        windowClassName.c_str(),
        windowTitle.c_str(),
        WS_OVERLAPPEDWINDOW ^ WS_MAXIMIZEBOX ^ WS_BORDER ^ WS_SIZEBOX,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        windowRect.right - windowRect.left,
        windowRect.bottom - windowRect.top,
        nullptr,
        nullptr,
        hInstance,
        nullptr);

    if (window == NULL)
    {
        MessageBox(NULL, _T("Error"), _T("Error1"), 0);
        state = 1;
        return 0;
    }

    ShowWindow(window, nCmdShow);

    MSG msg = {};
    while (msg.message != WM_QUIT)
    {
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    UnregisterClass(wnd.lpszClassName, hInstance);
    return 0;
}

这样就创建了一个1280*720的窗口,窗口矩形区域通过AdjustWindowRect指定,所以这个分辨率指的是客户区,既窗口中不含标题栏的部分。由于游戏和普通的桌面应用不同,所以在消息循环时使用了比GetMessage更好的PeekMessage,可以主动从消息队列中获取消息,性能优于GetMessage。

onwind.h

这是我写的一个辅助性的头文件,其中包含了大部分头文件的引用,包括Windows.h,也做了一些针对不同平台的函数重载。比如:

inline LPCWSTR ToLPCWSTR(std::string& orig)
{
    size_t origsize = orig.length() + 1;
    const size_t newsize = 100;
    size_t convertedChars = 0;
    wchar_t *wcstring = (wchar_t *)malloc(sizeof(wchar_t) *(origsize - 1));
    mbstowcs_s(nullptr, wcstring, origsize, orig.c_str(), _TRUNCATE);
    return wcstring;
}

inline void ThrowIfFailed(HRESULT hr)
{
    if (FAILED(hr))
    {
        throw std::exception();
    }
}

在Win32和Com中有许多需要字符串参数的地方,在Unicode字符集下需要类型为LPCWSTR(wchar_t*),而多字节字符集需要LPCSTR(char*),因此需要将string进行格式转换。Win32和Com的一些接口的返回值为HRESULT,如果返回S_OK则说明接口调用成功,所以在失败的时候需要抛出一个异常(因为有时需要在try-catch中针对异常进行处理)。

小结

以上,一个引擎的窗口就做好了,但是没有任何显示的功能。因此接下来就需要做些什么来让其显示。接下来WinMain和WinProc需要改动的地方不多,毕竟DirectX 12更加底层。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是使用DirectX 12渲染超大图片的基本代码: ```cpp #include <d3d12.h> #include <dxgi1_4.h> #include <DirectXMath.h> #include <DirectXTex.h> #include <wrl/client.h> using Microsoft::WRL::ComPtr; // 渲染窗口大小 const UINT WindowWidth = 800; const UINT WindowHeight = 600; // 超大图片大小 const UINT ImageWidth = 10000; const UINT ImageHeight = 10000; // 超大图片文件名 const wchar_t* ImageFilename = L"UltraLargeImage.jpg"; // 全局变量 ComPtr<ID3D12Device> g_Device; ComPtr<IDXGISwapChain3> g_SwapChain; ComPtr<ID3D12CommandQueue> g_CommandQueue; ComPtr<ID3D12CommandAllocator> g_CommandAllocator; ComPtr<ID3D12GraphicsCommandList> g_CommandList; ComPtr<ID3D12Resource> g_BackBuffer[2]; ComPtr<ID3D12Fence> g_Fence; UINT64 g_FenceValue = 0; HANDLE g_FenceEvent; // 初始化设备、命令队列等 bool InitializeDevice() { // 创建设备 HRESULT hr = D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&g_Device)); if (FAILED(hr)) return false; // 创建命令队列 D3D12_COMMAND_QUEUE_DESC commandQueueDesc = {}; commandQueueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; commandQueueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL; commandQueueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; commandQueueDesc.NodeMask = 0; hr = g_Device->CreateCommandQueue(&commandQueueDesc, IID_PPV_ARGS(&g_CommandQueue)); if (FAILED(hr)) return false; // 创建交换链 DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; swapChainDesc.Width = WindowWidth; swapChainDesc.Height = WindowHeight; swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; swapChainDesc.Stereo = FALSE; swapChainDesc.SampleDesc.Count = 1; swapChainDesc.SampleDesc.Quality = 0; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.BufferCount = 2; swapChainDesc.Scaling = DXGI_SCALING_STRETCH; swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; ComPtr<IDXGIFactory4> dxgiFactory; hr = CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)); if (FAILED(hr)) return false; hr = dxgiFactory->CreateSwapChainForHwnd(g_CommandQueue.Get(), GetForegroundWindow(), &swapChainDesc, nullptr, nullptr, &g_SwapChain); if (FAILED(hr)) return false; // 创建命令分配器、命令列表 hr = g_Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&g_CommandAllocator)); if (FAILED(hr)) return false; hr = g_Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, g_CommandAllocator.Get(), nullptr, IID_PPV_ARGS(&g_CommandList)); if (FAILED(hr)) return false; // 创建RTV CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(g_Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV), 0); for (UINT i = 0; i < 2; i++) { hr = g_SwapChain->GetBuffer(i, IID_PPV_ARGS(&g_BackBuffer[i])); if (FAILED(hr)) return false; g_Device->CreateRenderTargetView(g_BackBuffer[i].Get(), nullptr, rtvHandle); rtvHandle.Offset(1, g_Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV)); } // 创建围栏 hr = g_Device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&g_Fence)); if (FAILED(hr)) return false; g_FenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); if (g_FenceEvent == nullptr) return false; return true; } // 加载超大图片 ComPtr<ID3D12Resource> LoadImage() { // 加载图片 std::unique_ptr<DirectX::ScratchImage> image(new DirectX::ScratchImage()); HRESULT hr = DirectX::LoadFromWICFile(ImageFilename, DirectX::WIC_FLAGS_NONE, nullptr, *image); if (FAILED(hr)) return nullptr; // 创建纹理资源 ComPtr<ID3D12Resource> texture; D3D12_RESOURCE_DESC textureDesc = {}; textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; textureDesc.Alignment = 0; textureDesc.Width = ImageWidth; textureDesc.Height = ImageHeight; textureDesc.DepthOrArraySize = 1; textureDesc.MipLevels = 1; textureDesc.Format = image->GetMetadata().format; textureDesc.SampleDesc.Count = 1; textureDesc.SampleDesc.Quality = 0; textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; textureDesc.Flags = D3D12_RESOURCE_FLAG_NONE; hr = g_Device->CreateCommittedResource(&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, &textureDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&texture)); if (FAILED(hr)) return nullptr; // 上传图片数据 const DirectX::Image* imageData = image->GetImage(0, 0, 0); D3D12_SUBRESOURCE_DATA textureData = {}; textureData.pData = imageData->pixels; textureData.RowPitch = imageData->rowPitch; textureData.SlicePitch = imageData->slicePitch; UpdateSubresources(g_CommandList.Get(), texture.Get(), g_BackBuffer[0].Get(), 0, 0, 1, &textureData); // 转换资源状态 CD3DX12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(texture.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); g_CommandList->ResourceBarrier(1, &barrier); return texture; } // 渲染函数 void Render() { // 等待上一帧完成 const UINT currentBackBufferIndex = g_SwapChain->GetCurrentBackBufferIndex(); if (g_Fence->GetCompletedValue() < g_FenceValue) { hr = g_Fence->SetEventOnCompletion(g_FenceValue, g_FenceEvent); if (FAILED(hr)) return; WaitForSingleObject(g_FenceEvent, INFINITE); } // 开始渲染 g_CommandAllocator->Reset(); g_CommandList->Reset(g_CommandAllocator.Get(), nullptr); // 设置渲染目标 CD3DX12_RESOURCE_BARRIER barrierToRenderTarget = CD3DX12_RESOURCE_BARRIER::Transition(g_BackBuffer[currentBackBufferIndex].Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET); g_CommandList->ResourceBarrier(1, &barrierToRenderTarget); CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(g_Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV), currentBackBufferIndex); g_CommandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr); // 清空渲染目标 const float clearColor[] = { 0.0f, 0.0f, 0.0f, 1.0f }; g_CommandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr); // 绘制超大图片 // ... // 结束渲染 CD3DX12_RESOURCE_BARRIER barrierToPresent = CD3DX12_RESOURCE_BARRIER::Transition(g_BackBuffer[currentBackBufferIndex].Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT); g_CommandList->ResourceBarrier(1, &barrierToPresent); g_CommandList->Close(); // 提交命令列表 ID3D12CommandList* commandLists[] = { g_CommandList.Get() }; g_CommandQueue->ExecuteCommandLists(_countof(commandLists), commandLists); // 信号围栏 g_FenceValue++; hr = g_CommandQueue->Signal(g_Fence.Get(), g_FenceValue); if (FAILED(hr)) return; // 呈现画面 g_SwapChain->Present(1, 0); } // 程序入口 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // 初始化窗口等 // ... // 初始化设备、命令队列等 if (!InitializeDevice()) return -1; // 加载超大图片 ComPtr<ID3D12Resource> texture = LoadImage(); if (texture == nullptr) return -1; // 进入消息循环 // ... } ``` 其中,`LoadImage`函数用于加载超大图片,返回一个纹理资源。在绘制超大图片时,可以使用多次绘制来分批渲染,或者使用视口和裁剪矩形来渲染部分区域,以避免一次渲染过多数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值