AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Multimedia Delphi Projektion mit Direct3D berechnen
Thema durchsuchen
Ansicht
Themen-Optionen

Projektion mit Direct3D berechnen

Offene Frage von "oXmoX"
Ein Thema von oXmoX · begonnen am 27. Jul 2005 · letzter Beitrag vom 28. Jul 2005
Antwort Antwort
oXmoX

Registriert seit: 8. Jun 2005
85 Beiträge
 
#1

Projektion mit Direct3D berechnen

  Alt 27. Jul 2005, 20:39
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:
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.
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.

Dabei müsste ich doch die Bildschirmkoordinaten rausbekommen, wenn ich

Wordlmatrix * Viewmatrix * Projektionmatrix

rechne, oder?

Gruß,
oXmoX
  Mit Zitat antworten Zitat
marabu

Registriert seit: 6. Apr 2005
10.109 Beiträge
 
#2

Re: Projektion mit Direct3D berechnen

  Alt 28. Jul 2005, 05:49
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
  Mit Zitat antworten Zitat
oXmoX

Registriert seit: 8. Jun 2005
85 Beiträge
 
#3

Re: Projektion mit Direct3D berechnen

  Alt 28. Jul 2005, 07:36
Zitat von marabu:
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.
Hallo marabu,

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
  Mit Zitat antworten Zitat
marabu

Registriert seit: 6. Apr 2005
10.109 Beiträge
 
#4

Re: Projektion mit Direct3D berechnen

  Alt 28. Jul 2005, 07:42
Hast du das Tutorial von Microsoft schon mal angesehen?

marabu
  Mit Zitat antworten Zitat
oXmoX

Registriert seit: 8. Jun 2005
85 Beiträge
 
#5

Re: Projektion mit Direct3D berechnen

  Alt 28. Jul 2005, 08:55
Zitat von marabu:
Hast du das Tutorial von Microsoft schon mal angesehen?

marabu
Habs mir grad mal durchgesehen. Allerdings wird hier mit 3D-Text gearbeitet. Ich könnte natürlich auch einen 3D-Text verwenden und diesen dann immer frontal zur Kamera ausrichten und zusätzlich den Z-Test ausschalten, damit der Text immer im Vordergrund ist. Allerdings fände ich diese Lösung nicht so schön und möchte daher zunächst bei meinem 2D-Text bleiben.

Im Grunde will ich ja nur die Achsen eines Koordinaten-Systems mit "X", "Y", "Z" beschriften.
  Mit Zitat antworten Zitat
barf00s
(Gast)

n/a Beiträge
 
#6

Re: Projektion mit Direct3D berechnen

  Alt 28. Jul 2005, 09:48
man kann sich doch vom IDirect3D/Draw nen DC zurückgeben lassen und dann so ganz normal die GDI funktionen zur Textausgabe benutzen
  Mit Zitat antworten Zitat
oXmoX

Registriert seit: 8. Jun 2005
85 Beiträge
 
#7

Re: Projektion mit Direct3D berechnen

  Alt 28. Jul 2005, 12:02
Zitat von barf00s:
man kann sich doch vom IDirect3D/Draw nen DC zurückgeben lassen und dann so ganz normal die GDI funktionen zur Textausgabe benutzen
Klar, aber damit kann ich auch keine 3D-Weltkoordinaten auf 2D-Bildschirmkoordinaten Transformieren. Die Ausgabe des Textes ist ja nicht das Problem. Die korrekte Positionierung kriege ich nur nicht hin. Der Text soll quasi immer am Objekt kleben bleiben.
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 06:54 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz