Entwickler-Ecke

Multimedia / Grafik - 3d koordinaten in 2d umwandeln


chaoslion - So 12.03.06 16:08
Titel: 3d koordinaten in 2d umwandeln
Hallo!

Wie ihr aus der Überschrift lesen könnte hab ich im moment das Problem der Umwandlung von 3 Koordinaten (x,y,z) in die Bildschirmkoordinaten (x,y).
Ich hab folgende Umwandlungen versucht:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
function x3d2d(x,z:real):real;
begin
  //result:=(x+1*z); //funktioniert gut, aber probleme beim rotieren
  result:=ZOOMFAKTOR * (x / (1 + z)); //funktioniert überhaupt nicht
end;
function y3d2d(y,z:real):real;
begin
//  result:=(y+1*z); //funktioniert gut, aber probleme beim rotieren
  result:=ZOOMFAKTOR * (y / (1 + z) ); //funktioniert überhaupt nicht
end;


Das erste Bild zeigt komische Werte wenn ich die 2. Formel benutze.
Das 2. Bild zeigt wie es Aussieht mit der ersten Formel jedoch ist das rotieren falsch,
den das Koorinatensystem wird schief (3. bild ein wenig um y rotiert)..
[url=http://img59.imageshack.us/my.php?image=fehler12im.jpg]user defined image[/URL]
[url=http://img59.imageshack.us/my.php?image=ok17on.jpg]user defined image[/URL]
[url=http://img59.imageshack.us/my.php?image=ok1rotiertumy5ub.jpg]user defined image[/URL]


F34r0fTh3D4rk - So 12.03.06 16:48

wozu schreibst du eigentlich:

Delphi-Quelltext
1:
  (y + 1 * z)                    

wenns auch so geht:

Delphi-Quelltext
1:
y+z                    


?


chaoslion - So 12.03.06 16:53

weil 1 er teilungsfaktor ist, eigentlich war es vorher 0.5, das heisst das die z werte nur halb so groß sind wie die y oder x aber im moment steht 1 als platzhalter da.


F34r0fTh3D4rk - So 12.03.06 16:55

hast du dir das mal angeguckt ?

http://de.wikipedia.org/wiki/Parallelprojektion

wie wendest du deine funktion überhaupt an ?


chaoslion - So 12.03.06 17:15

so rufe ich sie auf:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
type 
 TVek3d=record
  x,y,z:real;
  end;

//...//

procedure TMATHE.ZeichnePunkt(pkt1:TVek3d);
begin
CANVAS.PutPixel(VNULLX+x3d2d(pkt1.x,pkt1.z),VNULLY-y3d2d(pkt1.y,pkt1.z),RED,fxBlend);
end;

VNULLX ^ VNULLY sind konstanten = 320 und 240, da ich von 640*480 als auflösung ausgehe
desweiteren soll er alles vom zentrum ausrichten
zb hast du x=20, y=20, z=30 welcher er dann "umrechnet" in 340, 260 und z halt durch formel..

putpixel..ist von einer Grafikengine..
red ist Farbkonstante
fxblend nen Effekt


F34r0fTh3D4rk - So 12.03.06 18:03

btw da ist n highlighting fehler, das .z wird als zahl gelesen Oo


delfiphan - So 12.03.06 19:28

Siehe Projektionsmatrix und homogene Koordinaten.

Bzw. Mit was für Koordinaten hast du es zu tun? Nimm mal für Zoomfaktor 400 und anstelle von "1" ebenfalls einen Wert in dieser Grössenordnung.


chaoslion - So 12.03.06 19:45

hab ich ja schon dennoch ist das koordinatensystem bei der rotation schief..


delfiphan - So 12.03.06 21:50

Je höher du diese beiden Werte setzst, desto näher solltest du eine Parallelprojektion erhalten. Wenn das nicht der Fall ist, dann ist eventuell etwas mit deiner Drehroutine falsch.


NCortex - So 12.03.06 22:11

hey, ich hatte ein ähnliches Problem. Ich hab das mit Matrixrechnung gelöst.

Kurze Vorerklärung:

TVektor, ist ein 3D Vektor mit (x,y,z)
TProjektionsMatrix ist eine 2x3 Matrix. also:

(x1,x2,x3)
(y1,y2,y3)

Die Projektionsmatrix bestimmt die Perspektive (Militär-,Isometrie, Dimetrie... etc):

Sie standaertprojektion sieht folgendermaßen aus:

(-0.5 , 1 , 0)
(-0,5 , 0 , 1)

fVR.mal() ist skalarprodukt. fVR.Vektor() gibt einen Vektor aus.


Delphi-Quelltext
1:
2:
3:
4:
5:
function TMatrixrechnung.Auf2DProjizieren(a:TVektor;P:TProjektionsMatrix):TPoint;
begin
result.X := round(fVR.mal(fVR.Vektor(P.x1,P.x2,P.x3),a));
result.Y := round(fVR.mal(fVR.Vektor(P.y1,P.y2,P.y3),a));
end;


der obige Code ist eiglich nur da um es dynamisch zu machen, du kannst auch einfach:


Delphi-Quelltext
1:
2:
V2d.x := -0.5 * V3d.x +  V3d.y;
V2d.x := -0.5 * V3d.x +  V3d.z;


benutzen.

wenn du noch fragen dazu hast, kann ich dir gern noch tiefgehender helfen.

mfg Ncortex


chaoslion - Mo 13.03.06 00:13

user profile iconNCortex hat folgendes geschrieben:


Delphi-Quelltext
1:
2:
V2d.x := -0.5 * V3d.x +  V3d.y;
V2d.x := -0.5 * V3d.x +  V3d.z;



hey!

Ist wahrscheinlich nur nen Schreibfehler, aber wieso berechnest Du 2x den x Wert?

Ich hab ja zur Darstellung eines Punktes das "Schulkoordinatensystem" genommen.
Das heißt der Punkt(x,y,z) wird so gezeichnet:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
     y |   .
       |  .
       | .
-------.---------- x
      .|
     . | 
   z.  |


korrekter Weise muss man doch von einer Zentralprojektion ausgehen, also das
der Benutzer in Richtung Z Achse schaut, oder?


Allesquarks - Mo 13.03.06 00:34

Wie hier schon erwähnt gibt es verschiedene Arten der Projektion Fluchtpunkt,senkrechte Projektion auf eine Ebene etc.
Aber beim Schulkoordinatensystem ist es für das Auge sowieso gewöhnungsbedürftig. Dort schaut der Benutzer keineswegs in z-Richtung. Und gerade weil dieses Darstellungssystem so zerrt auf jeden Fall immer erst die Koordinaten drehen und dann erneut abbilden. Und zum Abbildungsmaßstab:

| .
| .
| .
-----------------
|
|
|

z-Achse um die Hälfte gekürzt aber bei x-,y-Längenberechnung nochmal durch Wurzel zwei teilen.


delfiphan - Mo 13.03.06 02:15

NCortex: Deine Rechnung sieht etwas ungewöhnlich aus. Wenn man eine Projektion will muss man irgendwann mal durch die Tiefenkoordinate z teilen. Denn: Je grösser die Tiefenkoordinate z, desto kleiner soll das Objekt sichtbar sein. Wenn man durch z teilt, erhält man genau diesen Effekt: Je grösser z, desto kleiner die Abbildung.


F34r0fTh3D4rk - Mo 13.03.06 15:07

im grunde ist es auch eine 3x3 matrix, nur stehen in der letzten zeile lauter nullen, damit die z achse wegfällt.
man kann auch ebenso auf der x oder y achse projezieren.


NeoInDerMATRIX - Mo 13.03.06 20:55

Hi,

ich habe dazu mal gerade nen altes DOC auf meiner Platte gefunden! Evtl hilft es ja.

Damit habe ich das auch unter DOS mal hinbekommen.

Cu
Neo


NCortex - Mi 15.03.06 14:59

ähm... mir fällt grade auf, dass mein koordinatensystem anders ist.


bei mir ist z nach oben, y nach rechts und x die Tiefe. Daher auch 0.5 * x

das ist die standartperspektive, die man auch in der SChule benutzt. die tiefe einfach um die hälfte verkürzt dazu addieren.

hoffe das erklärt die Rechnung


NeoInDerMATRIX - Mi 15.03.06 21:44

Hi,

das mit dem Verkürtzt Rechnen ist doch aber wenn ich mich nicht irre bei der Perspektive nur so wenn der sicht winkel auf die Fläche ca. 45° sind oder irre ich mich da?

Cu
Neo


chaoslion - Do 16.03.06 15:51

jap 45°..er muss aber für ne korrekte betrachtung 90° sein

-----------------------------------------------------------
danke euch, hab mal nen bissle gegoogelt nach den begriffen die hier im thread so fielen
und hab ne lösung gefunden:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
function x3d2d(x,z:real):real;
begin
  result:=(x*ZOOMFAKTOR)/(z+ZOOMFAKTOR);
end;

function y3d2d(y,z:real):real;
begin
  result:=(y*ZOOMFAKTOR)/(z+ZOOMFAKTOR);
end;


wobei 100 <= ZOOMFAKTOR <= 400 liegt. und bitte nicht kleiner 100 sonst ÜBELST fischaugeneffekt.. :)

ich kann ja dann mal nen screen posten, falls der rest mit den graden auch geht..


delfiphan - Do 16.03.06 21:33

Dafür hättest du nicht googeln müssen, ich hatte dir die Lösung ja schon in meinem ersten Post gegeben ;)
user profile icondelfiphan hat folgendes geschrieben:
Nimm mal für Zoomfaktor 400 und anstelle von "1" ebenfalls einen Wert in dieser Grössenordnung.
(...)
Je höher du diese beiden Werte setzst, desto näher solltest du eine Parallelprojektion erhalten. Wenn das nicht der Fall ist, dann ist eventuell etwas mit deiner Drehroutine falsch.


F34r0fTh3D4rk - Do 23.03.06 20:53

ich bin gerade per zufall bei google auf was gutes gestoßen:

http://www.felix-colibri.com/papers/graphic/delphi_3d_designer/delphi_3d_designer.html

dort ist alles echt genial erklärt, objekte drehen, kamera drehen, etc ....


(ich sehe gerade, du schreibst das gleiche programm wie ich im moment, voerst ist die grafische umsetzung bei mir mit ogl was mir aber nicht so ganz gefällt)


chaoslion - Do 23.03.06 22:32

Die Seite sieht echt gut aus, bin noch nicht ganz fertig mit lesen..
naja ich stecke im Moment fest, ne simple Ebene zu zeichen, bzw die Fläche.
Ist noch nen bissle buggy :P
[url=http://img119.imageshack.us/my.php?image=maddeshading3cm.jpg]user defined image[/URL]

wenigstens klappt die rotation jetzt..


F34r0fTh3D4rk - Fr 24.03.06 21:34

ich bekomme es auch nicht hin, mein koordinatensystem steht auf dem kopf

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
function Projektion(v3d: TVektor3d): TVektor2d;
var
  r1, r2: TVektor3d;
  m3x3: TMatrix3x3;
begin
  m3x3[00] :=               0; m3x3[01] := 0; m3x3[02] := 0;
  m3x3[10] := - (sqrt(2) / 4); m3x3[11] := 1; m3x3[12] := 0;
  m3x3[20] := - (sqrt(2) / 4); m3x3[21] := 0; m3x3[22] := 1;

  matrixrowtovektor_3x3(m3x3, r1, 1);
  matrixrowtovektor_3x3(m3x3, r2, 2);
  
  result.x1 := skalarprodukt_v3d(v3d, r1);
  result.x2 := skalarprodukt_v3d(v3d, r2);
end;


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
var
  cube_3d: array [0..9of TVektor3d;
  cube_2d: array [0..9of TVektor2d;

procedure TForm1.Cube;
var
  i, j: integer;
begin
  image1.canvas.Pen.color := clblack;
  cube_3d[0].x1 :=  0; cube_3d[0].x2 :=  0;  cube_3d[0].x3 :=  0;
  cube_3d[1].x1 := 50; cube_3d[1].x2 :=  0;  cube_3d[1].x3 :=  0;
  cube_3d[2].x1 := 50; cube_3d[2].x2 := 50;  cube_3d[2].x3 :=  0;
  cube_3d[3].x1 :=  0; cube_3d[3].x2 := 50;  cube_3d[3].x3 :=  0;
  cube_3d[4].x1 :=  0; cube_3d[4].x2 :=  0;  cube_3d[4].x3 :=  0;
  cube_3d[5].x1 :=  0; cube_3d[5].x2 :=  0;  cube_3d[5].x3 := 50;
  cube_3d[6].x1 := 50; cube_3d[6].x2 :=  0;  cube_3d[6].x3 := 50;
  cube_3d[7].x1 := 50; cube_3d[7].x2 := 50;  cube_3d[7].x3 := 50;
  cube_3d[8].x1 :=  0; cube_3d[8].x2 := 50;  cube_3d[8].x3 := 50;
  cube_3d[9].x1 :=  0; cube_3d[9].x2 :=  0;  cube_3d[9].x3 := 50;
  for i := 0 to 9 do
    cube_2d[i] := Projektion(cube_3d[i]);
  image1.Canvas.MoveTo(image1.Width div 2 + round(cube_2d[0].x1), image1.Height div 2 + round(cube_2d[0].x2));
  for j := 1 to 9 do
    image1.canvas.LineTo(image1.Width div 2 + round(Cube_2d[j].x1), image1.Height div 2 + round(Cube_2d[j].x2));
end;

procedure TForm1.ODraw(x1, x2: double);
begin
  image1.Canvas.MoveTo(image1.Width div 2, image1.Height div 2);
  image1.Canvas.LineTo(image1.Width div 2 + round(x1), image1.Height div 2 + round(x2));
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  v3d_x1: TVektor3d;
  v2d_x1: TVektor2d;
  v3d_x2: TVektor3d;
  v2d_x2: TVektor2d;
  v3d_x3: TVektor3d;
  v2d_x3: TVektor2d;
begin
  image1.Canvas.pen.color := clred;
  v3d_x1.x1 := 200;
  v3d_x1.x2 := 0;
  v3d_x1.x3 := 0;
  v2d_x1 := Projektion(v3d_x1);
  ODraw(v2d_x1.x1, v2d_x1.x2);

  image1.Canvas.pen.color := clblue;
  v3d_x2.x1 := 0;
  v3d_x2.x2 := 100;
  v3d_x2.x3 := 0;
  v2d_x2 := Projektion(v3d_x2);
  ODraw(v2d_x2.x1, v2d_x2.x2);

  image1.Canvas.pen.color := clgreen;
  v3d_x3.x1 := 0;
  v3d_x3.x2 := 0;
  v3d_x3.x3 := 100;
  v2d_x3 := Projektion(v3d_x3);
  ODraw(v2d_x3.x1, v2d_x3.x2);
end;

die perspektive ist komisch, wie bekomme ich das korrekt ?

und wie zeichne ich den würfel einfacher, sodass ich nicht jede zeichenprozedur extra aufrufen muss, und zwar so, dass ich alle striche kriege ?

EDIT: nach langem gebastle:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
procedure TForm1.DrawCubeLine(a, b: integer);
begin
  image1.Canvas.MoveTo(image1.Width div 2 + round(cube_2d[a].x1), image1.Height div 2 + round(cube_2d[a].x2));
  image1.Canvas.LineTo(image1.Width div 2 + round(cube_2d[b].x1), image1.Height div 2 + round(cube_2d[b].x2));
end;

procedure TForm1.Cube;
var
  i, j: integer;
begin
  image1.canvas.Pen.color := clblack;
  cube_3d[0].x1 :=  0; cube_3d[0].x2 :=  0;  cube_3d[0].x3 :=  0;
  cube_3d[1].x1 :=  0; cube_3d[1].x2 := 50;  cube_3d[1].x3 :=  0;
  cube_3d[2].x1 := 50; cube_3d[2].x2 :=  0;  cube_3d[2].x3 :=  0;
  cube_3d[3].x1 := 50; cube_3d[3].x2 := 50;  cube_3d[3].x3 :=  0;
  cube_3d[4].x1 :=  0; cube_3d[4].x2 :=  0;  cube_3d[4].x3 := 50;
  cube_3d[5].x1 :=  0; cube_3d[5].x2 := 50;  cube_3d[5].x3 := 50;
  cube_3d[6].x1 := 50; cube_3d[6].x2 :=  0;  cube_3d[6].x3 := 50;
  cube_3d[7].x1 := 50; cube_3d[7].x2 := 50;  cube_3d[7].x3 := 50;
  for i := 0 to 7 do
    cube_2d[i] := Projektion(cube_3d[i]);
  DrawCubeLine(01);
  DrawCubeLine(02);
  DrawCubeLine(04);
  DrawCubeLine(13);
  DrawCubeLine(15);
  DrawCubeLine(23);
  DrawCubeLine(26);
  DrawCubeLine(37);
  DrawCubeLine(45);
  DrawCubeLine(46);
  DrawCubeLine(57);
  DrawCubeLine(67);
end;

aber mit der perspektive stimmt immer noch etwas nicht


chaoslion - Sa 25.03.06 08:46

also ich muss ehrlich sagen, ich hab das nicht mit ner Projektionsmatrix gemacht sondern so:

1. das koordinatensystem muss ja nach meiner berechnung ohne rotation so wie das 2d aussehen, also so:


Delphi-Quelltext
1:
2:
3:
4:
5:
|Y
|
|

Z-------X


du musst es drehen damit es nicht mehr auf dem kopf steht->meine drehrotine


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
    function Xrot(vek:TVek3d;winkel:real):TVek3d;
      begin
        result.x:=vek.x;
        result.y:=vek.y*cos(winkel)-vek.z*sin(winkel);
        result.z:=vek.y*sin(winkel)+vek.z*cos(winkel);
    end;

    function Yrot(vek:TVek3d;winkel:real):TVek3d;
      begin
        result.x:=vek.z*sin(winkel)+vek.x*cos(winkel);
        result.y:=vek.y;
        result.z:=vek.z*cos(winkel)-vek.x*sin(winkel);
      end;

    function Zrot(vek:TVek3d;winkel:real):TVek3d;
      begin
        result.x:=vek.x*cos(winkel)-vek.y*sin(winkel);
        result.y:=vek.x*sin(winkel)+vek.y*cos(winkel);
        result.z:=vek.z;
      end;

    function Convert3d2d(vek:TVek3d):TVek3d; //du kannst auch TVek2d nehmen
      begin
        vek:=Xrot(vek,TEMPX);                
        vek:=Yrot(vek,TEMPY);
        vek:=Zrot(vek,TEMPZ);

        result.x:=(vek.x*ZOOM)/(vek.z+ZOOM);
        result.y:=(vek.y*ZOOM)/(vek.z+ZOOM);
        result.z:=0;                          //musst dann z halt weglassen
      end;

//und zeichnen tue ich es so
 procedure ZeichneVektor(vek1,vek2:TVek3d;farbe:Cardinal);
      begin
        vek1:=Convert3d2d(vek1);
        vek2:=Convert3d2d(vek2);
        //line proc ist aus ner engine 
        //wenn du mit canvas von win arbeitest musst du
        //canvas.moveto(NULLX+vek1.x,NULLY-vek1.y);
        //und dann lineto(NULLX+vek2.x,NULLY-vek2.y);

        Canvas.Line(
        NULLX+vek1.x,
        NULLY-vek1.y,
        NULLX+vek2.x,
        NULLY-vek2.y,farbe,farbe,fxBlend);
      end;


F34r0fTh3D4rk - Sa 25.03.06 10:38

ich möchte aber gerne vektoren und matrizen nehmen, da das ein mathematisches programm wird und eben alles stimmig sein muss ^^, außerdem ist dann die dynamik größer