Jump to content

[Delphi] Przekształcanie współrzędnych 2d na 3d


Demon64

Recommended Posts

Witam! Mam taki mały problem, otóż próbuję napisać funkcję, która za parametry przyjmowałaby pozycję kursora myszy w 2d i położenie kamery 3d, a zwracałaby mi położenie kursora w 3d, no i nie wiem zbytnio jakimi wzorami się tu posłużyć.

 

Z założenia wygląda to mniej więcej tak:

KOD

type TPoint = record

x,y: integer;

end;

 

type TPoint3d = record

x,y,z: double;

end;

 

type TCamera = record

eye,lookat,up: TPoint3d;

end;

 

function Conv_2d_to_3d(punkt: TPoint; cam: TCamera): TPoint3d;

begin

Result.x:={wzór na przekształcenie na osi X};

Result.y:={wzór na przekształcenie na osi Y};

Result.z:=0;

end;

 

 

Parametr punkt to po prostu pixel, nad którym wisi kursor (x: 0-1023, y: 0-767, przy założeniu, że operujemy na rozdzielczości 1024x768), a parametr cam, to pozycja kamery 3d.

Kombinowałem już nad tym trochę, no i ciągle coś jest nie tak... Myślę, że gdyby kamera patrzyła centralnie w dół, to nie byłoby większych problemów z tym, ale kamera patrzy w taki sposób opisany wektorami:

KOD

kamera.eye := Point3d( 0.0, -11.0, 12.0 );

kamera.lookat := Point3d( 0.0, -3.0, 0.0 );

kamera.up := Point3d( 0.0, 1.0, 0.0 );

 

 

Czyli patrzy na scenę pod pewnym kątem...

Macie może jakieś pomysły? Albo jakieś gotowe wzory na takie przeliczenia?

Link to comment
Share on other sites

nie jestem specjalista od 3d wiec moze gadam bzdury, ale jak masz pozycje 2d kursora i kamere to mozesz wyrysowac linie (promien/prosta) i kursor jest na calej tel lini (od oka do pozycji 2d kursora). Tak wiec trzecia wspolrzedna znajdujesz przez kolizje promienia 'z' jakims obiektem, albo pozycje 'z' ustalasz w zaleznosci od odleglosci plaszczyzny po ktorej porusza sie kursor 2d od "oka".

Always Dark<br />u1_tt_logo.png banner-1.pngexFabula-banner.pngson_banner_ubersmall.jpg

Link to comment
Share on other sites

int screenWidth, screenHeight; //The screen resolution, i.e. 640 480
int mouseX, mouseY; //This is your mouse position in screen-coordinates
Matrix4x4 matWorld, matView, matProj; //These are the matrices you were using for setting the environment

Matrix4x4 mat;
Vertex4 mouseWorld; //Will store mouse position in world coordinates

mouseWorld.x = 1.0f + mouseX;
mouseWorld.y = 1.0f - mouseY; //This may change to 1 + mouseY if you're using OpenGL
mouseWorld.z = 0.0f;
mouseWorld.w = 1.0f;

mouseWorld.x /=  screenWidth;
mouseWorld.y /= -screenHeight; //Again the sign may change

mat = matWorld * matView * matProj; //Here we perform the matrix concatenation on our own
mat = mat.inverse(); //Now mat contains the inverse of that matrix, because we want to UNproject, not project. Google for "Matrix 4x4 inverse" if you don't know how to get the inverse matrix

//Un-project
mouseWorld = mouseWorld * mat; //If this doesn't work try mat * mouseWorld

/*
Alternatively you can do the matrix multiplication on your own:

Vertex4 tmp = mouseWorld;
mouseWorld.x = tmp.x * mat._11 + tmp.y * mat._21 + tmp.z * mat._31 + tmp.w * mat._41;
mouseWorld.y = tmp.x * mat._12 + tmp.y * mat._22 + tmp.z * mat._32 + tmp.w * mat._42;
mouseWorld.z = tmp.x * mat._13 + tmp.y * mat._23 + tmp.z * mat._33 + tmp.w * mat._43;
*/

//Now mouseWorld contains the position in world coordinates.

//Get the ray:
Vector3 dir; //Will contain the direction of the ray being casted by the mouse from the camera;

//Assumes GetCameraPosition() returns the camera's position;
dir = mouseWorld - GetCameraPosition();

 

Kod zaczerpnięty z: http://www.gamedev.net/community/forums/to...topic_id=526017 Wszystko opisane jest w komentarzach :P

Ot taka mini-strona moja po godzinach :)http://www.wnetrzekuchni.pl

Link to comment
Share on other sites

function Conv_2d_to_3d(punkt: TPoint; cam: TCamera): TPoint3d;
var mat: _D3DMatrix;
   mouseWorld,tmp: TVector4;
begin
 mouseWorld.x := 1.0 + punkt.X;
 mouseWorld.y := 1.0 - punkt.Y;
 mouseWorld.z := 0.0;
 mouseWorld.w := 1.0;

 mouseWorld.x := mouseWorld.x / Form1.ClientWidth;
 mouseWorld.y := mouseWorld.y / -Form1.ClientHeight;

 D3DXMatrixMultiply(mat,matWorld,matView);
 D3DXMatrixMultiply(mat,mat,matProj);

 D3DXMatrixInverse(mat,0,mat); // tu te zero to nie wiem dokładnie co znaczy pfDeterminant: PSingle

 tmp := mouseWorld;
 mouseWorld.x := tmp.x * mat._11 + tmp.y * mat._21 + tmp.z * mat._31 + tmp.w * mat._41;
 mouseWorld.y := tmp.x * mat._12 + tmp.y * mat._22 + tmp.z * mat._32 + tmp.w * mat._42;
 mouseWorld.z := tmp.x * mat._13 + tmp.y * mat._23 + tmp.z * mat._33 + tmp.w * mat._43;

 // tu miało być coś typu Result := mouseWorld - cam; ale takiego odejmowania Delphi nie może wykonać przez niezgodność typów
 Result.x := mouseWorld.x - cam.eye.x;
 Result.y := mouseWorld.y - cam.eye.y;
 Result.z := 0; // ta wartość będzie stała
end;

 

Kod zaczerpnięty z: http://www.gamedev.net/community/forums/to...topic_id=526017 Wszystko opisane jest w komentarzach :P

 

 

Nie wiem czy dobrze to sobie przepisałem na Delphi, ale coś jest nie tak, bo koordynaty jakie otrzymuję po tej zamianie są siedmiocyfrowe... Czyli o wiele za duże :/

 

Oto moja funkcja:

function Conv_2d_to_3d(punkt: TPoint; cam: TCamera): TPoint3d;
var mat: _D3DMatrix;
   mouseWorld,tmp: TVector4;
begin
 mouseWorld.x := 1.0 + punkt.X;
 mouseWorld.y := 1.0 - punkt.Y;
 mouseWorld.z := 0.0;
 mouseWorld.w := 1.0;

 mouseWorld.x := mouseWorld.x / Form1.ClientWidth;
 mouseWorld.y := mouseWorld.y / -Form1.ClientHeight;

 D3DXMatrixMultiply(mat,matWorld,matView);
 D3DXMatrixMultiply(mat,mat,matProj);

 D3DXMatrixInverse(mat,0,mat); // tu te zero to nie wiem dokładnie co znaczy pfDeterminant: PSingle

 tmp := mouseWorld;
 mouseWorld.x := tmp.x * mat._11 + tmp.y * mat._21 + tmp.z * mat._31 + tmp.w * mat._41;
 mouseWorld.y := tmp.x * mat._12 + tmp.y * mat._22 + tmp.z * mat._32 + tmp.w * mat._42;
 mouseWorld.z := tmp.x * mat._13 + tmp.y * mat._23 + tmp.z * mat._33 + tmp.w * mat._43;

 // tu miało być coś typu Result := mouseWorld - cam; ale takiego odejmowania Delphi nie może wykonać przez niezgodność typów
 Result.x := mouseWorld.x - cam.eye.x;
 Result.y := mouseWorld.y - cam.eye.y;
 Result.z := 0; // ta wartość będzie stała
end;

 

Co tu jest źle? Determinant? Czy może odejmowanie Result = mouseWorld - cam?

Dodam, że ten Determinant jak zmienię np. na wartość 1, to nie chce mnie kompilator puścić i wywala błąd Incompatible types 'Integer' and 'PSingle'...

Link to comment
Share on other sites

CYTATDeterminant

 

Wyznacznik macierzy.

 

CYTATDodam, że ten Determinant jak zmienię np. na wartość 1, to nie chce mnie kompilator puścić i wywala błąd Incompatible types 'Integer' and 'PSingle'...

Bo argumentem ma być wskaznik na float.

 

CYTATale takiego odejmowania Delphi nie może wykonać przez niezgodność typóww nowym śmiałoby przeszło :P gdyby przeciążyć operatory jak w cepie.

 

Szczerze mówiąc dokładnie nie analizowałem kodu źródłowego zarzuconego ale postaram się przetestować.

 

// tu te zero to nie wiem dokładnie co znaczy pfDeterminant: PSingle

Spróbuj zamiast 0 zamienić na nil ? Chociaż i tak pewnie to zostanie zamienione :/

Ot taka mini-strona moja po godzinach :)http://www.wnetrzekuchni.pl

Link to comment
Share on other sites

Darowałem sobie z tymi własnymi przeliczeniami póki co, gdyż znalazłem taką interesującą funkcję w DirectX służącą właśnie do takich przeliczeń współrzędnych 2d na 3d:

 

function Conv_2d_to_3d(punkt: TPoint; cam: TCamera): TPoint3d;
var vect_ok,v: _D3DVECTOR;
   view_port: _D3DVIEWPORT8;
begin
 v := D3DXVector3(punkt.X, punkt.Y, 0.0);

 Form1.c.Device.GetViewport(view_port);
 D3DXVec3Unproject(vect_ok, v, view_port, matProj, matView, matWorld);

 Result.x:=vect_ok.x;
 Result.y:=vect_ok.y;
 Result.z:=-8;
end;

 

Użyłem tej funkcji, ale coś nadal jest nie tak... Wygląda tak jakby na osi X robiło dobrze, ale za to na osi Y daje zbyt duże wartości i na dodatek szybko się one zmieniają w porównaniu do osi X...

 

function Conv_2d_to_3d(punkt: TPoint; cam: TCamera): TPoint3d;
var vect_ok,v: _D3DVECTOR;
   view_port: _D3DVIEWPORT8;
begin
 v := D3DXVector3(punkt.X, punkt.Y, 0.0);

 Form1.c.Device.GetViewport(view_port);
 D3DXVec3Unproject(vect_ok, v, view_port, matProj, matView, matWorld);

 Result.x:=vect_ok.x;
 Result.y:=vect_ok.y;
 Result.z:=-8;
end;

 

matProj, matView, matWorld - to są zmienne globalne macierzowe TD3DXMatrix

vect_ok - tu przypisywany jest wynik przeliczenia współrzędnych

v - przekazuje współrzędne punktu 2d

 

 

// EDIT: sprawdzałem przed chwilą i podczas zmieniania współrzędnej Z w zmiennej v z 0.0 na inne wartości, zmieniają się także otrzymywane wyniki przeliczeń z 2d do 3d nie wiem za bardzo do czego to służy... :/ Wyregulowałem to na 0.96 i w ostatniej linijce Result.z:=-8; zamieniłem na Result:=vect_ok.z; No i powiem, że teraz jak wstawię w tą wyliczoną współrzędną jakiś obiekt to się zgadza jego położenie na ekranie, jest równe z położeniem myszki, czyli wychodzi na to, że przekształca to pozycję myszki z 2d na pozycję 3d, ale w wyświetlanej płaszczyźnie. A mi zależy na tym, by wyświetlało go na wysokości z = -8 ale w takim samym miejscu jak pokazuje kursor na ekranie...

 

Oto 2 rysunki sytuacyjne, które pomogą się wam zorientować w sytuacji:

Rysunek sytuacyjny oś OY

Rysunek sytuacyjny oś OX

Link to comment
Share on other sites

Moim zdaniem zrobisz to tak(ale glowy nie daje):

obliczasz ten punkt 3d nazwijmy go p1, punkt 2d zapisujesz jako punkt 3d tak ze p2 = (2d.x, 2d.y, odleglosc pierwszego planu)

Teraz masz prosta w przestrzeni wyznaczona przez punkty p1 i p2 i mozesz obliczyc punkt p3 ktory znajduje sie na tej prostej i jest odalony od p2 o 8 jednostek.

 

EDIT: Eee.. zle p2 cza by jeszcze przemnozyc przez macierz projekcji zeby otrzymac wartosci z przedzialu . No i tez pytanie czy ta plaszczyzna na ktorej znajduje sie ten punkt 3d nie jest przednim planem, jesli tak to pewnie przez manipulacje pozostalymi macierzami bedziesz mogl przesunac ta plaszczyzne bardziej w glab.

 

To sa tylko moje przemyslenia i nie gwarantuje ze sa prawidlowe ;)

Link to comment
Share on other sites

Przed chwilą znów manipulowałem przekształconą współrzędną kursora 2d na 3d, by uzyskać pozycję Y obiektu na płaszczyźnie i oto rezultat:

 

function Conv_2d_to_3d(punkt: TPoint; cam: TCamera): TPoint3d;
var vect_ok,v: _D3DVECTOR;
   view_port: _D3DVIEWPORT8;
begin
 v := D3DXVector3(punkt.X, punkt.Y, 0.96);

 D3DXMatrixTranslation( matWorld, 0.0, 0.0, 0.0 );
 Form1.c.Device.GetViewport(view_port);
 D3DXVec3Unproject(vect_ok, v, view_port, matProj, matView, matWorld);

 Result.x := vect_ok.x;
 Result.Y := vect_ok.Y * ((cam.eye.Z+8) / Odl(vect_ok.Z,cam.eye.Z)); // obliczanie współrzędnej Y
 Result.z := -8;
end;
óĘ+óvwŁb(Ż<ŹŚĄś&śbxóvvŻ(4jWŚźdrŚź'ŹFŚzźs<!Ąjhh!ĘzKn'(ŹYz;3zxZ.Ę+zg        (r'jóyęr0iŁ^j0j'huŹŚr6$jgĘ*h'#bKjWx'j7jKh6Ą2KkĄlk-gG#kjG^bppŚgVć7F6eó&EFó6BVćCEC6ÓD6W&EC6Cf\"fV7EcC4EdT5D#fWu'CC4EdUu%CŚ&VvbŁC4EfV7F#2VćBVćBbC4EG&G&6ĆFEv&ĆBf&Óć2FWf6RvWEfWw'BfWu'BC4EfV35V&ŚV7BfV7EbfWu'BE&EfWrEv&ĆB&W7VBŁfV7E6ćWRłFfV7EĆ6ćWR&W7VBŁfV7E6ćWRłFfV7EĆ6ćWR&W7VBŁÓŚVćC

 

Wszystko działa pięknie ale tylko dla kamery położonej w punkcie (0.0, 0.0, Z), gdzie Z mogę mieć dowolne, a jak przesunę kamerę w inną pozycję to tu zaczynają się problemy. Może, żeby to lepiej zobrazować przedstawię wam screeny.

 

Kamera w pozycji (0.0, 0.0, 12.0):

 

Screen 1

Screen 2

Screen 3

Screen 4

Screen 5

 

Kamera przesunięta w inne miejsce:

 

Screen 1

Screen 2

Screen 3

Screen 4

Link to comment
Share on other sites

OK trochę się pobawiłem aktualnie funkcja wygląda tak:

 

function Conv_2d_to_3d(punkt: TPoint; cam: TCamera): TPoint3d;
var vect_ok,v: _D3DVECTOR;
   view_port: _D3DVIEWPORT8;
begin
 v := D3DXVector3(punkt.X, punkt.Y, 0.96);

 D3DXMatrixTranslation( matWorld, 0.0, 0.0, 0.0 );
 Form1.c.Device.GetViewport(view_port);
 D3DXVec3Unproject(vect_ok, v, view_port, matProj, matView, matWorld);

 Result.X := vect_ok.X * ((cam.eye.Z+8) / Odl(vect_ok.Z,cam.eye.Z));
 Result.Y := vect_ok.Y * ((cam.eye.Z+8) / Odl(vect_ok.Z,cam.eye.Z));
 Result.z := -8;
end;

z płaszczyzną, na której miał znajdować się szukany punkt i działa jak należy :D

Temat do zamknięcia :)

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...