PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : D3D9: LPD3DXFONT->DrawText() unscharf


Expandable
2007-09-19, 18:32:09
Hallo allerseits,

ich habe ein kleines Problem mit dem Font-Rendering unter D3D9. Meine App zeichnet nichts außer der aktuellen Framerate. Dazu wird folgende Klasse verwendet, die im D3D-SDK Sample "Text3D" auch vollkommen korrekt ihren Dienst tut:


FontRendererD3D9::FontRendererD3D9(LPDIRECT3DDEVICE9 device) : m_device(device), m_font(0)
{
if (FAILED(D3DXCreateFont(m_device, FONTSIZE, 0, FW_NORMAL, 1, false, ANSI_CHARSET, OUT_DEFAULT_PRECIS,
DEFAULT_QUALITY, FF_DONTCARE | DEFAULT_PITCH, FONTNAME, &m_font)))
throw new RenderSystemD3D9Exception("D3DXCreateFont failed");
}

FontRendererD3D9::~FontRendererD3D9()
{
if (m_font != 0)
m_font->Release();
}

void FontRendererD3D9::DrawString(const char* str, int x, int y)
{
RECT fontRectangle;
SetRect(&fontRectangle, x, y, 0, 0);

m_device->BeginScene();
m_font->DrawText(0, str, -1, &fontRectangle, DT_NOCLIP, D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f));
m_device->EndScene();
}


Ich hatte den Verdacht, dass es an irgendeinem State-Call liegen muss, allerdings zeigt mir PIX, dass außer BeginScene/EndScene und den DrawCalls nichts aufgerufen wird. Nun habe ich eine Stelle gefunden, die den Unterschied macht:

Rufe ich CreateDevice() mit D3DCREATE_SOFTWARE_VERTEXPROCESSING auf, ist die Schrift klar. Verwende ich dagegen D3DCREATE_HARDWARE_VERTEXPROCESSING, was ja eigentlich sinnvoller ist, ist die Schrift unscharf. Es funktioniert mit beidem, wenn ich den Viewport nicht ändere:


D3DVIEWPORT9 viewport;
viewport.X = 0;
viewport.Y = 0;
viewport.Height = m_winHeight;
viewport.Width = m_winWidth;
viewport.MinZ = 0.0f;
viewport.MaxZ = 1.0f;

m_device->SetViewport(&viewport);


Hier mal ein Bild zu Verdeutlichung:

http://img232.imageshack.us/img232/7046/fontrenderingth5.th.png (http://img232.imageshack.us/my.php?image=fontrenderingth5.png)

Mein Setup-Code sieht so aus:


void RenderSystemD3D9::Initialize(HINSTANCE hInstance)
{
RenderSystem::Initialize(hInstance);

m_d3dObject = Direct3DCreate9(D3D_SDK_VERSION);
if (m_d3dObject == 0)
throw RenderSystemD3D9Exception("Direct3DCreate9() failed");

m_d3dObject->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &m_displayMode);
m_d3dObject->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &m_caps);

if (m_caps.VertexShaderVersion < D3DVS_VERSION(2,0))
throw RenderSystemD3D9Exception("Vertex Shader 2.0 support required");
if (m_caps.PixelShaderVersion < D3DPS_VERSION(2,0))
throw RenderSystemD3D9Exception("Pixel Shader 2.0 support required");

SetupPresentParameters();

if (FAILED(m_d3dObject->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_hWnd,
D3DCREATE_HARDWARE_VERTEXPROCESSING, &m_presentParams, &m_device)))
throw RenderSystemD3D9Exception("CreateDevice() failed");

RenderSystem::Factory::Instance = new RenderSystemD3D9::FactoryD3D9(m_device);

m_isInitialized = true;
WindowSizeChange(m_winWidth, m_winHeight);

m_device->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
m_device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
}

void RenderSystemD3D9::SetupPresentParameters()
{
ZeroMemory(&m_presentParams, sizeof(m_presentParams));

D3DFORMAT adapterFormat = m_isWindowed == true ? m_displayMode.Format : D3DFMT_X8R8G8B8;

if (FAILED(m_d3dObject->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, adapterFormat,
D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, D3DFMT_D24S8))){
throw RenderSystemD3D9Exception("24bit depth buffer, 8bit stencil buffer not supported (D3D)");
}

m_presentParams.AutoDepthStencilFormat = D3DFMT_D24S8;
m_presentParams.Windowed = m_isWindowed;
m_presentParams.SwapEffect = D3DSWAPEFFECT_DISCARD;
m_presentParams.BackBufferCount = 1;
m_presentParams.BackBufferFormat = adapterFormat;
m_presentParams.BackBufferWidth = m_isWindowed == true ? 0 : m_displayMode.Width;
m_presentParams.BackBufferHeight = m_isWindowed == true ? 0 : m_displayMode.Height;
m_presentParams.MultiSampleType = D3DMULTISAMPLE_NONE;
m_presentParams.MultiSampleQuality = 0;
m_presentParams.hDeviceWindow = m_hWnd;
m_presentParams.EnableAutoDepthStencil = true;
m_presentParams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
}


Also irgendwie macht die Änderung des ViewPorts im Hardware-Modus das Font-Rendering kaputt. Sieht zufällig irgendjemand, woran das liegen könnte? Ich werde hier nämlich langsam wahnsinnig.

Vielen Dank!

Neomi
2007-09-19, 19:13:41
Zuerst dachte ich an ein falsches Pixelzentrum, aber die Unschärfe ist nicht konstant. Ich tippe daher auf eine Diskrepanz zwischen der vermuteten und der tatsächlichen Auflösung des Rendertargets.

Besteht das Problem auch im Vollbild oder nur im Fenstermodus? Rechnest du vielleicht mit der Fenstergröße statt mit der Größe des Clientrects? Der Text im Hardwaremodus ist minimal kleiner als der im Softwaremodus (sieht man daran, daß der linke Rand der "0" nach "Frame:" etwas weiter links ist, obwohl das "F" bei beiden Modi in der gleichen Spalte beginnt), daher sind solche etwas zu groß angenommenen Auflösungen sehr wahrscheinlich.

Expandable
2007-09-19, 19:32:16
Nein, im Fullscreen tritt der Fehler nicht auf. Wieso wird beim Hardware-Rendering von einer anderen Größe des Rendertargets ausgegangen? Es kann doch nicht sein, dass einmal der Fensterrand mit- und einmal nicht mitgerechnet wird...?

Nachtrag: Daran wird's wohl liegen... erstelle ich ein Fenster ohne Rand wird der Text auch richtig angezeigt... hmm...

Neomi
2007-09-19, 20:50:32
In dem Fall ist das "falsche" Verhalten beim Hardwarevertexprocessing wohl das richtige Ergebnis zur falschen Ansteuerung, das Verhalten beim Softwarevertexprocessing dagegen tatsächlich falsch. Wahrscheinlich gibst du beim Viewport die falsche Größe an.

Wie berechnest du m_winWidth und m_winHeight aus deinem Fenster bzw. wie berechnest du die Größe des zu erzeugenden Fensters in Abhängigkeit von der gewünschten Größe des Rendertargets? Am besten postest du den Codeschnipsel von der Fenstererzeugung/Größenberechnung.

Expandable
2007-09-20, 12:13:54
Folgende Funktion initialisiert das Fenster:


// Initialize window
void RenderSystem::Initialize()
{
WNDCLASSEX windowClass;
windowClass.cbSize = sizeof(WNDCLASSEX);
windowClass.style = CS_HREDRAW | CS_VREDRAW;
windowClass.lpfnWndProc = reinterpret_cast<WNDPROC>(RenderSystem::StaticWndProc);
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = m_hInstance;
windowClass.hIcon = LoadIcon(0, IDI_APPLICATION);
windowClass.hCursor = LoadCursor(0, IDC_ARROW);
windowClass.hbrBackground = 0;
windowClass.lpszMenuName = 0;
windowClass.lpszClassName = "RenderWindow";
windowClass.hIconSm = LoadIcon(0, IDI_WINLOGO);

if (!RegisterClassEx(&windowClass))
throw RenderSystemException("Window registration failed");

DWORD windowStyle, exWindowStyle;
if (m_isWindowed)
{
windowStyle = WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_SYSMENU | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
exWindowStyle = 0;
}
else
{
windowStyle = WS_POPUP;
exWindowStyle = WS_EX_APPWINDOW;
}

m_hWnd = CreateWindowEx(
exWindowStyle,
"RenderWindow",
"Engine 2",
windowStyle,
0, 0,
m_winWidth, m_winHeight,
0,
0,
m_hInstance,
this);

if (m_hWnd == 0)
throw RenderSystemException("Window creation failed");

SetWindowPos(m_hWnd, HWND_TOP, 0, 0, m_winWidth, m_winHeight, SWP_SHOWWINDOW | SWP_NOCOPYBITS);
}


In m_winWidth und m_winHeight steht die gewünschte Fenstergröße, welche ich wiederum zur Berechnung des Viewports verwende. Oder ist die Fenstergröße inkl. Rahmen und somit der angegebene Viewport zu groß? Wie bekomme ich die Größe des Viewports ohne Fensterrahmen raus?

Neomi
2007-09-20, 14:12:37
Ah ja, das habe ich mir gedacht. Deine Breite und Höhe sind nicht die des Clientrects, sondern die des gesamten Fensters inklusive Rahmen und Titelleiste. Dein Clientrect (das die Größe des Rendertargets und damit des Viewports bestimmt) ist dann natürlich kleiner. Mit der Funktion AdjustWindowRectEx (http://msdn2.microsoft.com/en-us/library/ms632667.aspx) kannst du passend zum gewünschten Clientrect die richtige Fenstergröße berechnen lassen, das funktioniert dann in etwa so:

RECT rect = { 0, 0, m_winWidth, m_winHeight };

AdjustWindowRectEx(&rect, windowStyle, FALSE, exWindowStyle);

m_hWnd = CreateWindowEx(exWindowStyle, "RenderWindow", "Engine 2", windowStyle,
CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left, rect.bottom - rect.top,
0, 0, m_hInstance, this);

Alternativ kannst du die Fenstergröße so lassen wie sie ist und abfragen, wie groß das Clientrect ist, das machst du mit der Funktion GetClientRect (http://msdn2.microsoft.com/en-us/library/ms633503.aspx).

Expandable
2007-09-21, 10:32:01
Alternativ kannst du die Fenstergröße so lassen wie sie ist und abfragen, wie groß das Clientrect ist, das machst du mit der Funktion GetClientRect (http://msdn2.microsoft.com/en-us/library/ms633503.aspx).

So funktioniert's bestens! Die andere Lösung hat das Ergebnis zwar verbessert (die Schrift war schärfer, aber immer noch nicht 100%ig scharf) und hat mir sowieso nicht ganz so gut gefallen, weil dann ein Teil des Fensters aus dem Bildschirm rausragte (ist ja klar... wenn der Inhalt 1024 Pixel hoch sein soll, ragt die Größe der Windows-Titelzeile unten aus dem Bildschirm raus bei einer Auflösung von 1280x1024).

Problem gelöst. Herzlichen Dank!