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.TranslateMessage
iDispatchMessage
: 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