En aquest tema, aprendrem a gestionar el bucle de renderització en una aplicació DirectX. El bucle de renderització és el cor de qualsevol aplicació gràfica en temps real, ja que és responsable de dibuixar els fotogrames a la pantalla de manera contínua. A continuació, desglossarem els conceptes clau, proporcionarem exemples pràctics i oferirem exercicis per reforçar l'aprenentatge.
Conceptes Clau
- Bucle de Renderització: És un bucle infinit que actualitza i dibuixa el contingut de la pantalla.
- Actualització de l'Estat: Processar la lògica del joc o aplicació abans de dibuixar.
- Renderització: Dibuixar els objectes a la pantalla.
- Sincronització Vertical (V-Sync): Sincronitzar la velocitat de fotogrames amb la freqüència de refresc del monitor per evitar el "tearing".
Estructura del Bucle de Renderització
El bucle de renderització típic en una aplicació DirectX segueix aquests passos:
- Processar Missatges del Sistema: Gestionar els esdeveniments del sistema operatiu.
- Actualitzar l'Estat: Actualitzar la lògica del joc o aplicació.
- Renderitzar: Dibuixar el contingut a la pantalla.
- Presentar el Fotograma: Mostrar el fotograma renderitzat a la pantalla.
Exemple Pràctic
A continuació, es mostra un exemple de codi en C++ que implementa un bucle de renderització bàsic utilitzant DirectX 11.
#include <windows.h>
#include <d3d11.h>
// Variables globals
HWND hwnd = nullptr;
ID3D11Device* device = nullptr;
ID3D11DeviceContext* context = nullptr;
IDXGISwapChain* swapChain = nullptr;
ID3D11RenderTargetView* renderTargetView = nullptr;
// Prototips de funcions
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void InitD3D(HWND hwnd);
void CleanD3D();
void RenderFrame();
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// Crear finestra
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WindowProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, _T("DirectXExample"), NULL };
RegisterClassEx(&wc);
hwnd = CreateWindow(wc.lpszClassName, _T("DirectX Example"), WS_OVERLAPPEDWINDOW, 100, 100, 800, 600, NULL, NULL, wc.hInstance, NULL);
// Inicialitzar Direct3D
InitD3D(hwnd);
// Mostrar finestra
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Bucle de missatges
MSG msg = { 0 };
while (msg.message != WM_QUIT) {
if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
} else {
// Actualitzar l'estat
// (Aquí aniria la lògica del joc o aplicació)
// Renderitzar
RenderFrame();
}
}
// Netejar Direct3D
CleanD3D();
UnregisterClass(wc.lpszClassName, wc.hInstance);
return 0;
}
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);
}
void InitD3D(HWND hwnd) {
// Crear dispositiu i context de dispositiu
D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, NULL, 0, D3D11_SDK_VERSION, &device, NULL, &context);
// Crear cadena d'intercanvi
DXGI_SWAP_CHAIN_DESC scd = { 0 };
scd.BufferCount = 1;
scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
scd.OutputWindow = hwnd;
scd.SampleDesc.Count = 4;
scd.Windowed = TRUE;
IDXGIFactory* factory;
CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&factory);
factory->CreateSwapChain(device, &scd, &swapChain);
factory->Release();
// Crear vista de renderització
ID3D11Texture2D* backBuffer;
swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&backBuffer);
device->CreateRenderTargetView(backBuffer, NULL, &renderTargetView);
backBuffer->Release();
// Establir la vista de renderització
context->OMSetRenderTargets(1, &renderTargetView, NULL);
}
void CleanD3D() {
swapChain->Release();
renderTargetView->Release();
context->Release();
device->Release();
}
void RenderFrame() {
// Esborrar la pantalla
float color[4] = { 0.0f, 0.2f, 0.4f, 1.0f };
context->ClearRenderTargetView(renderTargetView, color);
// (Aquí aniria el codi de renderització)
// Presentar el fotograma
swapChain->Present(0, 0);
}Explicació del Codi
-
Inicialització de Direct3D:
D3D11CreateDevice: Crea el dispositiu Direct3D i el context de dispositiu.CreateSwapChain: Crea la cadena d'intercanvi per gestionar els fotogrames.CreateRenderTargetView: Crea la vista de renderització per al back buffer.
-
Bucle de Missatges:
PeekMessage: Processa els missatges del sistema operatiu.TranslateMessageiDispatchMessage: Gestionen els esdeveniments de la finestra.
-
Renderització:
ClearRenderTargetView: Esborra la pantalla amb un color de fons.Present: Mostra el fotograma renderitzat a la pantalla.
Exercicis Pràctics
-
Afegir Actualització de l'Estat:
- Modifica el codi per incloure una funció que actualitzi la lògica del joc o aplicació abans de la renderització.
-
Implementar V-Sync:
- Modifica el codi per activar la sincronització vertical (V-Sync) en la funció
Present.
- Modifica el codi per activar la sincronització vertical (V-Sync) en la funció
-
Afegir Objectes a la Renderització:
- Afegeix codi per dibuixar objectes simples (com quadrats o cercles) a la pantalla.
Solucions
Exercici 1: Afegir Actualització de l'Estat
void Update() {
// Aquí aniria la lògica del joc o aplicació
}
// Dins del bucle de missatges
if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
} else {
Update(); // Actualitzar l'estat
RenderFrame(); // Renderitzar
}Exercici 2: Implementar V-Sync
Exercici 3: Afegir Objectes a la Renderització
void RenderFrame() {
// Esborrar la pantalla
float color[4] = { 0.0f, 0.2f, 0.4f, 1.0f };
context->ClearRenderTargetView(renderTargetView, color);
// Aquí aniria el codi per dibuixar objectes
// Presentar el fotograma
swapChain->Present(1, 0);
}Conclusió
En aquesta secció, hem après a gestionar el bucle de renderització en una aplicació DirectX. Hem vist com inicialitzar Direct3D, processar missatges del sistema, actualitzar l'estat de l'aplicació i renderitzar fotogrames. A més, hem proporcionat exercicis pràctics per reforçar els conceptes apresos. En el següent mòdul, explorarem com treballar amb shaders per crear efectes visuals avançats.
Curs de Programació DirectX
Mòdul 1: Introducció a DirectX
- Què és DirectX?
- Configuració de l'Entorn de Desenvolupament
- Comprendre l'API de DirectX
- Crear la Teva Primera Aplicació DirectX
Mòdul 2: Conceptes Bàsics de Direct3D
- Introducció a Direct3D
- Inicialitzar Direct3D
- Renderitzar un Triangle
- Gestionar el Bucle de Renderització
Mòdul 3: Treballar amb Shaders
Mòdul 4: Tècniques Avançades de Renderització
Mòdul 5: Models 3D i Animació
Mòdul 6: Optimització del Rendiment
- Perfilat i Depuració
- Optimitzar el Rendiment de la Renderització
- Gestió de Memòria
- Multifil en DirectX
