在 directx 9 中画一个圆

Drawing a circle in directx 9

本文关键字:一个 directx      更新时间:2023-10-16

我很难弄清楚如何在directx中画一个圆。我能够画一个三角形,所以我想象我可以通过将三角形旋转成一个圆来画一个三角形。但是,我完全被难住了。我已经在网上搜索了几个小时,但没有运气。谁能帮我?这是我的代码:

主.cpp

#include "Engine.h"
#include "Box2D.h"
#include "Triangle2D.h"
class MyApp : public Engine {
public:
    Box2D box2D;
    Triangle2D triangle2D;
    void OnStartup() override {
        box2D.New(10.0f, 10.0f, 100.0f, 100.0f, D3DCOLOR_ARGB(0xff, 0x00, 0x00, 0xff)); 
        triangle2D.New(150.0f, 10.0f, 100.0f, 100.0f, D3DCOLOR_ARGB(0xff, 0xff, 0x00, 0x00));
    }
    void OnShutdown() override {
        box2D.Delete();
        triangle2D.Delete();
    }
    void OnDraw() override {
        box2D.Draw();
        triangle2D.Draw();
    }
    void OnMouseDown(int x, int y) {
        Debug("%d %dn", x, y);
    }
};
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    MyApp app;
    app.Run();
    return 0;
}

引擎.cpp

#include "Engine.h"
// Initialize the classes static variables
Engine* Engine::_ENGINE = NULL;
// Returns the current instance of this class
Engine* Engine::GetInstance()
{
    return _ENGINE;
}
// Constructor
Engine::Engine()
: _exit(false)
, _window(NULL)
, _directx(NULL)
, _device(NULL)
{
    _ENGINE = this;
}
// Deconstructor
Engine::~Engine() {
    _ENGINE = NULL;
}
// Run the core main event loop, start to finish
bool Engine::Run() {
    // Create the Windows class
    WNDCLASSEX wc;
    ZeroMemory(&wc, sizeof(wc));
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS | CS_CLASSDC;
    wc.lpfnWndProc = _WND_PROC;
    wc.hInstance = GetModuleHandle(NULL);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszClassName = _GetModuleName();
    RegisterClassEx(&wc);
    // Adjust the window rect for the flags
    RECT rect;
    SetRect(&rect, 0, 0, _WIDTH, _HEIGHT);
    const DWORD windowFlags = WS_OVERLAPPED | WS_CAPTION | WS_CLIPCHILDREN | WS_SYSMENU | WS_MINIMIZEBOX;
    AdjustWindowRectEx(&rect, windowFlags, FALSE, 0);
    // Create the window
    _window = CreateWindowEx(0, _GetModuleName(), _GetModuleName(), windowFlags, 0, 0, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, GetModuleHandle(NULL), NULL);
    if (_window == NULL)
        return false;
    // Move the window to the center of the screen
    RECT system;
    SystemParametersInfo(SPI_GETWORKAREA, 0, &system, 0);
    MoveWindow(_window, (system.right - system.left) / 2 - (rect.right - rect.left) / 2, (system.bottom - system.top) / 2 - (rect.bottom - rect.top) / 2, rect.right - rect.left, rect.bottom - rect.top, TRUE);
    // Startup Direct X
    if (_DirectXStartup() == false)
        return false;
    // Call our startup callback
    OnStartup();
    // Show the window
    ShowWindow(_window, SW_SHOWNORMAL);
    // Run the event loop
    MSG msg;
    ULONGLONG timer = GetTickCount64();
    while (!_exit) {
        // Handle normal system events
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        // Run the timer at the framerate
        if (timer + 1000 / _FPS < GetTickCount64()) {
            timer = GetTickCount64();
            // Clear the buffer
            _device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0x00, 0xff, 0x00), 1.0f, NULL);
            // Call our draw callback every frame
            OnDraw();
            // Render to screen
            _device->Present(NULL, NULL, NULL, NULL);
        }
    }
    // Hide the window to avoid flicker
    ShowWindow(_window, SW_HIDE);
    // Call our shutdown callback
    OnShutdown();
    // Shutdown Direct X
    _DirectXShutdown();
    // Cleanup and destroy the window
    DestroyWindow(_window);
    UnregisterClass(_GetModuleName(), GetModuleHandle(NULL));
    return true;
}
// Return the DirectX device, needed by other DirectX objects
LPDIRECT3DDEVICE9 Engine::GetDevice() {
    return _device;
}
// Return our width
int Engine::GetWidth() {
    return _WIDTH;
}
// Return our height
int Engine::GetHeight() {
    return _HEIGHT;
}
// Our own custom debug string
void Engine::Debug(const char* message, ...) {
#if _DEBUG
    if (message) {
        va_list args;
        va_start(args, message);
        int size = vsnprintf(NULL, 0, message, args);
        if (size > 0) {
            char* string = new char[size + 1];
            vsnprintf(string, size + 1, message, args);
            OutputDebugStringA(string);
            delete[] string;
        }
        va_end(args);
    }
#endif
}
// This is the Windows callback
LRESULT CALLBACK Engine::_WND_PROC(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    // If we do not have access to an instance of our engine, do nothing
    if (Engine::_ENGINE == NULL)
        return  DefWindowProc(hwnd, uMsg, wParam, lParam);
    // Handle system messages
    switch (uMsg) {
    case WM_LBUTTONDOWN:
        Engine::_ENGINE->OnMouseDown(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
        return 0;
    case WM_KEYDOWN:
    case WM_SYSKEYDOWN:
        if ((lParam & (1 << 30)) == 0)
            Engine::_ENGINE->OnKeyDown((int)wParam);
        return 0;
    case WM_CHAR:
        if ((int)wParam <= 127 && isprint((int)wParam) && (lParam & (1 << 30)) == 0)
            Engine::_ENGINE->OnASCIIDown((char)wParam);
        return 0;
    case WM_CLOSE:
        Engine::_ENGINE->_exit = true;
        return 0;
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
// Custom function for getting our exe name
WCHAR* Engine::_GetModuleName() {
    static WCHAR* TITLE = NULL;
    static WCHAR BUFFER[MAX_PATH];
    if (TITLE != NULL)
        return TITLE;
    // Remove the path
    GetModuleFileName(NULL, BUFFER, MAX_PATH);
    TITLE = wcsrchr(BUFFER, '');
    if (TITLE == NULL) {
        wcscpy(BUFFER, L"Application");
        TITLE = BUFFER;
        return TITLE;
    }
    TITLE++;
    // Remove the extension
    WCHAR* ext = wcsrchr(BUFFER, '.');
    if (ext)
        *ext = 0;
    return TITLE;
}
// Startup DirectX here
bool Engine::_DirectXStartup() {
    // Startup Direct X
    _directx = Direct3DCreate9(D3D_SDK_VERSION);
    if (_directx == NULL)
        return false;
    // Create a Direct X device
    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.Windowed = TRUE;
    d3dpp.hDeviceWindow = _window;
    d3dpp.BackBufferWidth = _WIDTH;
    d3dpp.BackBufferHeight = _HEIGHT;
    d3dpp.BackBufferCount = 1;
    d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
    d3dpp.EnableAutoDepthStencil = TRUE;
    d3dpp.AutoDepthStencilFormat = D3DFMT_D24X8;
    d3dpp.SwapEffect = D3DSWAPEFFECT_COPY;
    HRESULT result = _directx->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, _window, D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &_device);
    if (FAILED(result))
        result = _directx->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, _window, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &_device);
    if (FAILED(result))
        return false;
    return true;
}
// Shutdown DirectX here
bool Engine::_DirectXShutdown() {
    if (_device) {
        _device->Release();
        _device = NULL;
    }
    if (_directx) {
        _directx->Release();
        _directx = NULL;
    }
    return true;
}

发动机.h

#ifndef _ENGINE_H_
#define _ENGINE_H_
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <windowsx.h>
#include <stdio.h>
#include <d3d9.h>
#include <d3dx9.h>
class Engine {
public:
    // Return the current singleton of this Engine class
    static Engine* GetInstance();
    // Constructor and deconstructor
    Engine();
    virtual ~Engine();
    // Run the Engine class
    bool Run();
    // Return useful information about the class
    LPDIRECT3DDEVICE9 GetDevice();
    int GetWidth();
    int GetHeight();
    // Use for debug output
    static void Debug(const char* message, ...);
    // Virtual callbacks
    virtual void OnStartup() {}
    virtual void OnShutdown() {}
    virtual void OnMouseDown(int x, int y) {}
    virtual void OnKeyDown(int key) {}
    virtual void OnASCIIDown(char key) {}
    virtual void OnDraw() {}
private:
    static Engine* _ENGINE;
    static const int _WIDTH = 854;
    static const int _HEIGHT = 480;
    static const int _FPS = 60;
    bool _exit;
    HWND _window;
    LPDIRECT3D9 _directx;
    LPDIRECT3DDEVICE9 _device;
    static LRESULT CALLBACK _WND_PROC(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    WCHAR* _GetModuleName();
    bool _DirectXStartup();
    bool _DirectXShutdown();
};
#endif // _ENGINE_H_

三角形2D.h

#ifndef _TRIANGLE_2D_H_
#define _TRIANGLE_2D_H_
#include "Engine.h"
class Triangle2D {
public:
    Triangle2D();
    bool New(FLOAT x, FLOAT y, FLOAT width, FLOAT height, DWORD color);
    void Delete();
    void Draw();
    void Draw(FLOAT x, FLOAT y, FLOAT width, FLOAT height, DWORD color);
private:
    struct CustomVertex {
        FLOAT x, y, z, w;
        DWORD color;
    };
    static const DWORD _FVF = D3DFVF_XYZRHW | D3DFVF_DIFFUSE;
    LPDIRECT3DVERTEXBUFFER9 _vb;
};
#endif // _BOX_2D_H_

三角形2D.cpp

#include "Triangle2D.h"
Triangle2D::Triangle2D()
: _vb(NULL)
{
}
bool Triangle2D::New(FLOAT x, FLOAT y, FLOAT width, FLOAT height, DWORD color) {
    if (FAILED(Engine::GetInstance()->GetDevice()->CreateVertexBuffer(sizeof(CustomVertex)* 4, D3DUSAGE_WRITEONLY, _FVF, D3DPOOL_DEFAULT, &_vb, NULL)))
        return false;
    CustomVertex* vertices;
    _vb->Lock(0, 0, (void**)&vertices, 0);
    vertices[0].x = x;
    vertices[0].y = y;
    vertices[0].z = 0.0f;
    vertices[0].w = 1.0f;
    vertices[0].color = color;
    vertices[1].x = x + width;
    vertices[1].y = y;
    vertices[1].z = 0.0f;
    vertices[1].w = 1.0f;
    vertices[1].color = color;
    vertices[2].x = x;
    vertices[2].y = y + height;
    vertices[2].z = 0.0f;
    vertices[2].w = 1.0f;
    vertices[2].color = color;
    _vb->Unlock();
    return true;
}
void Triangle2D::Delete(){
    if (_vb) {
        _vb->Release();
        _vb = NULL;
    }
}
void Triangle2D::Draw() {
    Engine::GetInstance()->GetDevice()->BeginScene();
    Engine::GetInstance()->GetDevice()->SetFVF(_FVF);
    Engine::GetInstance()->GetDevice()->SetStreamSource(0, _vb, 0, sizeof(CustomVertex));
    Engine::GetInstance()->GetDevice()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
    Engine::GetInstance()->GetDevice()->EndScene();
}
void Triangle2D::Draw(FLOAT x, FLOAT y, FLOAT width, FLOAT height, DWORD color) {
    CustomVertex* vertices;
    _vb->Lock(0, 0, (void**)&vertices, 0);
    vertices[0].x = x;
    vertices[0].y = y;
    vertices[0].z = 0.0f;
    vertices[0].w = 1.0f;
    vertices[0].color = color;
    vertices[1].x = x + width;
    vertices[1].y = y;
    vertices[1].z = 0.0f;
    vertices[1].w = 1.0f;
    vertices[1].color = color;
    vertices[2].x = x;
    vertices[2].y = y + height;
    vertices[2].z = 0.0f;
    vertices[2].w = 1.0f;
    vertices[2].color = color;
    _vb->Unlock();
    Draw();
}

Direct3D 不绘制"圆圈"。它本身只绘制三个基本基元:点、线和三角形。对于它如何绘制这些东西,有很多选项,但这就是它可以原生绘制的全部内容。OpenGL也是如此(它有时也可以画"四边形",但你总是可以画一个带有两个三角形的四边形)。

通常,绘制圆圈和其他平滑对象最好通过"矢量图形"库来完成。这些可以在正确的分辨率下渲染光滑对象(如圆)的高质量近似值。这是旧版 GDI 库的功能,也是 Direct2D 可以执行的操作。你可以为圆编写自己的近似值,但效果可能不如 Direct2D。这些库最终会为 Direct3D 执行的实际绘图操作生成点、线和三角形。

因此,如果你正在执行"演示"图形,则应考虑使用 Direct2D 而不是 Direct3D。大多数游戏实际上从未画过真正的圆圈。他们使用 Direct3D 将 2D 精灵绘制为两个带纹理的三角形,使用艺术家在 Photoshop 或 Paint 等东西中绘制的圆圈图像。

如果你坚持使用 Direct3D,请参阅 Direct3D 11 DebugDraw 帮助程序的 DirectX 工具包,该工具包可以绘制"环"(即 3D 空间中的线段圆)。这应该让您了解如何完成此操作。此代码始终使用 32 个段来形成环,但"矢量图形"库将根据它将在屏幕上覆盖的像素数来确定将其分解为多少个段:

void XM_CALLCONV DX::DrawRing(PrimitiveBatch<VertexPositionColor>* batch,
    FXMVECTOR origin,
    FXMVECTOR majorAxis,
    FXMVECTOR minorAxis,
    GXMVECTOR color)
{
    static const size_t c_ringSegments = 32;
    VertexPositionColor verts[c_ringSegments + 1];
    FLOAT fAngleDelta = XM_2PI / float(c_ringSegments);
    // Instead of calling cos/sin for each segment we calculate
    // the sign of the angle delta and then incrementally calculate sin
    // and cosine from then on.
    XMVECTOR cosDelta = XMVectorReplicate(cosf(fAngleDelta));
    XMVECTOR sinDelta = XMVectorReplicate(sinf(fAngleDelta));
    XMVECTOR incrementalSin = XMVectorZero();
    static const XMVECTORF32 s_initialCos =
    {
        1.f, 1.f, 1.f, 1.f
    };
    XMVECTOR incrementalCos = s_initialCos.v;
    for (size_t i = 0; i < c_ringSegments; i++)
    {
        XMVECTOR pos = XMVectorMultiplyAdd(majorAxis, incrementalCos, origin);
        pos = XMVectorMultiplyAdd(minorAxis, incrementalSin, pos);
        XMStoreFloat3(&verts[i].position, pos);
        XMStoreFloat4(&verts[i].color, color);
        // Standard formula to rotate a vector.
        XMVECTOR newCos = incrementalCos * cosDelta - incrementalSin * sinDelta;
        XMVECTOR newSin = incrementalCos * sinDelta + incrementalSin * cosDelta;
        incrementalCos = newCos;
        incrementalSin = newSin;
    }
    verts[c_ringSegments] = verts[0];
    batch->Draw(D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP, verts, c_ringSegments + 1);
}
Direct3D

9是旧版的,除非你专门针对Windows XP,否则如果你真的在做2D演示图形,你应该使用Direct3D 11或Direct2D。