c++directxdirectx-11

Triangle not appearing in DirectX 11 application


I'm trying to render a simple triangle but the problem is everything works fine except the triangle doesn't render:

#include <Windows.h>
#include <d3d11.h>
#include <d3dcompiler.h>
#include <DirectXMath.h>
#include <string>

#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "d3dcompiler.lib")

using namespace DirectX;

// Vertex shader source code
const std::string vertexShaderSource = R"(
    float4 main(float4 pos : POSITION) : SV_Position
    {
        return pos;
    }
)";

// Pixel shader source code
const std::string pixelShaderSource = R"(
    float4 main() : SV_Target
    {
        return float4(0.0f, 1.0f, 0.7f, 1.0f);
    }
)";

#define CHECK_HR_ERROR(msg) if (FAILED(hr)) MessageBox(nullptr, msg, L"Error", MB_ICONERROR)

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }

    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow)
{
    // Create the window
    const wchar_t* windowClassName = L"DirectX11WindowClass";
    const wchar_t* windowTitle = L"DirectX 11 Application";
    const int windowWidth = 800;
    const int windowHeight = 600;

    WNDCLASSEX wcex = {};
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WindowProc;
    wcex.hInstance = hInstance;
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.lpszClassName = windowClassName;

    RegisterClassEx(&wcex);

    RECT rc = { 0, 0, windowWidth, windowHeight };
    AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, false);

    HWND hwnd = CreateWindowEx(
        0,
        windowClassName,
        windowTitle,
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        rc.right - rc.left, rc.bottom - rc.top,
        nullptr,
        nullptr,
        hInstance,
        nullptr
    );

    if (!hwnd)
        return -1;

    ShowWindow(hwnd, nCmdShow);

    // Initialize DirectX
    DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
    swapChainDesc.BufferCount = 1;
    swapChainDesc.BufferDesc.Width = windowWidth;
    swapChainDesc.BufferDesc.Height = windowHeight;
    swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    swapChainDesc.OutputWindow = hwnd;
    swapChainDesc.SampleDesc.Count = 1;
    swapChainDesc.SampleDesc.Quality = 0;
    swapChainDesc.Windowed = true;

    ID3D11Device* device = nullptr;
    ID3D11DeviceContext* deviceContext = nullptr;
    IDXGISwapChain* swapChain = nullptr;
    ID3D11RenderTargetView* renderTargetView = nullptr;

    HRESULT hr = D3D11CreateDeviceAndSwapChain(
        nullptr,
        D3D_DRIVER_TYPE_HARDWARE,
        nullptr,
        0,
        nullptr,
        0,
        D3D11_SDK_VERSION,
        &swapChainDesc,
        &swapChain,
        &device,
        nullptr,
        &deviceContext
    );
    CHECK_HR_ERROR(L"failed to create the device, deviceContext and swapChain!");

    // Create the render target view
    ID3D11Texture2D* backBuffer = nullptr;
    hr = swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<LPVOID*>(&backBuffer));
    CHECK_HR_ERROR(L"failed to get the back buffer!");

    hr = device->CreateRenderTargetView(backBuffer, nullptr, &renderTargetView);
    CHECK_HR_ERROR(L"failed to create the render target view!");
    backBuffer->Release();

    D3D11_VIEWPORT viewport = {};
    viewport.Width = static_cast<float>(windowWidth);
    viewport.Height = static_cast<float>(windowHeight);
    viewport.MinDepth = 0.0f;
    viewport.MaxDepth = 1.0f;
    viewport.TopLeftX = 0.0f;
    viewport.TopLeftY = 0.0f;

    deviceContext->RSSetViewports(1, &viewport);

    // Set the render target
    deviceContext->OMSetRenderTargets(1, &renderTargetView, nullptr);

    // Create the vertex buffer
    struct VertexType
    {
        XMFLOAT4 position;
    };

    VertexType vertices[] =
    {
        { XMFLOAT4(-0.5f, -0.5f, 0.0f, 1.0f) },
        { XMFLOAT4(0.5f, -0.5f, 0.0f, 1.0f) },
        { XMFLOAT4(0.0f, 0.5f, 0.0f, 1.0f) }
    };

    D3D11_BUFFER_DESC vertexBufferDesc = {};
    vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
    vertexBufferDesc.ByteWidth = sizeof(VertexType) * 3; // sizeof(vertices)
    vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    vertexBufferDesc.CPUAccessFlags = 0;

    D3D11_SUBRESOURCE_DATA vertexData = {};
    vertexData.pSysMem = vertices;

    ID3D11Buffer* vertexBuffer = nullptr;
    hr = device->CreateBuffer(&vertexBufferDesc, &vertexData, &vertexBuffer);
    CHECK_HR_ERROR(L"failed to create the vertex buffer!");

    // Set the vertex buffer
    UINT stride = sizeof(VertexType);
    UINT offset = 0;
    deviceContext->IASetVertexBuffers(0, 1, &vertexBuffer, &stride, &offset);

    // Set the primitive topology
    deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    // Create the vertex shader
    ID3DBlob* vertexShaderBlob = nullptr;
    D3DCompile(vertexShaderSource.c_str(), vertexShaderSource.length(), nullptr, nullptr, nullptr, "main", "vs_4_0", 0, 0, &vertexShaderBlob, nullptr);
    CHECK_HR_ERROR(L"failed to compile the vertex shader!");

    ID3D11VertexShader* vertexShader = nullptr;
    hr = device->CreateVertexShader(vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), nullptr, &vertexShader);
    CHECK_HR_ERROR(L"failed to create the vertex shader!");

    deviceContext->VSSetShader(vertexShader, nullptr, 0);

    D3D11_INPUT_ELEMENT_DESC inputLayoutDesc[] =
    {
        { "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }
    };

    ID3D11InputLayout* inputLayout = nullptr;
    hr = device->CreateInputLayout(inputLayoutDesc, _countof(inputLayoutDesc), vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &inputLayout);
    CHECK_HR_ERROR(L"failed to create the input layout!");

    deviceContext->IASetInputLayout(inputLayout);

    // Create the pixel shader
    ID3DBlob* pixelShaderBlob = nullptr;
    hr = D3DCompile(pixelShaderSource.c_str(), pixelShaderSource.length(), nullptr, nullptr, nullptr, "main", "ps_4_0", 0, 0, &pixelShaderBlob, nullptr);
    CHECK_HR_ERROR(L"failed to compile the pixel shader!");

    ID3D11PixelShader* pixelShader = nullptr;
    hr = device->CreatePixelShader(pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize(), nullptr, &pixelShader);
    CHECK_HR_ERROR(L"failed to create the pixel shader!");

    deviceContext->PSSetShader(pixelShader, nullptr, 0);

    // Main message loop
    MSG msg = {};
    while (msg.message != WM_QUIT)
    {
        if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else
        {
            // Render the frame
            float clearColor[] = { 0.0f, 0.0f, 1.0f, 1.0f };
            deviceContext->ClearRenderTargetView(renderTargetView, clearColor);
            deviceContext->Draw(3, 0);
            swapChain->Present(1, 0);
        }
    }

    // Cleanup
    vertexBuffer->Release();
    vertexShaderBlob->Release();
    vertexShader->Release();
    pixelShaderBlob->Release();
    pixelShader->Release();
    renderTargetView->Release();
    swapChain->Release();
    deviceContext->Release();
    device->Release();

    return static_cast<int>(msg.wParam);
}

Solution

  • Your triangle is back-facing and you don't tell DirectX what are the triangle's vertices indices so it's not drawn (with default configuration you use). With the triangle as it is, here is how you can fix it:

    Add this in your initialization code:

    UINT indices[] =
    {
      2,1,0 // order is important!
    };
    
    D3D11_BUFFER_DESC indexBufferDesc = {};
    indexBufferDesc.ByteWidth = sizeof(indices);
    indexBufferDesc.Usage = D3D11_USAGE_IMMUTABLE;
    indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
    
    D3D11_SUBRESOURCE_DATA indexData = { indices };
    ID3D11Buffer* indexBuffer;
    hr = device->CreateBuffer(&indexBufferDesc, &indexData, &indexBuffer);
    CHECK_HR_ERROR(L"failed to create the index buffer!");
    
    deviceContext->IASetIndexBuffer(indexBuffer, DXGI_FORMAT_R32_UINT, 0);
    

    Replace your Draw call by this one:

    deviceContext->DrawIndexed(ARRAYSIZE(indices), 0, 0);
    

    Another solution here is to set a rasterizer state with D3D11_CULL_FRONT (or D3D11_CULL_NONE here), like this, to ask he system to draw back-facing (or all) triangles:

    D3D11_RASTERIZER_DESC rasterizerDesc = {};
    rasterizerDesc.FillMode = D3D11_FILL_SOLID;
    rasterizerDesc.CullMode = D3D11_CULL_FRONT;
    ID3D11RasterizerState* rasterizerState;
    hr = device->CreateRasterizerState(&rasterizerDesc, &rasterizerState);
    CHECK_HR_ERROR(L"failed to CreateRasterizerState!");
    
    deviceContext->RSSetState(rasterizerState);
    

    But you can keep all your code and draw call as is just inverting the triangle vertices (0,1,2 => 2,1,0), like this:

    VertexType vertices[] =
    {
        { XMFLOAT4(0.0f, 0.5f, 0.0f, 1.0f) },
        { XMFLOAT4(0.5f, -0.5f, 0.0f, 1.0f) },
        { XMFLOAT4(-0.5f, -0.5f, 0.0f, 1.0f) },
    };