Disegnare sullo schermo

API importanti

È arrivato il momento di portare il codice che disegna il cubo rotante sullo schermo.

In OpenGL ES 2.0, il contesto di disegno è definito come tipo EGLContext contenente i parametri finestra e di superficie e le risorse necessarie per disegnare nelle destinazioni di rendering, le quali verranno usate per comporre l'immagine finale visualizzata nella finestra. Tale contesto consente di configurare le risorse grafiche per visualizzare correttamente i risultati della pipeline dello shader sullo schermo. Una delle principali risorse è il "back buffer" (anche detto "oggetto buffer frame") che contiene le destinazioni di rendering composte finali, pronte per essere presentate alla visualizzazione.

Con Direct3D, il processo di configurazione delle risorse grafiche per il disegno sullo schermo è più didattico e richiede molte altre API. Un modello Direct3D di Microsoft Visual Studio può però semplificare molto questo processo. Per ottenere un contesto (chiamato anche contesto di dispositivo Direct3D), devi prima ottenere un oggetto ID3D11Device1 e usarlo per creare e configurare un oggetto ID3D11DeviceContext1. Questi due oggetti vengono usati insieme per configurare le risorse specifiche necessarie per il disegno nella visualizzazione.

In breve, le API DXGI contengono principalmente API per la gestione delle risorse direttamente relative alla scheda grafica, mentre Direct3D contiene le API che consentono di interfacciarsi tra la GPU e il programma principale in esecuzione sulla CPU.

Ai fini del confronto in questo esempio, ecco i tipi pertinenti di ogni API:

  • ID3D11Device1: fornisce una rappresentazione virtuale del dispositivo grafico e delle relative risorse.
  • ID3D11DeviceContext1: fornisce l'interfaccia per configurare i buffer ed eseguire i comandi di rendering.
  • IDXGISwapChain1: la swapchain è analoga al buffer nascosto in OpenGL ES 2.0. Si tratta dell'area di memoria nella scheda grafica che contiene le immagini di cui è stato eseguito il rendering finale per la visualizzazione. Viene chiamata "swapchain", cioè catena di scambio, perché ha diversi buffer che possono essere scritti e "scambiati" per presentare il rendering più recente sullo schermo.
  • ID3D11RenderTargetView: contiene il buffer bitmap 2D in cui viene disegnato il contesto del dispositivo Direct3D e che viene presentato dalla swapchain. Come per OpenGL ES 2.0, è possibile avere più destinazioni di rendering, alcune delle quali non associate alla swapchain, ma usate per tecniche di ombreggiatura a più passaggi.

Nel modello, l'oggetto renderer contiene i campi seguenti:

Direct3D 11: Dichiarazioni del dispositivo e del contesto di dispositivo

Platform::Agile<Windows::UI::Core::CoreWindow>       m_window;

Microsoft::WRL::ComPtr<ID3D11Device1>                m_d3dDevice;
Microsoft::WRL::ComPtr<ID3D11DeviceContext1>          m_d3dContext;
Microsoft::WRL::ComPtr<IDXGISwapChain1>                      m_swapChainCoreWindow;
Microsoft::WRL::ComPtr<ID3D11RenderTargetView>          m_d3dRenderTargetViewWin;

Ecco come il buffer nascosto viene configurato come destinazione di rendering e fornito alla swapchain.

ComPtr<ID3D11Texture2D> backBuffer;
m_swapChainCoreWindow->GetBuffer(0, IID_PPV_ARGS(backBuffer));
m_d3dDevice->CreateRenderTargetView(
  backBuffer.Get(),
  nullptr,
  &m_d3dRenderTargetViewWin);

Il runtime Direct3D crea in modo implicito un IDXGISurface1 per ID3D11Texture2D, che rappresenta la texture come "buffer nascosto" utilizzabile dalla swapchain per la visualizzazione.

L'inizializzazione e la configurazione del dispositivo Direct3D e del contesto di dispositivo, oltre alle destinazioni di rendering, sono disponibili nei metodi CreateDeviceResources e CreateWindowSizeDependentResources nel modello Direct3D.

Per maggiori informazioni sul contesto di dispositivo Direct3D in relazione a EGL e al tipo EGLContext, leggere Convertire il codice EGL in DXGI e Direct3D.

Istruzioni

Passaggio 1: Rendering della scena e visualizzazione

Dopo avere aggiornato i dati del cubo (in questo caso ruotandolo leggermente intorno all'asse y), il metodo Render imposta il riquadro di visualizzazione sulle dimensioni del contesto di disegno (un oggetto EGLContext). Tale contesto contiene il buffer dei colori che verrà visualizzato sulla superficie della finestra (EGLSurface), usando la visualizzazione configurata (EGLDisplay). In questo momento, l'esempio aggiorna gli attributi dei dati dei vertici, associa nuovamente il buffer degli indici, disegna il cubo e scambia il buffer colori disegnato dalla pipeline di ombreggiatura alla superficie di visualizzazione.

OpenGL ES 2.0: rendering di un frame per la visualizzazione

void Render(GraphicsContext *drawContext)
{
  Renderer *renderer = drawContext->renderer;

  int loc;
   
  // Set the viewport
  glViewport ( 0, 0, drawContext->width, drawContext->height );
   
   
  // Clear the color buffer
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glEnable(GL_DEPTH_TEST);


  // Use the program object
  glUseProgram (renderer->programObject);

  // Load the a_position attribute with the vertex position portion of a vertex buffer element
  loc = glGetAttribLocation(renderer->programObject, "a_position");
  glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, 
      sizeof(Vertex), 0);
  glEnableVertexAttribArray(loc);

  // Load the a_color attribute with the color position portion of a vertex buffer element
  loc = glGetAttribLocation(renderer->programObject, "a_color");
  glVertexAttribPointer(loc, 4, GL_FLOAT, GL_FALSE, 
      sizeof(Vertex), (GLvoid*) (sizeof(float) * 3));
  glEnableVertexAttribArray(loc);

  // Bind the index buffer
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderer->indexBuffer);

  // Load the MVP matrix
  glUniformMatrix4fv(renderer->mvpLoc, 1, GL_FALSE, (GLfloat*) &renderer->mvpMatrix.m[0][0]);

  // Draw the cube
  glDrawElements(GL_TRIANGLES, renderer->numIndices, GL_UNSIGNED_INT, 0);

  eglSwapBuffers(drawContext->eglDisplay, drawContext->eglSurface);
}

In Direct3D 11 il processo è molto simile. Si presuppone che si stia usando il riquadro di visualizzazione e la configurazione della destinazione di rendering dal modello Direct3D.

Direct3D 11: rendering di un frame per la visualizzazione

void RenderObject::Render()
{
  // ...

  // Only update shader resources that have changed since the last frame.
  m_d3dContext->UpdateSubresource(
    m_constantBuffer.Get(),
    0,
    NULL,
    &m_constantBufferData,
    0,
    0);

  // Set up the IA stage corresponding to the current draw operation.
  UINT stride = sizeof(VertexPositionColor);
  UINT offset = 0;
  m_d3dContext->IASetVertexBuffers(
    0,
    1,
    m_vertexBuffer.GetAddressOf(),
    &stride,
    &offset);

  m_d3dContext->IASetIndexBuffer(
    m_indexBuffer.Get(),
    DXGI_FORMAT_R16_UINT,
    0);

  m_d3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
  m_d3dContext->IASetInputLayout(m_inputLayout.Get());

  // Set up the vertex shader corresponding to the current draw operation.
  m_d3dContext->VSSetShader(
    m_vertexShader.Get(),
    nullptr,
    0);

  m_d3dContext->VSSetConstantBuffers(
    0,
    1,
    m_constantBuffer.GetAddressOf());

  // Set up the pixel shader corresponding to the current draw operation.
  m_d3dContext->PSSetShader(
    m_pixelShader.Get(),
    nullptr,
    0);

  m_d3dContext->DrawIndexed(
    m_indexCount,
    0,
    0);

    // ...

  m_swapChainCoreWindow->Present1(1, 0, &parameters);
}

Dopo avere chiamato IDXGISwapChain1::Present1, il fotogramma viene restituito alla visualizzazione configurata.

Passaggio precedente

Convertire il codice GLSL

Osservazioni:

Questo esempio illustra gran parte della complessità che si estende alla configurazione delle risorse del dispositivo, in particolare per le app DirectX della piattaforma UWP (Universal Windows Platform). È consigliabile esaminare il codice completo del modello, in particolare le parti che eseguono la configurazione e la gestione delle risorse della finestra e del dispositivo. Le app UWP devono supportare gli eventi di rotazione e di sospensione/ripresa e il modello illustra le procedure consigliate per gestire la perdita di un'interfaccia o una modifica nei parametri di visualizzazione.