Direct2D的双缓冲?

时间:2017-02-19 19:56:26

标签: c++ drawing directx

我对Direct2D编程非常陌生并且一直在关注教程。我已经将教程中给出的示例改编为一个稍微复杂的程序,该程序将球从窗口的边界弹回。

我的主程序(main.cpp):

#include "Graphics.h"

Graphics* graphics;

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    // Exit handler

    if (uMsg == WM_DESTROY)
    {
        PostQuitMessage(0);
        return 0;
    }

    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPWSTR cmd, int nCmdShow)
{
    WNDCLASSEX windowClass;
    SecureZeroMemory(&windowClass, sizeof(WNDCLASSEX));

    // Set up window

    windowClass.cbSize = sizeof(WNDCLASSEX);
    windowClass.hbrBackground = (HBRUSH)COLOR_WINDOW;
    windowClass.hInstance = hInstance;
    windowClass.lpfnWndProc = WindowProc;
    windowClass.lpszClassName = "MainWindow";
    windowClass.style = CS_HREDRAW | CS_VREDRAW;

    // Register window class and handle

    RegisterClassEx(&windowClass);

    RECT rect = { 0, 0, 800, 600 };
    AdjustWindowRectEx(&rect, WS_OVERLAPPEDWINDOW, false, WS_EX_OVERLAPPEDWINDOW);

    HWND windowHandle = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, "MainWindow", "Test Window", WS_OVERLAPPEDWINDOW, 100, 100,
        rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, hInstance, 0);

    if (!windowHandle)
        return -1;

    graphics = new Graphics();

    if (!graphics->Init(windowHandle))
    {
        delete graphics;
        return -1;
    }

    ShowWindow(windowHandle, nCmdShow);

    // Message loop

    float x = 51.0, xSpeed = 5.0f, y = 0.0, ySpeed = 5.0f;

    MSG message;
    message.message = WM_NULL;

    while (message.message != WM_QUIT)
    {
        if (PeekMessage(&message, NULL, 0, 0, PM_REMOVE))
            DispatchMessage(&message);
        else
        {
            // Ball physics

            //xSpeed += 0.6f;
            x += xSpeed;

            ySpeed += 0.2f;
            y += ySpeed;

            if (y > rect.bottom - 50)
            {
                ySpeed = -ySpeed;
            }

            if (x > rect.right - 50)
            {
                xSpeed = -xSpeed;
            }
            else if (x < 50)
            {
                xSpeed = -xSpeed;
            }

            // Redraw ball

            graphics->beginDraw();

            graphics->clearScreen(0.0f, 0.0f, 0.5f);
            graphics->drawCircle(x, y, 50.0f, 1.0f, 1.0f, 1.0f, 1.0f);

            graphics->endDraw();
        }
    }

    delete graphics;

    return 0;
}

我的头文件(Graphics.h):

#pragma once

#include <Windows.h>
#include <d2d1.h>

class Graphics
{
    ID2D1Factory* factory;
    ID2D1HwndRenderTarget* renderTarget;
    ID2D1SolidColorBrush* brush;

public:
    Graphics();
    ~Graphics();

    bool Init(HWND windowHandle);

    void beginDraw() { renderTarget->BeginDraw(); }
    void endDraw() { renderTarget->EndDraw(); }

    void clearScreen(float r, float g, float b);
    void drawCircle(float x, float y, float radius, float r, float g, float b, float a);
};

我的图形功能(Graphics.cpp):

#include "Graphics.h"

#define CHECKRES if (res != S_OK) return false

Graphics::Graphics()
{
    factory = NULL;
    renderTarget = NULL;
    brush = NULL;
}

Graphics::~Graphics()
{
    if (factory)
        factory->Release();

    if (renderTarget)
        renderTarget->Release();

    if (brush)
        brush->Release();
}

bool Graphics::Init(HWND windowHandle)
{
    HRESULT res = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &factory);

    CHECKRES;

    RECT rect;
    GetClientRect(windowHandle, &rect);

    res = factory->CreateHwndRenderTarget(
        D2D1::RenderTargetProperties(),
        D2D1::HwndRenderTargetProperties(windowHandle, D2D1::SizeU(rect.right, rect.bottom)),
        &renderTarget
    );

    CHECKRES;

    res = renderTarget->CreateSolidColorBrush(D2D1::ColorF(0, 0, 0, 0), &brush);

    CHECKRES;

    return true;
}

void Graphics::clearScreen(float r, float g, float b)
{
    renderTarget->Clear(D2D1::ColorF(r, g, b));
}

void Graphics::drawCircle(float x, float y, float radius, float r, float g, float b, float a)
{
    brush->SetColor(D2D1::ColorF(r, g, b, a));
    renderTarget->DrawEllipse(D2D1::Ellipse(D2D1::Point2F(x, y), radius, radius), brush, 3.0f);
}

虽然这个项目确实运作良好,但球的反弹有一些轻微的撕裂。我看到this question引导我this MSDN article。尽管阅读了这篇文章,我仍然不完全了解如何实现双缓冲,以期有望减少撕裂。有人可以提供ID2D1RenderTarget::CreateCompatibleRenderTarget的简明示例和解释,因为这种高级Windows编程与我以前的编程完全不同吗?

1 个答案:

答案 0 :(得分:3)

Check article here。 ID2D1HwndRenderTarget对象本质上是双缓冲的,并且首先对屏幕外缓冲区进行绘制,当绘制结束时,它将在屏幕上显示。