Мне трудно понять, как нарисовать круг в DirectX. Я могу нарисовать треугольник, поэтому я подумал, что смогу нарисовать его, просто повернув треугольник по кругу. Тем не менее, я абсолютно в тупик. Я часами рыскал в сети без удачи. Может кто-нибудь мне помочь? Вот мой код:
main.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 %d\n", x, y);
}
};
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
MyApp app;
app.Run();
return 0;
}
Engine.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;
}
Engine.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_
Triangle2D.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_
Triangle2D.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. Большинство игр на самом деле никогда не рисуют настоящие круги. Они рисуют 2D-спрайты в виде двух текстурированных треугольников в Direct3D, используя изображение круга, нарисованное на нем художником в чем-то вроде Photoshop или Paint.
Если вы настаиваете на использовании Direct3D, см. Набор инструментов DirectX для Direct3D 11 DebugDraw помощник, который может рисовать «кольца» (то есть круги отрезка линии в трехмерном пространстве). Это должно дать вам представление о том, как это делается. Этот код всегда использует 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, вы должны использовать Direct3D 11 или, возможно, Direct2D, если вы действительно делаете 2D презентационную графику.
Других решений пока нет …