direct3d 11 和 2D:将顶点的坐标作为 int 而不是 float 传递

direct3d 11 and 2D: pass coordinates of a vertex as int and not float

我的目的是编写仅使用 Direct3D 11 的 2D 工具包后端(没有其他库,如 Direct2D、SpriteBatch 或其他)。

请注意,我是第一次使用 Direct3D,目前正在学习 d3D 11。

所以现在,我可以显示我想要的颜色的三角形或矩形。

我的 C 代码的顶点结构包含 2 个用于位置的浮点数和 4 个用于颜色的无符号字符。在我的顶点着色器中,顶点结构有 2 个浮点数用于顶点位置,4 个浮点数用于颜色。

我已经说过,如果我在 D3D11_INPUT_ELEMENT_DESC 数组中使用 DXGI_FORMAT_R8G8B8A8_UNORM 作为颜色,那么颜色会自动从值 0 到 255 插入到值 0.0f 到 1.0f。当我阅读文档(DXGI Format anumeration,_UNORM 的描述)时似乎很合理:

"无符号规范化整数;在资源中被解释为无符号整数,在着色器中被解释为 [0, 1] 范围内的无符号规范化浮点值。所有 0 映射到 0.0f , 并且所有 1 都映射到 1.0f。表示从 0.0f 到 1.0f 的一系列均匀分布的浮点值。例如,2 位 UNORM 表示 0.0f、1/3、2/3 和 1.0f ."

或者至少我是这样解释这个文档的(我可能是错的)。并且三角形的颜色是正确的。

对于像素,我想做的是相同的:如果我为坐标传递一个整数(x 介于 0 和 window -1 的宽度之间,y 介于 0 和 window -1 的高度之间window - 1),然后它被顶点着色器解释为正确的带符号归一化浮点值(x 为 -1.0f 到 1.0f,y 为 1.0f 到 -1.0f)。我在 Vertex C 结构和 D3D11_INPUT_ELEMENT_DESC 数组中尝试了几个值,但没有成功。所以我有两个问题:

  1. 可能吗?
  2. 如果不可能,在 C 代码或着色器代码(视口作为常量缓冲区)中转换坐标是否更快?从 int 到 float 的转换见下面代码中的宏 XF 和 YF。

下面是我显示一个简单三角形的完整代码,后面是顶点和像素着色器的 HLSL 代码。我用的是Direct3D的Capi。我支持Win 7和Win 10。

源代码:

/* Windows 10 */
#define _WIN32_WINNT 0x0A00

#if defined _WIN32_WINNT && _WIN32_WINNT >= 0x0A00
# define HAVE_WIN10
#endif

#include <stdio.h>

#ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
#endif

#include <windows.h>

/* C API for d3d11 */
#define COBJMACROS

#include <guiddef.h>

#ifdef HAVE_WIN10
# include <dxgi1_3.h>
#else
# include <dxgi.h>
#endif
#include <d3d11.h>

#include "d3d11_vs.h"
#include "d3d11_ps.h"

/* comment for no debug informations */
#define _DEBUG

#ifdef _DEBUG
# define FCT \
do { printf(" * %s\n", __FUNCTION__); fflush(stdout); } while (0)
#else
# define FCT \
do { } while (0)
#endif

#define XF(w,x) ((float)(2 * (x) - (w)) / (float)(w))
#define YF(h,y) ((float)((h) - 2 * (y)) / (float)(h))

typedef struct Window Window;
typedef struct D3d D3d;

struct Window
{
    HINSTANCE instance;
    RECT rect;
    HWND win;
    D3d *d3d;
};

struct D3d
{
#ifdef HAVE_WIN10
    IDXGIFactory2 *dxgi_factory;
    IDXGISwapChain1 *dxgi_swapchain;
#else
    IDXGIFactory *dxgi_factory;
    IDXGISwapChain *dxgi_swapchain;
#endif
    ID3D11Device *d3d_device;
    ID3D11DeviceContext *d3d_device_ctx;
    ID3D11RenderTargetView *d3d_render_target_view;
    ID3D11InputLayout *d3d_input_layout;

    ID3D11VertexShader *d3d_vertex_shader;
    ID3D11PixelShader *d3d_pixel_shader;
    D3D11_VIEWPORT viewport;
    Window *win;
    unsigned int vsync : 1;
};

typedef struct
{
    FLOAT x;
    FLOAT y;
    BYTE r;
    BYTE g;
    BYTE b;
    BYTE a;
} Vertex;

void d3d_resize(D3d *d3d, UINT width, UINT height);

void d3d_render(D3d *d3d);

/************************* Window *************************/

LRESULT CALLBACK
_window_procedure(HWND   window,
                  UINT   message,
                  WPARAM window_param,
                  LPARAM data_param)
{
    switch (message)
    {
    case WM_CLOSE:
        PostQuitMessage(0);
        return 0;
    case WM_KEYUP:
        if (window_param == 'Q')
        {
            PostQuitMessage(0);
        }
        return 0;
    case WM_ERASEBKGND:
        /* no need to erase back */
        return 1;
    /* GDI notifications */
    case WM_CREATE:
#ifdef _DEBUG
        printf(" * WM_CREATE\n");
        fflush(stdout);
#endif
        return 0;
    case WM_SIZE:
    {
        Window *win;

#ifdef _DEBUG
        printf(" * WM_SIZE\n");
        fflush(stdout);
#endif

        win = (Window *)GetWindowLongPtr(window, GWLP_USERDATA);
        d3d_resize(win->d3d,
                   (UINT)LOWORD(data_param), (UINT)HIWORD(data_param));

        return 0;
    }
    case WM_PAINT:
    {
#ifdef _DEBUG
        printf(" * WM_PAINT\n");
        fflush(stdout);
#endif

        if (GetUpdateRect(window, NULL, FALSE))
        {
            PAINTSTRUCT ps;
            Window *win;

            BeginPaint(window, &ps);

            win = (Window *)GetWindowLongPtr(window, GWLP_USERDATA);
            d3d_render(win->d3d);

            EndPaint(window, &ps);
        }

        return 0;
    }
    default:
        return DefWindowProc(window, message, window_param, data_param);
    }
}

Window *window_new(int x, int y, int w, int h)
{
    WNDCLASS wc;
    RECT r;
    Window *win;

    win = (Window *)calloc(1, sizeof(Window));
    if (!win)
        return NULL;

    win->instance = GetModuleHandle(NULL);
    if (!win->instance)
        goto free_win;

    memset(&wc, 0, sizeof(WNDCLASS));
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = _window_procedure;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = win->instance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = NULL;
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "D3D";

    if (!RegisterClass(&wc))
        goto free_library;

    r.left = 0;
    r.top = 0;
    r.right = w;
    r.bottom = h;

    if (!AdjustWindowRectEx(&r,
                            WS_OVERLAPPEDWINDOW | WS_SIZEBOX,
                            FALSE,
                            0U))
        goto unregister_class;

    win->win = CreateWindowEx(0U,
                              "D3D", "Test",
                              WS_OVERLAPPEDWINDOW | WS_SIZEBOX,
                              x, y,
                              r.right - r.left,
                              r.bottom - r.top,
                              NULL,
                              NULL, win->instance, NULL);
    if (!win->win)
        goto unregister_class;

    return win;

  unregister_class:
    UnregisterClass("D2D", win->instance);
  free_library:
    FreeLibrary(win->instance);
  free_win:
    free(win);

    return NULL;
}

void window_del(Window *win)
{
    if (!win)
        return;

    DestroyWindow(win->win);
    UnregisterClass("D2D", win->instance);
    FreeLibrary(win->instance);
    free(win);
}

void window_show(Window *win)
{
    ShowWindow(win->win, SW_SHOWNORMAL);
}

/************************** D3D11 **************************/

static void d3d_refresh_rate_get(D3d *d3d, UINT *num, UINT *den)
{
    DXGI_MODE_DESC *display_mode_list = NULL; /* 28 bytes */
    IDXGIAdapter *dxgi_adapter;
    IDXGIOutput *dxgi_output;
    UINT nbr_modes;
    UINT i;
    HRESULT res;

    *num = 0U;
    *den = 1U;

    if (!d3d->vsync)
        return;

    /* adapter of primary desktop : pass 0U */
    res = IDXGIFactory_EnumAdapters(d3d->dxgi_factory, 0U, &dxgi_adapter);
    if (FAILED(res))
        return;

    /* output of primary desktop : pass 0U */
    res = IDXGIAdapter_EnumOutputs(dxgi_adapter, 0U, &dxgi_output);
    if (FAILED(res))
        goto release_dxgi_adapter;

    /* number of mode that fit the format */
    res = IDXGIOutput_GetDisplayModeList(dxgi_output,
                                         DXGI_FORMAT_B8G8R8A8_UNORM,
                                         DXGI_ENUM_MODES_INTERLACED,
                                         &nbr_modes, NULL);
    if (FAILED(res))
        goto release_dxgi_output;

    printf("display mode list : %d\n", nbr_modes);
    fflush(stdout);
    display_mode_list = (DXGI_MODE_DESC *)malloc(nbr_modes * sizeof(DXGI_MODE_DESC));
    if (!display_mode_list)
        goto release_dxgi_output;

    /* fill the mode list */
    res = IDXGIOutput_GetDisplayModeList(dxgi_output,
                                         DXGI_FORMAT_B8G8R8A8_UNORM,
                                         DXGI_ENUM_MODES_INTERLACED,
                                         &nbr_modes, display_mode_list);
    if (FAILED(res))
        goto free_mode_list;

    for (i = 0; i < nbr_modes; i++)
    {
        if ((display_mode_list[i].Width == (UINT)GetSystemMetrics(SM_CXSCREEN)) &&
            (display_mode_list[i].Height == (UINT)GetSystemMetrics(SM_CYSCREEN)))
        {
            *num = display_mode_list[i].RefreshRate.Numerator;
            *den = display_mode_list[i].RefreshRate.Denominator;
            break;
        }
    }

#ifdef _DEBUG
    {
        DXGI_ADAPTER_DESC adapter_desc;

        IDXGIAdapter_GetDesc(dxgi_adapter, &adapter_desc);
        printf(" * video mem: %llu B, %llu MB\n",
               adapter_desc.DedicatedVideoMemory,
               adapter_desc.DedicatedVideoMemory / 1024 / 1024);
        fflush(stdout);
        wprintf(L" * description: %ls\n", adapter_desc.Description);
        fflush(stdout);
    }
#endif

  free_mode_list:
    free(display_mode_list);
  release_dxgi_output:
    IDXGIOutput_Release(dxgi_output);
  release_dxgi_adapter:
    IDXGIFactory_Release(dxgi_adapter);
}

D3d *d3d_init(Window *win, int vsync)
{
    D3D11_INPUT_ELEMENT_DESC desc_ie[] =
    {
        { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
        { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 2 * sizeof(float), D3D11_INPUT_PER_VERTEX_DATA, 0 }
    };
#ifdef HAVE_WIN10
    DXGI_SWAP_CHAIN_DESC1 desc;
    DXGI_SWAP_CHAIN_FULLSCREEN_DESC desc_fs;
#else
    DXGI_SWAP_CHAIN_DESC desc;
#endif
    D3d *d3d;
    RECT r;
    HRESULT res;
    UINT flags;
    UINT num;
    UINT den;
    D3D_FEATURE_LEVEL feature_level[4];

    d3d = (D3d *)calloc(1, sizeof(D3d));
    if (!d3d)
        return NULL;

    d3d->vsync = vsync;
    win->d3d = d3d;
    d3d->win = win;

    /* create the DXGI factory */
    flags = 0;
#ifdef HAVE_WIN10
# ifdef _DEBUG
    flags = DXGI_CREATE_FACTORY_DEBUG;
# endif
    res = CreateDXGIFactory2(flags, &IID_IDXGIFactory2, (void **)&d3d->dxgi_factory);
#else
    res = CreateDXGIFactory(&IID_IDXGIFactory, (void **)&d3d->dxgi_factory);
#endif
    if (FAILED(res))
        goto free_d3d;

    /* single threaded for now */
    flags = D3D11_CREATE_DEVICE_SINGLETHREADED |
            D3D11_CREATE_DEVICE_BGRA_SUPPORT;
#ifdef HAVE_WIN10
# ifdef _DEBUG
    flags |= D3D11_CREATE_DEVICE_DEBUG;
# endif
#endif

    feature_level[0] = D3D_FEATURE_LEVEL_11_1;
    feature_level[1] = D3D_FEATURE_LEVEL_11_0;
    feature_level[2] = D3D_FEATURE_LEVEL_10_1;
    feature_level[3] = D3D_FEATURE_LEVEL_10_0;

    /* create device and device context with hardware support */
    res = D3D11CreateDevice(NULL,
                            D3D_DRIVER_TYPE_HARDWARE,
                            NULL,
                            flags,
                            feature_level,
                            3U,
                            D3D11_SDK_VERSION,
                            &d3d->d3d_device,
                            NULL,
                            &d3d->d3d_device_ctx);
    if (FAILED(res))
        goto release_dxgi_factory2;

    if (!GetClientRect(win->win, &r))
        goto release_d3d_device;

    /*
     * create the swap chain. It needs some settings...
     * the size of the internal buffers
     * the image format
     * the number of back buffers (>= 2 for flip model, see SwapEffect field)
     *
     * Settings are different in win 7 and win10
     */

    d3d_refresh_rate_get(d3d, &num, &den);

#ifdef HAVE_WIN10
    desc.Width = r.right - r.left;
    desc.Height = r.bottom - r.top;
    desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    desc.Stereo = FALSE;
#else
    desc.BufferDesc.Width = r.right - r.left;
    desc.BufferDesc.Height = r.bottom - r.top;
    desc.BufferDesc.RefreshRate.Numerator = num;
    desc.BufferDesc.RefreshRate.Denominator = den;
    desc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;;
    desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
#endif
    desc.SampleDesc.Count = 1U;
    desc.SampleDesc.Quality = 0U;
    desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    desc.BufferCount = 2U;
#ifdef HAVE_WIN10
    desc.Scaling = DXGI_SCALING_NONE;
#else
    desc.OutputWindow = win->win;
    desc.Windowed = TRUE;
#endif
    desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
#ifdef HAVE_WIN10
    desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
#endif
    desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

#ifdef HAVE_WIN10
    desc_fs.RefreshRate.Numerator = num;
    desc_fs.RefreshRate.Denominator = den;
    desc_fs.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    desc_fs.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
    desc_fs.Windowed = TRUE;
#endif

#ifdef HAVE_WIN10
    res = IDXGIFactory2_CreateSwapChainForHwnd(d3d->dxgi_factory,
                                               (IUnknown *)d3d->d3d_device,
                                               win->win,
                                               &desc,
                                               &desc_fs,
                                               NULL,
                                               &d3d->dxgi_swapchain);
#else
    res = IDXGIFactory_CreateSwapChain(d3d->dxgi_factory,
                                       (IUnknown *)d3d->d3d_device,
                                       &desc,
                                       &d3d->dxgi_swapchain);
#endif
    if (FAILED(res))
        goto release_d3d_device;

    /* Vertex shader */
    res = ID3D11Device_CreateVertexShader(d3d->d3d_device,
                                          d3d_vertex_shader,
                                          sizeof(d3d_vertex_shader),
                                          NULL,
                                          &d3d->d3d_vertex_shader);
    if (FAILED(res))
    {
        printf(" * CreateVertexShader() failed\n");
        goto release_dxgi_swapchain;
    }

    /* Pixel shader */
    res = ID3D11Device_CreatePixelShader(d3d->d3d_device,
                                         d3d_pixel_shader,
                                         sizeof(d3d_pixel_shader),
                                         NULL,
                                         &d3d->d3d_pixel_shader);
    if (FAILED(res))
    {
        printf(" * CreatePixelShader() failed\n");
        goto release_vertex_shader;
    }

    /* create the input layout */
    res = ID3D11Device_CreateInputLayout(d3d->d3d_device,
                                         desc_ie,
                                         sizeof(desc_ie) / sizeof(D3D11_INPUT_ELEMENT_DESC),
                                         d3d_vertex_shader,
                                         sizeof(d3d_vertex_shader),
                                         &d3d->d3d_input_layout);
    if (FAILED(res))
    {
        printf(" * CreateInputLayout() failed\n");
        goto release_pixel_shader;
    }

    return d3d;

  release_pixel_shader:
    ID3D11PixelShader_Release(d3d->d3d_pixel_shader);
  release_vertex_shader:
    ID3D11VertexShader_Release(d3d->d3d_vertex_shader);
  release_dxgi_swapchain:
#ifdef HAVE_WIN10
    IDXGISwapChain1_SetFullscreenState(d3d->dxgi_swapchain, FALSE, NULL);
    IDXGISwapChain1_Release(d3d->dxgi_swapchain);
#else
    IDXGISwapChain_SetFullscreenState(d3d->dxgi_swapchain, FALSE, NULL);
    IDXGISwapChain_Release(d3d->dxgi_swapchain);
#endif
  release_d3d_device:
    ID3D11DeviceContext_Release(d3d->d3d_device_ctx);
    ID3D11Device_Release(d3d->d3d_device);
  release_dxgi_factory2:
#ifdef HAVE_WIN10
    IDXGIFactory2_Release(d3d->dxgi_factory);
#else
    IDXGIFactory_Release(d3d->dxgi_factory);
#endif
  free_d3d:
    free(d3d);

    return NULL;
}

void d3d_shutdown(D3d *d3d)
{
#ifdef _DEBUG
    ID3D11Debug *d3d_debug;
    HRESULT res;
#endif

    if (!d3d)
        return;

#ifdef _DEBUG
    res = ID3D11Debug_QueryInterface(d3d->d3d_device, &IID_ID3D11Debug,
                                     (void **)&d3d_debug);
#endif

    ID3D11PixelShader_Release(d3d->d3d_pixel_shader);
    ID3D11VertexShader_Release(d3d->d3d_vertex_shader);
    ID3D11InputLayout_Release(d3d->d3d_input_layout);
    ID3D11RenderTargetView_Release(d3d->d3d_render_target_view);
#ifdef HAVE_WIN10
    IDXGISwapChain1_SetFullscreenState(d3d->dxgi_swapchain, FALSE, NULL);
    IDXGISwapChain1_Release(d3d->dxgi_swapchain);
#else
    IDXGISwapChain_SetFullscreenState(d3d->dxgi_swapchain, FALSE, NULL);
    IDXGISwapChain_Release(d3d->dxgi_swapchain);
#endif
    ID3D11DeviceContext_Release(d3d->d3d_device_ctx);
    ID3D11Device_Release(d3d->d3d_device);
#ifdef HAVE_WIN10
    IDXGIFactory2_Release(d3d->dxgi_factory);
#else
    IDXGIFactory_Release(d3d->dxgi_factory);
#endif
    free(d3d);

#ifdef _DEBUG
    if (SUCCEEDED(res))
    {
        ID3D11Debug_ReportLiveDeviceObjects(d3d_debug, D3D11_RLDO_DETAIL);
        ID3D11Debug_Release(d3d_debug);
    }
#endif
}

void d3d_resize(D3d *d3d, UINT width, UINT height)
{
    D3D11_RENDER_TARGET_VIEW_DESC desc_rtv;
    ID3D11Texture2D *back_buffer;
    HRESULT res;

    FCT;

    /* set viewport, depends on size of the window */
    d3d->viewport.TopLeftX = 0.0f;
    d3d->viewport.TopLeftY = 0.0f;
    d3d->viewport.Width = (float)width;
    d3d->viewport.Height = (float)height;
    d3d->viewport.MinDepth = 0.0f;
    d3d->viewport.MaxDepth = 1.0f;

    /* release the render target view */
    if (d3d->d3d_render_target_view)
        ID3D11RenderTargetView_Release(d3d->d3d_render_target_view);

    /* unset the render target view in the output merger */
    ID3D11DeviceContext_OMSetRenderTargets(d3d->d3d_device_ctx,
                                           0U, NULL, NULL);

    /* resize the internal nuffers of the swapt chain to the new size */
#ifdef HAVE_WIN10
    res = IDXGISwapChain1_ResizeBuffers(d3d->dxgi_swapchain,
                                        0U, /* preserve buffer count */
                                        width, height,
                                        DXGI_FORMAT_UNKNOWN, /* preserve format */
                                        0U);
#else
    res = IDXGISwapChain_ResizeBuffers(d3d->dxgi_swapchain,
                                       0U, /* preserve buffer count */
                                       width, height,
                                       DXGI_FORMAT_UNKNOWN, /* preserve format */
                                       0U);
#endif
    if ((res == DXGI_ERROR_DEVICE_REMOVED) ||
        (res == DXGI_ERROR_DEVICE_RESET) ||
        (res == DXGI_ERROR_DRIVER_INTERNAL_ERROR))
    {
        return;
    }

    if (FAILED(res))
    {
        printf("ResizeBuffers() failed\n");
        fflush(stdout);
        return;
    }

    /* get the internal buffer of the swap chain */
#ifdef HAVE_WIN10
    res = IDXGISwapChain1_GetBuffer(d3d->dxgi_swapchain, 0,
                                    &IID_ID3D11Texture2D,
                                    (void **)&back_buffer);
#else
    res = IDXGISwapChain_GetBuffer(d3d->dxgi_swapchain, 0,
                                   &IID_ID3D11Texture2D,
                                   (void **)&back_buffer);
#endif
    if (FAILED(res))
    {
        printf("swapchain GetBuffer() failed\n");
        fflush(stdout);
        return;
    }

    ZeroMemory(&desc_rtv, sizeof(D3D11_RENDER_TARGET_VIEW_DESC));
    desc_rtv.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    desc_rtv.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;

    /* create the new render target view from this internal buffer */
    res = ID3D11Device_CreateRenderTargetView(d3d->d3d_device,
                                              (ID3D11Resource *)back_buffer,
                                              &desc_rtv,
                                              &d3d->d3d_render_target_view);

    ID3D11Texture2D_Release(back_buffer);
}

/*** triangle ***/

typedef struct
{
    Vertex vertices[3];
    unsigned int indices[3];
    ID3D11Buffer *vertex_buffer;
    ID3D11Buffer *index_buffer; /* not useful for a single triangle */
    UINT stride;
    UINT offset;
    UINT count;
    UINT index_count;
} Triangle;

Triangle *triangle_new(D3d *d3d,
                       int w, int h,
                       int x1, int y1,
                       int x2, int y2,
                       int x3, int y3,
                       unsigned char r,
                       unsigned char g,
                       unsigned char b,
                       unsigned char a)
{
    D3D11_BUFFER_DESC desc;
    D3D11_SUBRESOURCE_DATA sr_data;
    Triangle *t;
    HRESULT res;

    t = (Triangle *)malloc(sizeof(Triangle));
    if (!t)
        return NULL;

    t->vertices[0].x = XF(w, x1);
    t->vertices[0].y = YF(h, y1);
    t->vertices[0].r = r;
    t->vertices[0].g = g;
    t->vertices[0].b = b;
    t->vertices[0].a = a;
    t->vertices[1].x = XF(w, x2);
    t->vertices[1].y = YF(h, y2);
    t->vertices[1].r = r;
    t->vertices[1].g = g;
    t->vertices[1].b = b;
    t->vertices[1].a = a;
    t->vertices[2].x = XF(w, x3);
    t->vertices[2].y = YF(h, y3);
    t->vertices[2].r = r;
    t->vertices[2].g = g;
    t->vertices[2].b = b;
    t->vertices[2].a = a;

    /* useful only for the rectangle later */
    t->indices[0] = 0;
    t->indices[1] = 1;
    t->indices[2] = 2;

    t->stride = sizeof(Vertex);
    t->offset = 0U;
    t->index_count = 3U;

    desc.ByteWidth = sizeof(t->vertices);
    desc.Usage = D3D11_USAGE_DYNAMIC;
    desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    desc.MiscFlags = 0U;
    desc.StructureByteStride = 0U;

    sr_data.pSysMem = t->vertices;
    sr_data.SysMemPitch = 0U;
    sr_data.SysMemSlicePitch = 0U;

    res = ID3D11Device_CreateBuffer(d3d->d3d_device,
                                    &desc,
                                    &sr_data,
                                    &t->vertex_buffer);
    if (FAILED(res))
    {
        free(t);
        return NULL;
    }

    desc.ByteWidth = sizeof(t->indices);
    desc.Usage = D3D11_USAGE_DYNAMIC;
    desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    desc.MiscFlags = 0U;
    desc.StructureByteStride = 0U;

    sr_data.pSysMem = t->indices;
    sr_data.SysMemPitch = 0U;
    sr_data.SysMemSlicePitch = 0U;

    res = ID3D11Device_CreateBuffer(d3d->d3d_device,
                                    &desc,
                                    &sr_data,
                                    &t->index_buffer);
    if (FAILED(res))
    {
        free(t);
        return NULL;
    }

    return t;
}

void triangle_free(Triangle *t)
{
    if (!t)
        return;

    ID3D11Buffer_Release(t->index_buffer);
    ID3D11Buffer_Release(t->vertex_buffer);
    free(t);
}

void d3d_render(D3d *d3d)
{
#ifdef HAVE_WIN10
    DXGI_PRESENT_PARAMETERS pp;
#endif
    const FLOAT color[4] = { 0.10f, 0.18f, 0.24f, 1.0f };
    RECT rect;
    HRESULT res;

    FCT;

    if (!GetClientRect(d3d->win->win, &rect))
    {
        return;
    }

    /* scene */
    Triangle *t;

    t = triangle_new(d3d,
                     rect.right - rect.left,
                     rect.bottom - rect.top,
                     320, 120,
                     480, 360,
                     160, 360,
                     255, 255, 0, 255); /* r, g, b, a */

    /* clear render target */
    ID3D11DeviceContext_ClearRenderTargetView(d3d->d3d_device_ctx,
                                              d3d->d3d_render_target_view,
                                              color);
    /* Input Assembler (IA) */
    /* TRIANGLESTRIP only useful for the rectangle later */
    ID3D11DeviceContext_IASetPrimitiveTopology(d3d->d3d_device_ctx,
                                               D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    ID3D11DeviceContext_IASetInputLayout(d3d->d3d_device_ctx,
                                         d3d->d3d_input_layout);
    ID3D11DeviceContext_IASetVertexBuffers(d3d->d3d_device_ctx,
                                           0,
                                           1,
                                           &t->vertex_buffer,
                                           &t->stride,
                                           &t->offset);
    ID3D11DeviceContext_IASetIndexBuffer(d3d->d3d_device_ctx,
                                         t->index_buffer,
                                         DXGI_FORMAT_R32_UINT,
                                         0);
    /* vertex shader */
    ID3D11DeviceContext_VSSetShader(d3d->d3d_device_ctx,
                                    d3d->d3d_vertex_shader,
                                    NULL,
                                    0);
    /* pixel shader */
    ID3D11DeviceContext_PSSetShader(d3d->d3d_device_ctx,
                                    d3d->d3d_pixel_shader,
                                    NULL,
                                    0);

    /* set viewport in the Rasterizer Stage */
    ID3D11DeviceContext_RSSetViewports(d3d->d3d_device_ctx, 1U, &d3d->viewport);

    /* Output merger */
    ID3D11DeviceContext_OMSetRenderTargets(d3d->d3d_device_ctx,
                                           1U, &d3d->d3d_render_target_view,
                                           NULL);

    /* draw */
    ID3D11DeviceContext_DrawIndexed(d3d->d3d_device_ctx,
                                    t->index_count,
                                    0, 0);

    triangle_free(t);

    /*
     * present frame, that is flip the back buffer and the front buffer
     * if no vsync, we present immediatly
     */
#ifdef HAVE_WIN10
    pp.DirtyRectsCount = 0;
    pp.pDirtyRects = NULL;
    pp.pScrollRect = NULL;
    pp.pScrollOffset = NULL;
    res = IDXGISwapChain1_Present1(d3d->dxgi_swapchain,
                                   d3d->vsync ? 1 : 0, 0, &pp);
#else
    res = IDXGISwapChain_Present(d3d->dxgi_swapchain,
                                 d3d->vsync ? 1 : 0, 0);
#endif
    if (res == DXGI_ERROR_DEVICE_RESET || res == DXGI_ERROR_DEVICE_REMOVED)
    {
        printf("device removed or lost, need to recreate everything\n");
        fflush(stdout);
    }
    else if (res == DXGI_STATUS_OCCLUDED)
    {
        printf("window is not visible, so vsync won't work. Let's sleep a bit to reduce CPU usage\n");
        fflush(stdout);
    }
}

int main()
{
    Window *win;
    D3d *d3d;

    /* remove scaling on HiDPI */
#ifdef  HAVE_WIN10
    SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE);
#endif

    win = window_new(100, 100, 800, 480);
    if (!win)
        return 1;

    d3d = d3d_init(win, 0);
    if (!d3d)
        goto del_window;

    SetWindowLongPtr(win->win, GWLP_USERDATA, (LONG_PTR)win);

    window_show(win);

    /* mesage loop */
    while (1)
    {
        MSG msg;
        BOOL ret;

        ret = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
        if (ret)
        {
            do
            {
                if (msg.message == WM_QUIT)
                    goto beach;
                TranslateMessage(&msg);
                DispatchMessageW(&msg);
            } while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
        }
    }

  beach:
    d3d_shutdown(d3d);
    window_del(win);

    return 0;

  del_window:
    window_del(win);
    printf(" error\n");
    fflush(stdout);

    return 1;
}

顶点着色器:

struct vs_input
{
    float2 position : POSITION;
    float4 color : COLOR;
};

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

ps_input main(vs_input input )
{
    ps_input output;
    output.position = float4(input.position, 0.0f, 1.0f);
    output.color = input.color;
    return output;
}

像素着色器:

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

float4 main(ps_input input) : SV_TARGET
{
    return input.color;
}

谢谢

如果你想为你的顶点使用像素坐标,你可以使用这两种格式之一:

DXGI_FORMAT_R32G32_FLOAT (same as you use right now, pixel in floating point)
DXGI_FORMAT_R32G32_UINT  (pixel coordinates as int, vertex shader position input becomes uint2 position : POSITION)

如果使用float,则float转换在C端完成,如果使用UINT,则转换在vertex shader端完成。速度差异需要分析,如果顶点数量很少,我希望它可以忽略不计。

然后您可以轻松地将这些值重新映射到顶点着色器中的 -1 到 1 范围内(这非常有效),您只需要在常量缓冲区中传递反向视口大小。

所以你的顶点着色器变成:

struct vs_input
{
    float2 position : POSITION;
    //uint2 position : POSITION; If you use UINT
    float4 color : COLOR;
};

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

cbuffer cbViewport : register(b0)
{
    float2 inverseViewportSize;
}

ps_input main(vs_input input )
{
    ps_input output;
    float2 p = input.position; //if you use UINT, conversion is done here
    p *= inverseViewportSize;
    p *= 2.0f;
    p -= 1.0f;
    p.y *= -1.0f; (clip space is bottom to top, pixel is top to bottom)
    output.position = float4(p, 0.0f, 1.0f);
    output.color = input.color;
    return output;
}