![]() |
Projektion mit Direct3D berechnen
Hallo!
Ich möchte mit Direct3D einen 2D-Schriftzug auf dem Bildschirm genau dort zeichnen, wo sich die Kante eines Würfels befindet. Hier erstmal mein Quellcode
Code:
Leider funktioniert das ganze nicht so, wie ich mir das vorstelle. Der Schirftzug "Hallo" irgendowo im oberen linken Fenster-Bereich gezeichnet ...und nicht etwa auf der Würfelkante.
unit RendererD3D;
interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Direct3D8, d3dx8, ExtCtrls, ComCtrls; const Pi180 = Pi/180.0; Fovy = Pi/4; // Öffnungswinkel des Sichtkegels (Field of view) ViewDist = 15; // Abstand des Betrachters vom Nullpunkt ViewHeight = 5; // Höhe des Betrachters Delta = 0.01; // Winkelinkrement für Animation CubeCount = 36; type TMyVertex = record x,y,z : single; color : dword; end; // Vertexliste für Würfel TCube = array [0..CubeCount-1] of TMyVertex; const D3D8T_CUSTOMVERTEX =D3DFVF_XYZ or D3DFVF_DIFFUSE; // Standardwürfel (Seitenlänge 2) NormCube : TCube = ( (x :-1.0; y :-1.0; z :-1.0; color : $000000FF), // Vorn (x :-1.0; y : 1.0; z :-1.0; color : $000000FF), (x : 1.0; y : 1.0; z :-1.0; color : $000000FF), (x : 1.0; y : 1.0; z :-1.0; color : $000000FF), (x : 1.0; y :-1.0; z :-1.0; color : $000000FF), (x :-1.0; y :-1.0; z :-1.0; color : $000000FF), (x :-1.0; y :-1.0; z : 1.0; color : $00FF00FF), // Links (x :-1.0; y : 1.0; z : 1.0; color : $00FF00FF), (x :-1.0; y : 1.0; z :-1.0; color : $00FF00FF), (x :-1.0; y : 1.0; z :-1.0; color : $00FF00FF), (x :-1.0; y :-1.0; z :-1.0; color : $00FF00FF), (x :-1.0; y :-1.0; z : 1.0; color : $00FF00FF), (x : 1.0; y :-1.0; z : 1.0; color : $0000FF00), // Hinten (x : 1.0; y : 1.0; z : 1.0; color : $0000FF00), (x :-1.0; y : 1.0; z : 1.0; color : $0000FF00), (x :-1.0; y : 1.0; z : 1.0; color : $0000FF00), (x :-1.0; y :-1.0; z : 1.0; color : $0000FF00), (x : 1.0; y :-1.0; z : 1.0; color : $0000FF00), (x : 1.0; y :-1.0; z :-1.0; color : $0000FFFF), // Rechts (x : 1.0; y : 1.0; z :-1.0; color : $0000FFFF), (x : 1.0; y : 1.0; z : 1.0; color : $0000FFFF), (x : 1.0; y : 1.0; z : 1.0; color : $0000FFFF), (x : 1.0; y :-1.0; z : 1.0; color : $0000FFFF), (x : 1.0; y :-1.0; z :-1.0; color : $0000FFFF), (x :-1.0; y : 1.0; z :-1.0; color : $00FF0000), // Oben (x :-1.0; y : 1.0; z : 1.0; color : $00FF0000), (x : 1.0; y : 1.0; z : 1.0; color : $00FF0000), (x : 1.0; y : 1.0; z : 1.0; color : $00FF0000), (x : 1.0; y : 1.0; z :-1.0; color : $00FF0000), (x :-1.0; y : 1.0; z :-1.0; color : $00FF0000), (x : 1.0; y :-1.0; z :-1.0; color : $00FFFF00), // Unten (x : 1.0; y :-1.0; z : 1.0; color : $00FFFF00), (x :-1.0; y :-1.0; z : 1.0; color : $00FFFF00), (x :-1.0; y :-1.0; z : 1.0; color : $00FFFF00), (x :-1.0; y :-1.0; z :-1.0; color : $00FFFF00), (x : 1.0; y :-1.0; z :-1.0; color : $00FFFF00)); type TRendererD3D = class procedure StartScene(Handle: HWND); procedure StopScene; private lpd3d : IDIRECT3D8; lpd3ddevice : IDirect3DDevice8; D3dDevCaps : TD3DCaps8; // Vertexbuffer für Würfel CubeVB : IDirect3DVertexBuffer8; // Rotationswinkel für den Beobachter in Grad RotY : single ; HwVertexProcess, FullScreen, Animate : boolean; FFont: TFont; FDXFont: ID3DXFont; procedure D3DInit(Handle: HWND); procedure D3DShutdown; procedure D3DInitScene; procedure D3DKillScene; procedure D3DRender; procedure MyIdleHandler (Sender: TObject; var Done: Boolean); public constructor Create; end; implementation constructor TRendererD3D.Create; begin // Font erzeugen Self.FFont := TFont.Create; with Self.FFont do begin Style := [fsBold]; Size := 10; Name := 'Arial'; end; lpd3d:=nil; lpd3ddevice:=nil; CubeVB:=nil; RotY:=0; Animate:=false; Application.OnIdle:= MyIdleHandler; end; procedure TRendererD3D.D3DInit(Handle: HWND); var hr : HRESULT; d3dpp : TD3DPRESENTPARAMETERS; d3ddm : TD3DDISPLAYMODE; vp : integer; begin lpd3d:=Direct3DCreate8(D3D_SDK_VERSION); if(lpd3d=nil) then raise Exception.Create('Fehler beim Erstellen von Direct3D!'); // Setze zunächst alle D3DPRESENT_PARAMETERS auf 0 ZeroMemory(@d3dpp,sizeof(d3dpp)); with d3dpp do begin SwapEffect:=D3DSWAPEFFECT_DISCARD; hDeviceWindow:=Handle; // Dies ist unser HWND von TForm // Wir brauche einen Z-Buffer also schalten wir ihn ein EnableAutoDepthStencil := TRUE; AutoDepthStencilFormat := D3DFMT_D16; // Initialisieren des Backbuffers Windowed:=not FullScreen; hr:=lpd3d.GetAdapterDisplayMode(D3DADAPTER_DEFAULT,d3ddm); if failed(hr) then raise Exception.Create('Fehler beim Ermitteln des Dislaymodes'); BackBufferFormat := d3ddm.Format; end; // Hardware T&L? hr:=lpd3d.GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,D3dDevCaps); if FAILED(hr) then raise Exception.Create('Fehler beim Abfragen der DevCaps'); HwVertexProcess:=D3dDevCaps.DevCaps and D3DDEVCAPS_HWTRANSFORMANDLIGHT <> 0; if HwVertexProcess then vp:=D3DCREATE_HARDWARE_VERTEXPROCESSING else vp:=D3DCREATE_SOFTWARE_VERTEXPROCESSING; //Erstellen des D3D-Device hr:=lpd3d.CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, Handle, vp, d3dpp, lpd3ddevice); if FAILED(hr) then raise Exception.Create('Fehler beim Erzeugen des 3D-Device'); end; // *** D3DShutdown hier werden die Resourcen von D3D wieder freigegeben procedure TRendererD3D.D3DShutdown; begin if assigned(lpd3ddevice) then lpd3ddevice:=nil; if assigned(lpd3d) then lpd3d:=nil; end; procedure TRendererD3D.D3DInitScene; var hr : HRESULT; vbVertices : pByte; ProjMatrix : TD3DXMATRIX; begin HR := D3DXCreateFont(Self.lpd3ddevice, Self.FFont.Handle, Self.FDXFont); if HR <> S_OK then raise Exception.Create('Fehler beim Erzeugen des Font.'); if assigned(lpd3ddevice) then with lpd3ddevice do begin // Vertex Buffer für Würfel hr:=CreateVertexBuffer (sizeof(TCube), D3DUSAGE_WRITEONLY, // Nur Schreibzugriffe D3D8T_CUSTOMVERTEX, // Unser Vertex D3DPOOL_MANAGED, CubeVB); // Pointer zu unserem Buffer if FAILED(hr) then raise Exception.Create('Fehler beim Erstellen des Vertex Buffers'); // Nun kopieren wir unsere Vertizes in den Buffer // Wir müssen es zuvor mit Lock festhalten, um es bearbeiten zu können with CubeVB do begin hr:=Lock(0, // Offset, an dem wir beginnen 0, // Größe des locks ( 0 für alles ) vbVertices, // Wenn erfolgreich dann hier ablegen 0); // sonstige Flags if FAILED(hr) then raise Exception.Create('Fehler beim Locken des Vertex-Buffers'); // Hier wird der Vertexbuffer kopiert. Move(NormCube,vbVertices^,SizeOf(TCube)); // Und wieder loslassen Unlock; end; // Lighting abschalten SetRenderState(D3DRS_LIGHTING,0); // Z-Buffer beim Rendern einschalten SetRenderState(D3DRS_ZENABLE,D3DZB_TRUE); // Erstelle eine Projektionsmatrix D3DXMatrixPerspectiveFovLH(ProjMatrix, // Resultierende Matrix Fovy, // Öffnungswinkel des Sichtkegels 640/480, // Seitenverhältnis der Ansicht 1.0, // Mindeste Nähe 1000.0); // Maximal sichtbare Entfernung SetTransform(D3DTS_PROJECTION,ProjMatrix ); end; Animate:=true; end; procedure TRendererD3D.D3DKillScene; begin CubeVB:=nil; end; // Rendern der Szene procedure TRendererD3D.D3DRender; var WorldMatrix, ViewMatrix, ProjMatrix, TempMatrix : TD3DXMATRIX; WorldPoint : TD3DXVector3; ScrnPoint : TD3DXVector4; TXTRect : TRect; begin RotY:=RotY+Delta; // Rotation des Beobachters if assigned(lpd3ddevice) then with lpd3ddevice do begin Clear(0, // Wieviel Rechtecke löschen? 0 Löscht alle nil, // Pointer zu den Rechtecken. nil = Ganzer Bildschirm D3DCLEAR_TARGET or D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0), //Hintergrundfarbe schwarz 1, // Lösche ZBuffer ( Wir haben momentan noch keinen ) 0 ); if SUCCEEDED(BeginScene) then begin D3DXMatrixLookAtLH (ViewMatrix,D3DXVECTOR3(ViewDist*sin(Pi180*RotY), ViewHeight, ViewDist*cos(Pi180*RotY)), D3DXVECTOR3(0.0,0.0,0.0), D3DXVECTOR3(0.0,1.0,0.0)); SetTransform(D3DTS_VIEW,ViewMatrix); // Vertex Typ einstellen SetVertexShader(D3D8T_CUSTOMVERTEX); // Stream auf Vertexbuffer für Würfel setzen SetStreamSource(0,CubeVB,sizeof(TMyVertex)); SetRenderState(D3DRS_CULLMODE,D3DCULL_CCW); // Setze die Welt-Matrix für die Kiste etwas nach oben D3DXMatrixTranslation(WorldMatrix,0,0,0); SetTransform(D3DTS_WORLD,WorldMatrix); // Zeichnen der Kiste DrawPrimitive(D3DPT_TRIANGLELIST,0,12); EndScene; end; // Hier wird die Beschriftung gezeichnet ======================================= GetTransform(D3DTS_WORLD, WorldMatrix); GetTransform(D3DTS_PROJECTION, ProjMatrix); GetTransform(D3DTS_VIEW, ViewMatrix); D3DXMatrixMultiply(TempMatrix, WorldMatrix, ViewMatrix); D3DXMatrixMultiply(TempMatrix, TempMatrix, ProjMatrix); WorldPoint.x := NormCube[1].x; WorldPoint.y := NormCube[1].y; WorldPoint.z := NormCube[1].z; D3DXVec3Transform(ScrnPoint, WorldPoint, TempMatrix); TxtRect := Rect(Round(ScrnPoint.x), Round(ScrnPoint.y), 0, 0); Self.FDXFont.DrawTextA('Hallo', -1, TxtRect, DT_CALCRECT, 0); Self.FDXFont.DrawTextA('Hallo', -1, TxtRect, DT_LEFT, $FFFFFFFF); SetRenderState(D3DRS_ALPHATESTENABLE, 1); SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE); // ============================================================================= // Zeige Resultate auf dem Bildschirm Present(nil,nil,0,nil); end; end; procedure TRendererD3D.StartScene(Handle: HWND); begin D3DInit(Handle); D3DInitScene; D3DRender; end; procedure TRendererD3D.StopScene; begin D3DKillScene; D3DShutdown; end; procedure TRendererD3D.MyIdleHandler (Sender: TObject; var Done: Boolean); begin if Animate then D3DRender; Done:=false; end; end. Dabei müsste ich doch die Bildschirmkoordinaten rausbekommen, wenn ich Wordlmatrix * Viewmatrix * Projektionmatrix rechne, oder? Gruß, oXmoX |
Re: Projektion mit Direct3D berechnen
Hallo oXmoX,
eines vorneweg - ich habe keine Ahnung von Direct3D, aber ich habe früher selbst ein 2D segmentiertes Grafikpaket (in Anlehnung an GKS) implementiert. Bist du sicher, dass die Matrix-Multiplikation matWorld * matView * matProjection so korrekt ist? Ich wäre es nicht - schon gar nicht in dieser Reihenfolge. Ich würde erwarten, dass du diese Multiplikation gar nicht ausführen musst, weil sie nach dem Setzen von matView und matProjection intern erledigt werden sollte. Meine Erwartung wäre, dass du die aktuelle matWorld (warum GetTransform? normal hast du die doch immer vorrätig...) mit der Verschiebung für deinen Text multiplizierst, danach SetTransform() und das war es. Aber vielleicht geht das mit D3D alles ganz anders. Grüße vom marabu |
Re: Projektion mit Direct3D berechnen
Zitat:
durch die WordMatrix kann man natürlich Transformationen der 3D-Objekte bezüglich des World-Koordinatensystems vornehmen. Bei Text handelt es sich jedoch nicht um ein Objekt der "3D-Welt" und daher gibt man in der Funktion DrawTextA auch Bildschirmkoordinaten in Pixeln an, um diesen zu positionieren. Um aber von den Welt-Koordinaten des 3D-Objektes zu den Bildschirmkoordinaten zu kommen muss man die Multiplikation Wordmatrix * Viewmatrix * Projektionmatrix "manuell" ausführen und dann den Vektor der 3D-Koordinaten mit dieser Matrix transformaieren. Ob die Reihenfolge der Multiplikation so stimmt, da bin ich mir auch nicht 100%ig sicher. Zumal mir jemand erzählt hat, dass es Wordmatrix * Viewmatrix * Perspectivematrix heißen müßte. Daher frage ich mich, ob Projektionmatrix = Perspectivematrix? Eine Perspectivematrix gibt es in D3D nicht :( . Gruß, oXmoX |
Re: Projektion mit Direct3D berechnen
|
Re: Projektion mit Direct3D berechnen
Zitat:
Im Grunde will ich ja nur die Achsen eines Koordinaten-Systems mit "X", "Y", "Z" beschriften. |
Re: Projektion mit Direct3D berechnen
man kann sich doch vom IDirect3D/Draw nen DC zurückgeben lassen und dann so ganz normal die GDI funktionen zur Textausgabe benutzen
|
Re: Projektion mit Direct3D berechnen
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 03:44 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz