Configuración de recursos de DirectX y visualización de una imagen
Aquí se muestra cómo crear un dispositivo Direct3D, una cadena de intercambio y una vista de destino de representación y cómo presentar la imagen representada a la pantalla.
Objetivo: Para configurar recursos de DirectX en una aplicación de Plataforma universal de Windows de C++ (UWP) y mostrar un color sólido.
Requisitos previos
Se supone que está familiarizado con C++. También necesita experiencia básica con los conceptos de programación de gráficos.
Tiempo de finalización: 20 minutos.
Instrucciones
1. Declarar variables de interfaz de Direct3D con ComPtr
Declaramos variables de interfaz de Direct3D con la plantilla de puntero inteligente ComPtr de la biblioteca de plantillas de C++ de Windows Runtime (WRL), por lo que podemos administrar la duración de esas variables de forma segura. A continuación, podemos usar esas variables para acceder a la clase ComPtr y a sus miembros. Por ejemplo:
ComPtr<ID3D11RenderTargetView> m_renderTargetView;
m_d3dDeviceContext->OMSetRenderTargets(
1,
m_renderTargetView.GetAddressOf(),
nullptr // Use no depth stencil.
);
Si declara ID3D11RenderTargetView con ComPtr, puede usar el método GetAddressOf de ComPtr para obtener la dirección del puntero a ID3D11RenderTargetView (**ID3D11RenderTargetView) para pasar a ID3D11DeviceContext::OMSetRenderTargets. OMSetRenderTargets enlaza el destino de representación a la fase de fusión de salida para especificar el destino de representación como destino de salida.
Una vez iniciada la aplicación de ejemplo, se inicializa y se carga y luego está listo para ejecutarse.
2. Creación del dispositivo Direct3D
Para usar la API de Direct3D para representar una escena, primero debemos crear un dispositivo Direct3D que represente el adaptador de pantalla. Para crear el dispositivo Direct3D, llamamos a la función D3D11CreateDevice. Especificamos los niveles 9.1 a 11.1 en la matriz de valores de D3D_FEATURE_LEVEL . Direct3D recorre la matriz en orden y devuelve el nivel de característica más alto admitido. Por lo tanto, para obtener el nivel de característica más alto disponible, enumeramos las entradas de matriz D3D_FEATURE_LEVEL de mayor a menor. Pasamos la marca D3D11_CREATE_DEVICE_BGRA_SUPPORT al parámetro Flags para que los recursos de Direct3D interoperan con Direct2D. Si usamos la compilación de depuración, también pasamos la marca D3D11_CREATE_DEVICE_DEBUG. Para obtener más información sobre la depuración de aplicaciones, consulte Uso de la capa de depuración para depurar aplicaciones.
Obtenemos el dispositivo Direct3D 11.1 (ID3D11Device1) y el contexto del dispositivo (ID3D11DeviceContext1) consultando el dispositivo Direct3D 11 y el contexto del dispositivo que se devuelven de D3D11CreateDevice.
// First, create the Direct3D device.
// This flag is required in order to enable compatibility with Direct2D.
UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
#if defined(_DEBUG)
// If the project is in a debug build, enable debugging via SDK Layers with this flag.
creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
// This array defines the ordering of feature levels that D3D should attempt to create.
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_1
};
ComPtr<ID3D11Device> d3dDevice;
ComPtr<ID3D11DeviceContext> d3dDeviceContext;
DX::ThrowIfFailed(
D3D11CreateDevice(
nullptr, // Specify nullptr to use the default adapter.
D3D_DRIVER_TYPE_HARDWARE,
nullptr, // leave as nullptr if hardware is used
creationFlags, // optionally set debug and Direct2D compatibility flags
featureLevels,
ARRAYSIZE(featureLevels),
D3D11_SDK_VERSION, // always set this to D3D11_SDK_VERSION
&d3dDevice,
nullptr,
&d3dDeviceContext
)
);
// Retrieve the Direct3D 11.1 interfaces.
DX::ThrowIfFailed(
d3dDevice.As(&m_d3dDevice)
);
DX::ThrowIfFailed(
d3dDeviceContext.As(&m_d3dDeviceContext)
);
3. Creación de la cadena de intercambio
A continuación, creamos una cadena de intercambio que el dispositivo usa para representar y mostrar. Declaramos e inicializamos una estructura de DXGI_SWAP_CHAIN_DESC1 para describir la cadena de intercambio. A continuación, configuramos la cadena de intercambio como modelo invertida (es decir, una cadena de intercambio que tiene el valor DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL establecido en el miembro SwapEffect) y establece el miembro Format en DXGI_FORMAT_B8G8R8A8_UNORM. Establecemos el miembro Count de la estructura DXGI_SAMPLE_DESC que el miembro SampleDesc especifica en 1 y el miembro Quality de DXGI_SAMPLE_DESC en cero porque el modelo volteo no admite varios suavizados de muestra (MSAA). Establecemos el miembro BufferCount en 2 para que la cadena de intercambio pueda usar un búfer frontal para presentar al dispositivo de visualización y un búfer de reserva que actúa como destino de representación.
Obtenemos el dispositivo DXGI subyacente consultando el dispositivo Direct3D 11.1. Para minimizar el consumo de energía, que es importante hacer en dispositivos con batería, como portátiles y tabletas, llamamos al método IDXGIDevice1::SetMaximumFrameLatency con 1 como el número máximo de fotogramas de búfer de retroceso que DXGI puede poner en cola. Esto garantiza que la aplicación se represente solo después del espacio en blanco vertical.
Para crear finalmente la cadena de intercambio, es necesario obtener el generador primario del dispositivo DXGI. Llamamos a IDXGIDevice::GetAdapter para obtener el adaptador del dispositivo y, a continuación, llamamos a IDXGIObject::GetParent en el adaptador para obtener la factoría primaria (IDXGIFactory2). Para crear la cadena de intercambio, llamamos a IDXGIFactory2::CreateSwapChainForCoreWindow con el descriptor de cadena de intercambio y la ventana principal de la aplicación.
// If the swap chain does not exist, create it.
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
swapChainDesc.Stereo = false;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.Scaling = DXGI_SCALING_NONE;
swapChainDesc.Flags = 0;
// Use automatic sizing.
swapChainDesc.Width = 0;
swapChainDesc.Height = 0;
// This is the most common swap chain format.
swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
// Don't use multi-sampling.
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
// Use two buffers to enable the flip effect.
swapChainDesc.BufferCount = 2;
// We recommend using this swap effect for all applications.
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
// Once the swap chain description is configured, it must be
// created on the same adapter as the existing D3D Device.
// First, retrieve the underlying DXGI Device from the D3D Device.
ComPtr<IDXGIDevice2> dxgiDevice;
DX::ThrowIfFailed(
m_d3dDevice.As(&dxgiDevice)
);
// Ensure that DXGI does not queue more than one frame at a time. This both reduces
// latency and ensures that the application will only render after each VSync, minimizing
// power consumption.
DX::ThrowIfFailed(
dxgiDevice->SetMaximumFrameLatency(1)
);
// Next, get the parent factory from the DXGI Device.
ComPtr<IDXGIAdapter> dxgiAdapter;
DX::ThrowIfFailed(
dxgiDevice->GetAdapter(&dxgiAdapter)
);
ComPtr<IDXGIFactory2> dxgiFactory;
DX::ThrowIfFailed(
dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory))
);
// Finally, create the swap chain.
CoreWindow^ window = m_window.Get();
DX::ThrowIfFailed(
dxgiFactory->CreateSwapChainForCoreWindow(
m_d3dDevice.Get(),
reinterpret_cast<IUnknown*>(window),
&swapChainDesc,
nullptr, // Allow on all displays.
&m_swapChain
)
);
4. Creación de la vista de destino de representación
Para representar gráficos en la ventana, es necesario crear una vista de destino de representación. Llamamos a IDXGISwapChain::GetBuffer para obtener el búfer de reserva de la cadena de intercambio que se usará al crear la vista de destino de representación. Especificamos el búfer de reserva como una textura 2D (ID3D11Texture2D). Para crear la vista de destino de representación, llamamos a ID3D11Device::CreateRenderTargetView con el búfer de reserva de la cadena de intercambio. Debe especificarse para dibujar en toda la ventana principal especificando el puerto de vista (D3D11_VIEWPORT) como el tamaño completo del búfer de reserva de la cadena de intercambio. Usamos el puerto de vista en una llamada a ID3D11DeviceContext::RSSetViewports para enlazar el puerto de vista a la fase de rasterizador de la canalización. La fase de rasterizador convierte la información de vector en una imagen ráster. En este caso, no se requiere una conversión porque solo se muestra un color sólido.
// Once the swap chain is created, create a render target view. This will
// allow Direct3D to render graphics to the window.
ComPtr<ID3D11Texture2D> backBuffer;
DX::ThrowIfFailed(
m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer))
);
DX::ThrowIfFailed(
m_d3dDevice->CreateRenderTargetView(
backBuffer.Get(),
nullptr,
&m_renderTargetView
)
);
// After the render target view is created, specify that the viewport,
// which describes what portion of the window to draw to, should cover
// the entire window.
D3D11_TEXTURE2D_DESC backBufferDesc = {0};
backBuffer->GetDesc(&backBufferDesc);
D3D11_VIEWPORT viewport;
viewport.TopLeftX = 0.0f;
viewport.TopLeftY = 0.0f;
viewport.Width = static_cast<float>(backBufferDesc.Width);
viewport.Height = static_cast<float>(backBufferDesc.Height);
viewport.MinDepth = D3D11_MIN_DEPTH;
viewport.MaxDepth = D3D11_MAX_DEPTH;
m_d3dDeviceContext->RSSetViewports(1, &viewport);
5. Presentación de la imagen representada
Entramos en un bucle sin fin para representar y mostrar continuamente la escena.
En este bucle, llamamos a:
- ID3D11DeviceContext::OMSetRenderTargets para especificar el destino de representación como destino de salida.
- ID3D11DeviceContext::ClearRenderTargetView para borrar el destino de representación en un color sólido.
- IDXGISwapChain::P resent para presentar la imagen representada en la ventana.
Dado que anteriormente establecemos la latencia máxima de fotogramas en 1, Windows normalmente ralentiza el bucle de representación a la frecuencia de actualización de pantalla, normalmente a unos 60 Hz. Windows ralentiza el bucle de representación haciendo que la aplicación se suspenda cuando la aplicación llama a Present. Windows hace que la aplicación se suspenda hasta que se actualice la pantalla.
// Enter the render loop. Note that UWP apps should never exit.
while (true)
{
// Process events incoming to the window.
m_window->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);
// Specify the render target we created as the output target.
m_d3dDeviceContext->OMSetRenderTargets(
1,
m_renderTargetView.GetAddressOf(),
nullptr // Use no depth stencil.
);
// Clear the render target to a solid color.
const float clearColor[4] = { 0.071f, 0.04f, 0.561f, 1.0f };
m_d3dDeviceContext->ClearRenderTargetView(
m_renderTargetView.Get(),
clearColor
);
// Present the rendered image to the window. Because the maximum frame latency is set to 1,
// the render loop will generally be throttled to the screen refresh rate, typically around
// 60 Hz, by sleeping the application on Present until the screen is refreshed.
DX::ThrowIfFailed(
m_swapChain->Present(1, 0)
);
}
6. Cambiar el tamaño de la ventana de la aplicación y el búfer de la cadena de intercambio
Si cambia el tamaño de la ventana de la aplicación, la aplicación debe cambiar el tamaño de los búferes de la cadena de intercambio, volver a crear la vista de destino de representación y, a continuación, presentar la imagen representada cuyo tamaño ha cambiado. Para cambiar el tamaño de los búferes de la cadena de intercambio, llamamos a IDXGISwapChain::ResizeBuffers. En esta llamada, se deja el número de búferes y el formato de los búferes sin cambios (el parámetro BufferCount en dos y el parámetro NewFormat en DXGI_FORMAT_B8G8R8A8_UNORM). Hacemos que el tamaño del búfer de reserva de la cadena de intercambio tenga el mismo tamaño que la ventana cuyo tamaño ha cambiado. Después de cambiar el tamaño de los búferes de la cadena de intercambio, creamos el nuevo destino de representación y presentamos la nueva imagen representada de forma similar a cuando inicializamos la aplicación.
// If the swap chain already exists, resize it.
DX::ThrowIfFailed(
m_swapChain->ResizeBuffers(
2,
0,
0,
DXGI_FORMAT_B8G8R8A8_UNORM,
0
)
);
Resumen y pasos siguientes
Hemos creado un dispositivo Direct3D, una cadena de intercambio y una vista de destino de representación, y hemos presentado la imagen representada en la pantalla.
A continuación, también dibujamos un triángulo en la pantalla.
Creación de sombreadores y primitivos de dibujo