🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Draw a rectangle with DX11

Started by
13 comments, last by Aerodactyl55 1 year, 10 months ago

One way to use 2D coord is to create an orthographic projection matrix and use it to transform the vertices. Research the XMMatrixOrthographicOffCenter[LH/RH] function (or a similar function in other math libraries).

Advertisement

Aerodactyl55 said:

One way to use 2D coord is to create an orthographic projection matrix and use it to transform the vertices. Research the XMMatrixOrthographicOffCenter[LH/RH] function (or a similar function in other math libraries).

Thats not true, but a projection camera does add distortion to the image so you get the sense of depth in the view. If you screen align your objects in a projection camera the object will seem like they are 2D too, but you still have depth effects on them.

If you purely want the outline of the rectangle you should use a line list with 8 vertices which are the corners of your rectangle.

Worked on titles: CMR:DiRT2, DiRT 3, DiRT: Showdown, GRID 2, theHunter, theHunter: Primal, Mad Max, Watch Dogs: Legion

@NightCreature83 Thank you I have already a conversion function from pixel to NDC:

float PixelToNDC(int Pixel, int Max)
{
   float F_Pixel = Pixel;
   float F_Max = Max;

   return (F_Pixel / F_Max * 2) - 1;
}

and seem work. Now my demo project is a good point, I like to known the best way to clear temporarily the drawing object and recreate it in the second time.

Here my updated project:

void ClearObjects()
{
   OurVertices.clear();

   float color[4] = { 0.0f, 0.0f, 0.0f, 0.0f };

   // clear the back buffer
   devcon->ClearRenderTargetView(backbuffer, color);
}


void DrawAll()
{
   static const XMVECTORF32 s_MyValue = { 1.f, 2.f, 3.f, 4.f };

   DrawRectangle(2, 2, 798, 598, s_MyValue);

   DrawLine(20, 300, 600, 300, s_MyValue);
}


void DrawAll2()
{
   static const XMVECTORF32 s_MyValue = { 1.f, 2.f, 3.f, 4.f };

   DrawRectangle(2, 2, 398, 398, s_MyValue);

   DrawLine(20, 300, 600, 300, s_MyValue);
}



// this function initializes and prepares Direct3D for use
void InitD3D(HWND hWnd)
{
   // create a struct to hold information about the swap chain
   DXGI_SWAP_CHAIN_DESC scd;

   // clear out the struct for use
   ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC));

   // fill the swap chain description struct
   scd.BufferCount = 1;                                   // one back buffer
   scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;    // use 32-bit color
   scd.BufferDesc.Width = SCREEN_WIDTH;                   // set the back buffer width
   scd.BufferDesc.Height = SCREEN_HEIGHT;                 // set the back buffer height
   scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;     // how swap chain is to be used
   scd.OutputWindow = hWnd;                               // the window to be used
   scd.SampleDesc.Count = 4;                              // how many multisamples
   scd.Windowed = TRUE;                                   // windowed/full-screen mode
   scd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;    // allow full-screen switching

   // create a device, device context and swap chain using the information in the scd struct
   D3D11CreateDeviceAndSwapChain(NULL,
       D3D_DRIVER_TYPE_HARDWARE,
       NULL,
       NULL,
       NULL,
       NULL,
       D3D11_SDK_VERSION,
       &scd,
       &swapchain,
       &dev,
       NULL,
       &devcon);


   // get the address of the back buffer
   ID3D11Texture2D* pBackBuffer;
   swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);

   // use the back buffer address to create the render target
   dev->CreateRenderTargetView(pBackBuffer, NULL, &backbuffer);
   pBackBuffer->Release();

   // set the render target as the back buffer
   devcon->OMSetRenderTargets(1, &backbuffer, NULL);


   // Set the viewport
   D3D11_VIEWPORT viewport;
   ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT));

   viewport.TopLeftX = 0;
   viewport.TopLeftY = 0;
   viewport.Width = SCREEN_WIDTH;
   viewport.Height = SCREEN_HEIGHT;

   devcon->RSSetViewports(1, &viewport);

   InitPipeline();

   DrawAll();

   UpdateDeviceContext();

}






// this is the function used to render a single frame
void RenderFrame(void)
{
   if (OurVertices.size() < 1)
       return;

   float color[4] = { 0.0f, 0.0f, 0.0f, 0.0f };

   // clear the back buffer
   devcon->ClearRenderTargetView(backbuffer, color);

   // select which vertex buffer to display
   UINT stride = sizeof(VERTEX);
   UINT offset = 0;

    devcon->IASetVertexBuffers(0, 1, &pVBuffer, &stride, &offset);

   // select which primtive type we are using
 //   draw the vertex buffer to the back buffer
   devcon->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINELIST);
   devcon->Draw(OurVertices.size(), 0);
   swapchain->Present(0, 0);

}


// this is the function that cleans up Direct3D and COM
void CleanD3D(void)
{
   swapchain->SetFullscreenState(FALSE, NULL);    // switch to windowed mode

   // close and release all existing COM objects
   pLayout->Release();
   pVS->Release();
   pPS->Release();
   pVBuffer->Release();
   swapchain->Release();
   backbuffer->Release();
   dev->Release();
   devcon->Release();
}


float PixelToNDC(int Pixel, int Max)
{
   float F_Pixel = Pixel;
   float F_Max = Max;

   return (F_Pixel / F_Max * 2) - 1;
}

void UpdateVertexVect(float x, float y, XMVECTORF32 col)
{
   VERTEX CurVertex;
   CurVertex.pos.x = x;
   CurVertex.pos.y = y;
   CurVertex.col = col;
   OurVertices.push_back(CurVertex);
}

void UpdateDeviceContext()
{
   rasterDesc.AntialiasedLineEnable = false;
   rasterDesc.CullMode = D3D11_CULL_BACK;
   rasterDesc.DepthBias = 0;
   rasterDesc.DepthBiasClamp = 0.0f;
   rasterDesc.DepthClipEnable = false;
   rasterDesc.FillMode = D3D11_FILL_WIREFRAME;
   rasterDesc.FrontCounterClockwise = true;
   rasterDesc.MultisampleEnable = false;
   rasterDesc.ScissorEnable = false;
   rasterDesc.SlopeScaledDepthBias = 0.0f;

   HRESULT result = dev->CreateRasterizerState(&rasterDesc, &m_rasterState);
   if (FAILED(result)) return;
   devcon->RSSetState(m_rasterState);

   D3D11_BUFFER_DESC bd;
   ZeroMemory(&bd, sizeof(bd));
   bd.Usage = D3D11_USAGE_DYNAMIC;
   bd.ByteWidth = OurVertices.size() * sizeof(VERTEX);
   bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
   bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
   HRESULT val = dev->CreateBuffer(&bd, NULL, &pVBuffer);                   // create the buffer
   D3D11_MAPPED_SUBRESOURCE ms;

   devcon->Map(pVBuffer, NULL, D3D11_MAP_WRITE_DISCARD, NULL, &ms);   // map the buffer
   memcpy(ms.pData, OurVertices.data(), OurVertices.size() * sizeof(VERTEX));  // copy the data
   devcon->Unmap(pVBuffer, NULL);
}



void DrawRectangle(int x_Pix, int y_Pix, int width_Pix, int height_Pix, XMVECTORF32 col)
{
   float x, y, width, height;
   x = PixelToNDC(x_Pix, SCREEN_WIDTH);
   y = PixelToNDC(y_Pix, SCREEN_HEIGHT);
   width = PixelToNDC(width_Pix, SCREEN_WIDTH);
   height = PixelToNDC(height_Pix, SCREEN_HEIGHT);

   // Update Vector Coordinate compatible with "D3D11_PRIMITIVE_TOPOLOGY_LINELIST" topology  
   UpdateVertexVect(x, y, col);
   UpdateVertexVect(x, height, col);
   UpdateVertexVect(x, y, col);
   UpdateVertexVect(width, y, col);
   UpdateVertexVect(x, height, col);
   UpdateVertexVect(width, height, col);
   UpdateVertexVect(width, y, col);
   UpdateVertexVect(width, height, col);

}

void DrawLine(int x1_Pix, int y1_Pix, int x2_Pix, int y2_Pix, XMVECTORF32 col)
{
   float x1, y1, x2, y2;
   x1 = PixelToNDC(x1_Pix, SCREEN_WIDTH);
   y1 = PixelToNDC(y1_Pix, SCREEN_HEIGHT);
   x2 = PixelToNDC(x2_Pix, SCREEN_WIDTH);
   y2 = PixelToNDC(y2_Pix, SCREEN_HEIGHT);

   // Update Vector Coordinate compatible with "D3D11_PRIMITIVE_TOPOLOGY_LINELIST" topology  
   UpdateVertexVect(x1,- y1, col);
   UpdateVertexVect(x2,- y2, col);
}



// this function loads and prepares the shaders
void InitPipeline()
{
   ID3DBlob *pErrors;

   // load and compile the two shaders
   ID3D10Blob* VS, * PS;
   D3DCompileFromFile(L"shaders.shader", 0, 0, "VShader", "vs_4_0", 0, 0, &VS, &pErrors); // , 0, 0);
   D3DCompileFromFile(L"shaders.shader", 0, 0, "PShader", "ps_4_0", 0, 0, &PS, &pErrors); // , 0, 0);

   // encapsulate both shaders into shader objects
   dev->CreateVertexShader(VS->GetBufferPointer(), VS->GetBufferSize(), NULL, &pVS);
   dev->CreatePixelShader(PS->GetBufferPointer(), PS->GetBufferSize(), NULL, &pPS);

  // set the shader objects
   devcon->VSSetShader(pVS, 0, 0);
   devcon->PSSetShader(pPS, 0, 0);

   // create the input layout object
   D3D11_INPUT_ELEMENT_DESC ied[] =
   {
       {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
       {"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0},
   };

   dev->CreateInputLayout(ied, 2, VS->GetBufferPointer(), VS->GetBufferSize(), &pLayout);
  devcon->IASetInputLayout(pLayout);

}


// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
   HWND hWnd;
   WNDCLASSEX wc;

   ZeroMemory(&wc, sizeof(WNDCLASSEX));

   wc.cbSize = sizeof(WNDCLASSEX);
   wc.style = CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc = WindowProc;
   wc.hInstance = hInstance;
   wc.hCursor = LoadCursor(NULL, IDC_ARROW);
   wc.lpszClassName = L"WindowClass";

   RegisterClassEx(&wc);

   RECT wr = { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT };
   AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);


   hWnd = CreateWindowEx(WS_EX_LAYERED, L"WindowClass", L"Our First Direct3D Program", WS_POPUP, 300, 300, wr.right - wr.left, wr.bottom - wr.top, NULL, NULL, hInstance, NULL);
   SetLayeredWindowAttributes(hWnd, RGB(0, 0, 0), RGB(255, 255, 255), LWA_COLORKEY);

   ShowWindow(hWnd, nCmdShow);

   // set up and initialize Direct3D
   InitD3D(hWnd);

   // enter the main loop:

   MSG msg;


   while (TRUE)
   {
       if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
       {
           TranslateMessage(&msg);
           DispatchMessage(&msg);

           if (msg.message == WM_QUIT)
               break;
       }

       if (GetKeyState('A') & 0x8000/*Check if high-order bit is set (1 << 15)*/)
       {
           ClearObjects();
       }


       if (GetKeyState('B') & 0x8000/*Check if high-order bit is set (1 << 15)*/)
       {
           DrawAll2();
       }


       RenderFrame();
   }

   // clean up DirectX and COM
   CleanD3D();

   return msg.wParam;
}



I have created a function “ClearObjects” that in theory must delete all drawing but don't do nothing probrably I need to add some code,

Theorically I can call: “CleanD3D” but in this case I need to restart the entere project I like only clear the rectangles and lines and call for example “DrawAll” to redraw it again.

What you suggest ?

NightCreature83 said:

Aerodactyl55 said:

One way to use 2D coord is to create an orthographic projection matrix and use it to transform the vertices. Research the XMMatrixOrthographicOffCenter[LH/RH] function (or a similar function in other math libraries).

Thats not true, but a projection camera does add distortion to the image so you get the sense of depth in the view. If you screen align your objects in a projection camera the object will seem like they are 2D too, but you still have depth effects on them.

If you purely want the outline of the rectangle you should use a line list with 8 vertices which are the corners of your rectangle.

I don't understand what you mean here, and I am not sure if what you are saying is related to what I posted. Using a (properly setup) orthographic projection matrix allows you to specify the coords of the vertices in pixels just as the OP requested, or you can convert to NDC directly like OP did (which is a method that I already knew but did not suggest because I thought is was easier to just use an orthographic projection matrix).

This topic is closed to new replies.

Advertisement