绘制到屏幕

重要的应用程序接口(API)

最后,我们移植将旋转立方体绘制到屏幕的代码。

在 OpenGL ES 2.0 中,绘图上下文定义为 EGLContext 类型,其中包含窗口和图面参数以及绘制到呈现目标所需的资源,这些资源将用于撰写显示到窗口的最终图像。 使用此上下文配置图形资源,以便将着色器管道的结果正确呈现在显示器上。 主要资源之一是“后台缓冲区”(或“帧缓冲区对象”),其中包含最终的复合呈现目标,可供显示。

在使用 Direct3D 时,配置图形资源以在显示器上绘图的过程更具指导性,并且需要更多的 API。 (不过,Microsoft Visual Studio Direct3D 模板可以显著简化此过程!若要获取上下文(称为 Direct3D 设备上下文),必须先获取 ID3D11Device1 对象,并使用它来创建和配置 ID3D11DeviceContext1 对象。 这两个对象结合用于配置将需要为屏幕绘制的特定资源。

简言之,DXGI API 主要包含用于管理与图形适配器直接相关的资源的 API,Direct3D 包含用于在 GPU 与 CPU 上运行的主程序之间的接口的 API。

为了进行此示例中的比较,下面是每个 API 的相关类型:

  • ID3D11Device1:提供图形设备及其资源的虚拟表示形式。
  • ID3D11DeviceContext1:提供用于配置缓冲区和发出呈现命令的接口。
  • IDXGISwapChain1:交换链类似于 OpenGL ES 2.0 中的后台缓冲区。 它是图形适配器上的内存区域,包含用于显示的最终渲染图像。 它被称为“交换链”,因为它有多个缓冲区可以写入,并且通过“交换”来将最新的渲染内容显示到屏幕上。
  • ID3D11RenderTargetView:它包含 Direct3D 设备上下文绘制到的 2D 位图缓冲区,并且该缓冲区由交换链呈现。 与 OpenGL ES 2.0 一样,可以有多个渲染目标,其中一些目标不绑定到交换链上,而是用于多重着色技术。

在模板中,呈现器对象包含以下字段:

Direct3D 11:设备和设备上下文声明

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;

下面介绍如何将后台缓冲区配置为渲染目标,并将其提供给交换链。

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

Direct3D 运行时会隐式地为 ID3D11Texture2D创建 IDXGISurface1,该表面将纹理作为交换链可用于显示的“后台缓冲区”呈现。

Direct3D 设备和设备上下文的初始化和配置以及呈现目标可以在 Direct3D 模板中的自定义 CreateDeviceResourcesCreateWindowSizeDependentResources 方法中找到。

有关 Direct3D 设备上下文与 EGL 和 EGLContext 类型的关系的详细信息,请阅读 将 EGL 代码移植到 DXGI 和 Direct3D

说明书

步骤 1:呈现场景并显示它

更新立方体数据(在本例中,通过让立方体围绕 y 轴稍微旋转),Render 方法将视口设置为绘图上下文(EGLContext)的维度。 此上下文包含颜色缓冲区,该缓冲区将通过已配置的显示器(EGLDisplay)显示到窗口表面(EGLSurface)。 此时,示例更新顶点数据属性,重新绑定索引缓冲区,绘制立方体,并将着色管线绘制的颜色缓冲区交换到显示表面。

OpenGL ES 2.0:显示帧的渲染

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);
}

在 Direct3D 11 中,此过程非常相似。 (假设你使用的是 Direct3D 模板中的视口和渲染目标配置。

Direct3D 11:渲染用于显示的帧

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);
}

调用 IDXGISwapChain1::Present1 后,帧将输出到配置的显示器。

上一步

移植 GLSL

注解

该示例忽略了配置设备资源过程中的许多复杂性,特别是在针对通用 Windows 平台 (UWP) 的 DirectX 应用程序时。 建议查看完整的模板代码,尤其是执行窗口和设备资源设置和管理的部分。 UWP 应用必须支持轮换事件以及暂停/恢复事件,模板演示了处理接口丢失或更改显示参数的最佳做法。