Kako postaviti pametne telefone i računala. Informativni portal
  • Dom
  • OS
  • Opća perspektivna transformacijska matrica. DirectX9 programiranje: rotirajući objekti

Opća perspektivna transformacijska matrica. DirectX9 programiranje: rotirajući objekti

Aksonometrija je paralelna projekcija. U tablici 3.3 navedene su prve matrice ortografskih projekcija na koordinatne ravnine dobivene iz njihovih definicija.

Tablica 3.3. Projektna transformacija i projekcijske matrice

Ortografska projekcija na XOY

Pravopisna projekcija na YOZ

Pravopisna projekcija na XOZ

Ortografska projekcija na ravninu x = str

Trimetrijska transformacijska matrica na ravnini XOY

Izometrijska transformacijska matrica u ravninu XOY

XOY izometrijska projekcijska matrica

Kosa projekcijska matrica na XOY

Besplatna projekcijska matrica na XOY

XOY Projekciona matrica ormarića

Perspektivna transformacijska matrica s jednom točkom nestajanja (ravnina slike okomita na os apscise)

Matrica perspektivne transformacije s jednom točkom nestajanja (ravnina slike okomita na ordinatu)

Perspektivna transformacijska matrica s jednom točkom nestajanja (ravnina slike okomita na primijenjenu os)

Perspektivna transformacijska matrica s dvije točke nestajanja (ravnina slike paralelna s ordinatom)

Matrica perspektivne transformacije s tri točke nestajanja (ravnina slike proizvoljnog položaja)

Izometrija, dimetrija i trimetrija dobivaju se kombiniranjem rotacija nakon kojih slijedi projekcija iz beskonačnosti. Ako trebate opisati projekciju na ravninu XOY, prvo morate transformirati rotaciju za kut oko ordinatne osi, zatim za kut u odnosu na os apscise. Tablica 3.3 prikazuje matricu trimetrijske transformacije. Da bi se dobila matrica dimetrične transformacije, u kojoj će, na primjer, koeficijenti izobličenja duž osi apscise i ordinate biti jednaki, odnos između kutova rotacije mora biti u skladu s ovisnošću

Odnosno, odabirom kuta , možete izračunati kut te odrediti matricu dimetrične projekcije. Za izometrijsku transformaciju, odnos ovih kutova pretvara se u strogo definirane vrijednosti, a to su:

Tablica 3.3 prikazuje matricu izometrijske transformacije kao i matricu izometrijske projekcije na ravninu XOY. Potreba za matricama prvog tipa leži u njihovoj upotrebi u algoritmima za uklanjanje nevidljivih elemenata.

U kosim projekcijama projicirane ravne linije tvore kut s ravninom projekcije koji se razlikuje od 90 stupnjeva. Tablica 3.3 prikazuje opću matricu kose projekcije na ravninu XOY, kao i slobodnu i matricu projekcije ormara, u kojoj:

Perspektivne projekcije (tablica 3.3) također su predstavljene perspektivnim transformacijama i perspektivnim projekcijama na ravninu XOY. V X, V Y i V Z su projekcijska središta - točke na odgovarajućim osi. –V X, –V Y, –V Z bit će točke u kojima se konvergiraju snopovi ravnih linija paralelnih s odgovarajućim osi.

Promatračev koordinatni sustav je lijevo koordinatni sustav (slika 3.3), u kojem je os z e usmjerena s gledišta prema naprijed, os x e usmjerena je udesno, a os y e prema gore. Takvo pravilo je usvojeno za podudarnost x e i y e osi s x s i y s osi na ekranu. Određivanje vrijednosti zaslonskih koordinata x s i y s za točku P dovodi do potrebe dijeljenja s koordinatom z e. Da bi se izgradio točan perspektivni pogled, potrebno je podijeliti s koordinatama dubine svake točke.

U tablici 3.4 prikazane su vrijednosti deskriptora vrha S (X, Y, Z) modela (slika 2.1), podvrgnutog rotacijskim transformacijama i izometrijskim transformacijama.

Tablica 3.4 Deskriptori vrhova modela

Originalni model

M (R (z, 90)) xM (R (y, 90))

Motor ne pokreće brod.
Brod ostaje na mjestu, i
motori pokreću svemir
Oko njega.

Futurama

Ovo je jedna od najvažnijih lekcija. Zamišljeno pročitajte barem osam puta.

Homogene koordinate

U prethodnim lekcijama pretpostavili smo da se vrh nalazi u koordinatama (x, y, z). Dodajmo još jednu koordinatu - w. Od sada ćemo imati vrhove na koordinatama (x, y, z, w)

Uskoro ćete shvatiti što je što, ali za sada uzmite to zdravo za gotovo:

  • Ako je w == 1 tada je vektor (x, y, z, 1) položaj u prostoru
  • Ako je w == 0 tada je vektor (x, y, z, 0) smjer.

Zapamtite ovo kao aksiom bez dokaza !!!

A što nam to daje? Pa, ništa za rotaciju. Ako zakrenete točku ili smjer, dobit ćete isti rezultat. Ali ako rotirate pomak (kada pomičete točku u određenom smjeru), onda se sve dramatično mijenja. Što znači "smjer pomaka"? Ništa posebno.

Homogene koordinate nam omogućuju rad s jednim uređajem za oba slučaja.

Transformacijske matrice

Uvod u matrice

Jednostavno rečeno, matrica je samo niz brojeva s fiksnim brojem redaka i stupaca.

Na primjer, matrica 2x3 bi izgledala ovako:

U 3D grafici gotovo uvijek koristimo matrice 4x4. To nam omogućuje transformaciju naših (x, y, z, w) vrhova. Vrlo je jednostavno - množimo vektor položaja s transformacijskom matricom.

Matrica * Vertex = Transformirani vrh

Nije tako strašno kao što izgleda. Pokažite lijevim prstom u a, a desnim prstom u x. Ovo će biti sjekira. Lijevi prst pomaknite do sljedećeg broja b, a desni prst dolje do sljedećeg broja y. Prošli smo. Još jednom - cz. I opet - dw. Sada zbrajamo sve rezultirajuće brojeve - ax + by + cz + dw. Dobili smo naš novi x. Ponovite ovo za svaki redak i dobit ćete novi vektor (x, y, z, w).

Međutim, ovo je prilično dosadna operacija, pa neka to obavi računalo umjesto nas.

U C ++ pomoću GLM biblioteke:

glm :: mat4 myMatrix;


glm :: vec4 myVector;



glm:: vec 4 transformiraniVektor = myMatrix * myVector; // Ne zaboravite na red !!! Ovo je izuzetno važno!!!

Na GLSL-u:

mat4 myMatrix;


vec4 mojVektor;


// ispunimo matricu i vektor našim vrijednostima ... ovo preskačemo


vec 4 transformiraniVektor = myMatrix * myVector; // baš kao u GLM-u

(Nešto mi se čini da ovaj dio koda niste kopirali u svoj projekt i da ga niste isprobali ... hajde, probajte, zanimljivo je!)

Matrica pomaka

Matrica pomaka je vjerojatno najjednostavnija matrica od svih. tu je ona:


Ovdje su X, Y, Z vrijednosti koje želimo dodati našem položaju vrha.

Dakle, ako trebamo pomaknuti vektor (10,10,10,1) za 10 točaka, na poziciji X, tada:
(Probajte sami, pa, molim vas!)

... I dobivamo (20,10,10,1) u homogenom vektoru. Kao što se nadam da se sjećate, 1 znači da je vektor pozicija, a ne smjer.

Sada pokušajmo transformirati smjer (0,0, -1,0) na isti način:

I na kraju smo dobili isti vektor (0,0, -1,0).
Kao što sam rekao, nema smisla pomicati smjer.

Kako to kodiramo?

U C ++ s GLM:

#uključiti // nakon


glm :: mat4 myMatrix = glm :: translate (10.0f, 0.0f, 0,0f);


glm :: vec4 myVector (10.0f, 10.0f, 10.0f, 0,0f);


glm:: vec 4 transformiraniVektor = myMatrix * myVector; // i kakav je rezultat?


A u GLSL-u: U GLSL-u tako rijetko tko. Najčešće se koristi funkcija glm :: translate (). Prvo kreiraju matricu u C ++, a zatim je pošalju u GLSL i već tamo rade samo jedno množenje:

vec4 transformedVector = myMatrix * myVector;

Matrica identiteta

Ovo je posebna matrica. Ona ne radi ništa. Ali to spominjem jer je važno znati da množenjem A s 1,0 dobije se A:

glm :: mat4 myIdentityMatrix = glm :: mat4 (1.0f);

Matrica skaliranja

Matrica skaliranja je također prilično jednostavna:

Dakle, ako želite udvostručiti vektor (položaj ili smjer, nije važno) u svim smjerovima:

A w koordinata se nije promijenila. Ako pitate: "Što je skaliranje smjera?" Nije često korisno, ali ponekad korisno.

(imajte na umu da skaliranje matrice identiteta s (x, y, z) = (1,1,1))

C ++:

// Koristiti#uključiti i#uključiti


glm :: mat4 myScalingMatrix = glm :: skala (2.0f, 2.0f, 2.0f);

Matrica rotacije

Ali ova matrica je prilično komplicirana. Stoga se neću zadržavati na detaljima njegove interne provedbe. Ako stvarno želite, bolje pročitajte (često postavljana pitanja o matricama i kvaternionima)

VS++:

// Koristiti#uključiti i#uključiti


glm :: vec3 myRotationAxis (??, ??, ??);


glm :: rotirati (kut_u_stupnjevima, moja os zakretanja);

Kompozitne transformacije

Sada znamo kako rotirati, pomicati i skalirati naše vektore. Bilo bi lijepo znati sve to iskombinirati. To se radi jednostavnim množenjem matrica jedna s drugom.

Transformirani vektor = TranslationMatrix * RotationMatrix * ScaleMatrix * OriginalVector;

I opet red!!! Prvo morate promijeniti veličinu, zatim se pomicati i tek onda pomicati.

Ako transformacije primijenimo drugim redoslijedom, nećemo dobiti isti rezultat. Probaj:

  • Napravite korak naprijed (nemojte srušiti računalo sa stola) i skrenite lijevo
  • Skrenite lijevo i napravite jedan korak naprijed.

Da, uvijek morate zapamtiti redoslijed radnji kada upravljate, na primjer, likom igre. Prvo, ako je potrebno, izvršite skaliranje, zatim postavite smjer (rotaciju) i zatim se pomaknite. Pogledajmo mali primjer (uklonio sam rotaciju kako bih olakšao izračune):

Pogrešan način:

  • Premjestite brod na (10,0,0). Njegov centar je sada 10 X od centra.
  • Povećavamo veličinu našeg broda 2 puta. Svaka koordinata se množi s 2 u odnosu na centar, koji je daleko ... I kao rezultat, dobivamo brod potrebne veličine, ali na poziciji 2 * 10 = 20. Što nije baš ono što smo željeli.

Pravi put:

  • Povećavamo veličinu broda za 2 puta. Sada imamo veliki brod u centru.
  • Pomičemo brod. Veličina broda se nije promijenila i nalazi se na pravom mjestu.
Množenje matrice-matrice radi se na isti način kao i množenje matrice-vektora. Nećemo ulaziti u detalje, ali neka znatiželjnici pročitaju iz specijaliziranih izvora. Samo ćemo se osloniti na GLM biblioteku.
VS ++:
glm :: mat4 myModelMatrix = myTranslationMatrix * myRotationMatrix * myScaleMatrix;
glm :: vec4 myTransformedVector = myModelMatrix * myOriginalVector;
Na GLSL-u:
mat4 transformacija = mat2 * mat1;
vec4 out_vec = transform * in_vec;

Model, pogled i projekcijske matrice

Ilustracije radi, pretpostavit ćemo da već znamo nacrtati u OpenGL-u naš omiljeni 3D model Blendera - glavu majmuna Suzanne.

Matrice modela, pogleda i projekcije vrlo su prikladna metoda za odvajanje transformacija. Ne morate ih koristiti ako to stvarno želite (nismo ih koristili u lekcijama 1 i 2). Ali toplo preporučam da ih koristite. Samo što ih gotovo sve 3D biblioteke, igre itd. koriste za odvajanje transformacija.

Model Matrix

Ovaj model, poput našeg voljenog trokuta, definiran je skupom vrhova. Koordinate X, Y, Z određuju se u odnosu na središte objekta. Dakle, ako se vrh nalazi na koordinatama (0,0,0), onda je u središtu cijelog objekta

Sada možemo premjestiti naš model. Na primjer, zato što korisnik njime upravlja pomoću tipkovnice i miša. Vrlo je jednostavno za napraviti: skaliranje * rotiranje * pomicanje i to je to. Primjenjujete svoju matricu na sve vrhove u svakom okviru (u GLSL-u, a ne C++) i sve se kreće. Sve što se ne miče nalazi se u središtu “svijeta”.

Vrhovi se nalaze u svjetskom prostoru Crna strelica na slici pokazuje kako idemo iz prostora modela u svjetski prostor (Svi vrhovi su specificirani u odnosu na središte modela, a čelik u odnosu na središte svijeta)

Ova se transformacija može prikazati sljedećim dijagramom:


View Matrix

Pročitajmo opet citat iz futurame:

“Motor ne pokreće brod. Brod ostaje na mjestu, a motori pokreću svemir oko njega."


Isto se može primijeniti i na kameru. Ako želite fotografirati planinu iz bilo kojeg kuta, možete pomaknuti kameru ... ili planinu. To je nemoguće u stvarnom životu, ali je vrlo jednostavno i praktično u računalnoj grafici.

Prema zadanim postavkama, naša kamera je u središtu svjetskih koordinata. Da bismo pomaknuli naš svijet, moramo stvoriti novu matricu. Na primjer, našu kameru trebamo pomaknuti 3 jedinice udesno (+ X). To je isto kao pomicanje cijelog svijeta 3 jedinice ulijevo (-X). I dok vam se mozak tope, pokušajmo:


// Koristi #include i #uključiti


glm :: mat4 ViewMatrix = glm :: translate (-3.0f, 0,0f, 0,0f);

Slika ispod to pokazuje: krećemo se iz prostora svijeta (svi vrhovi su postavljeni u odnosu na središte svijeta kao što smo učinili u prethodnom odjeljku) u prostor kamere (svi vrhovi su postavljeni u odnosu na kameru).

I prije nego vam glava potpuno eksplodira, pogledajte ovu sjajnu funkciju našeg dobrog starog GLM-a:

glm :: mat4 CameraMatrix = glm :: LookAt (


položaj kamere, // Položaj kamere u svjetskim koordinatama


cameraTarget, // točka koju želimo gledati u svjetskim koordinatama


upVektor// najvjerojatnije glm:: vec3 (0,1,0) i (0, -1,0) će pokazati sve naopako, što je ponekad i cool.


Evo ilustracije gore navedenog:


No, na našu radost, to nije sve.

Projekciona matrica

Sada imamo koordinate u prostoru kamere. To znači da će se nakon svih ovih transformacija, čiji vrh ima sreću biti na x == 0 i y == 0, prikazati u središtu ekrana. Ali ne možemo samo koristiti koordinate X, Y da shvatimo gdje nacrtati vrh: udaljenost do kamere (Z) također se mora uzeti u obzir! Ako imamo dva vrha, onda će jedan od njih biti bliži središtu ekrana od drugog, budući da ima veću Z koordinatu.

To se zove perspektivna projekcija:


I na našu sreću, matrica 4x4 također može predstavljati transformacije perspektive:

glm :: mat4 projectionMatrix = glm :: perspektiva (


FoV, // Horizontalno vidno polje u stupnjevima. Ili veličinaaproksimacije. Kakokao da « leće» nafotoaparat. Obično između 90 (super širok, poput ribljeg oka) i 30 (kao mala špijunska stakla)


4.0 f / 3.0 f, // Omjer širine i visine. Ovisi o veličini vašeg prozora. Na primjer, 4/3 == 800/600 == 1280/960 zvuči poznato, zar ne?


0.1 f, // Near clipping field. Treba ga postaviti što je moguće veće, inače će biti problema s preciznošću.


100.0 f // Daleko polje za izrezivanje. Treba ga držati što manjim.


);

Ponovimo ono što smo sada napravili:

Udaljili smo se od prostora kamere (svi vrhovi su postavljeni u koordinatama u odnosu na kameru) u homogeni prostor (svi vrhovi su u koordinatama male kocke (-1,1). Sve u kocki je na ekranu.)

I konačni dijagram:


Evo još jedne slike da bude jasnije što se događa kada pomnožimo sve ove gluposti s projekcijskom matricom. Prije množenja s matricom projekcije, imamo plave objekte definirane u prostoru kamere i crveni objekt koji predstavlja vidno polje kamere: prostor koji pada u objektiv kamere:

Nakon množenja s matricom projekcije, dobivamo sljedeće:

Na prethodnoj slici vidno polje se pretvorilo u savršenu kocku (s koordinatama vrhova od -1 do 1 u svim osama.), a svi objekti su deformirani u perspektivi. Svi plavi objekti koji su blizu kamere postali su veliki, a oni dalje - mali. Isto kao u životu!

Evo pogleda koji imamo iz "objektiva":

Međutim, ona je kvadratna i potrebno je primijeniti još jednu matematičku transformaciju kako bi slika odgovarala veličini prozora.

Za rotiranje objekata (ili kamere) potrebna je ozbiljna matematička osnova uz pomoć koje će se izračunati koordinate svih objekata pri prikazu na "ravnom" ekranu računala. Odmah želim reći da se ne trebate plašiti, sve matematičke knjižnice su već napisane za nas, samo ćemo ih koristiti. U svakom slučaju, sljedeći tekst nije potrebno preskočiti, bez obzira na razinu znanja matematike.

1. Matrice, opći pojmovi

Što su matrice? Podsjećamo na višu matematiku: matrica ¬ je skup brojeva s prethodno poznatom dimenzijom redaka i stupaca.

Matrice se mogu zbrajati, množiti brojem, množiti jedna s drugom i mnoge druge zanimljive stvari, ali ovaj trenutak ćemo preskočiti, jer dovoljno je detaljno opisano u bilo kojem udžbeniku više matematike (udžbenici se mogu pretraživati ​​na google.com). Koristit ćemo matrice kao programeri, popunjavamo ih i kažemo što s njima, sve će izračune izvoditi Direct3D matematička biblioteka, tako da morate uključiti modul zaglavlja d3dx9.h (i biblioteku d3dx9.lib) u projekt.

Naš zadatak je stvoriti objekt, t.j. ispuniti matricu koordinatama vrhova objekta. Svaki vrh je vektor (X, Y, Z) u 3D prostoru. Sada, da bismo izvršili neku radnju, trebamo uzeti naš objekt (tj. matricu) i pomnožiti s matricom transformacije, rezultat ove operacije je novi objekt specificiran u obliku matrice.

Postoje tri glavne matrice definirane i korištene u Direct3D-u: matrica svijeta, matrica pogleda i matrica projekcije. Razmotrimo ih detaljnije.

Svjetska matrica- omogućuje rotaciju, transformaciju i skaliranje objekta, a također svakom objektu daje vlastiti lokalni koordinatni sustav.

Funkcije za rad sa svjetskom matricom:

  • D3DXMatrixRotationX (), D3DXMatrixRotationY (), D3DXMatrixRotationZ () - rotacija točke oko jedne od osi;
  • D3DXMatrixTranslation () - premjestiti točku na drugu poziciju;
  • D3DXMatrixScale () - skaliranje.

    View Matrix- definira mjesto kamere za gledanje scene i može se sastojati od bilo koje kombinacije emitiranja i rotacije.
    D3DXMatrixLookAtLH () i D3DXMatrixLookAtRH () definiraju položaj kamere i kut gledanja za ljevoruki i desnoruki koordinatni sustav.

    Projekciona matrica- stvara projekciju 3D scene na zaslon monitora. Transformira objekt, pomiče ishodište prema naprijed i definira prednju i stražnju ravninu izrezivanja.

    Ispunjavanjem ovih matrica i izvođenjem transformacija stvarate trodimenzionalnu scenu u kojoj dobivate mogućnost pomicanja, rotiranja, zumiranja, uklanjanja i obavljanja drugih radnji na objektima, ovisno o vašim potrebama.

    2. Stvaranje objekta

    Izrađujemo novi projekt, sličan prvom. Prije nego što nastavimo komplicirati naš kod, razbijmo ga na dijelove radi bolje čitljivosti koda. Logično je naš projekt podijeliti u tri komponente:
    1. Windows prozor (inicijalizacija prozora, poruke, ...)
    2. 3D inicijalizacija (učitavanje koordinata objekta, brisanje resursa, ...)
    3. Renderiranje scene (matrice, primitivi za crtanje,...)
    Kao rezultat, imat ćemo 3 datoteke - window.cpp, init3d.h, render.h sa sljedećim sadržajem: init3d.h- prenosimo globalne varijable i strukture, deklaracije funkcija, funkcije InitDirectX (), InitBufferVertex (), Destroy3D () render.h- prenosimo funkciju RenderScene (), sve što ostane dodiruje glavni prozor, bit će to datoteka - prozor.cpp.

    Dodavanje datoteke zaglavlja i biblioteke za korištenje matričnih funkcija

    #uključiti // ili C: \ DXSDK \ Include \ d3dx9.h #pragma komentar (lib, "d3dx9.lib") // ili C: \\ DXSDK \\ Lib \\ d3dx9.lib

    Potrebne su nam i standardne funkcije za rad s vremenom, pa uključujemo odgovarajuću datoteku zaglavlja:

    #uključiti

    Promijenimo format prikaza vrhova:

    #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ / D3DFVF_DIFFUSE) strukturirati CUSTOMVERTEX ( Plutati x, y, z; DWORD boja; );

    Koristit ćemo nekonvertirani tip vrha, budući da transformacije će se vršiti matricama.
    Promijenite kod funkcije InitDirectX (). Ovoj funkciji mora se dodati postavka dvaju načina prikaza.
    Isključite način izrezivanja tako da kada rotirate možete vidjeti sve strane objekta:

    PDirectDevice-> SetRenderState (D3DRS_CULLMODE, D3DCULL_NONE);

    Trenutno ne koristimo rasvjetu, već bojimo vrhove u određenu boju, pa gasimo rasvjetu:

    PDirectDevice-> SetRenderState (D3DRS_LIGHTING, FALSE);

    Pojednostavimo svoje srce predstavljajući ga kao tri trokuta. Koristit ćemo lokalni koordinatni sustav.


    CUSTOMVERTEX stVertex = ((-1.0f, 0.5f, 0.0f, 0x00ff0000), (-0.5f, 1.0f, 0.0f, 0x00ff0000), (0.0f, 0.5f, 0.0f, 0x0.0),0. f, 0.0f, 0x000000ff), (0.5f, 1.0f, 0.0f, 0x000000ff), (1.0f, 0.5f, 0.0f, 0x000000ff), (-1.0f, 0.5f, 0.0f, 0.0). f, 0,5f, 0,0f, 0x0000ff00), (0,0f, -1,0f, 0,0f, 0x0000ff00),);

    3. Kreiranje transformacijskih matrica

    Upišimo u datoteku render.h funkciju SetupMatrix () u kojoj će se odvijati sve radnje na matricama.

    Kreirajmo matrice:

  • D3DXMATRIX MatrixWorld; - svjetska matrica
  • D3DXMATRIX MatrixView; - matrica oblika
  • D3DXMATRIX MatrixProjection; - projekcijska matrica
    Postavljanje matrice svijeta

    Da bi se objekt rotirao potrebno je dobiti sistemsko vrijeme i svaki "trenutak" mijenjati kut između lokalnog i svjetskog koordinatnog sustava. Rotirati ćemo oko X osi, pa koristimo funkciju D3DXMatrixRotationX. Nakon izračunavanja svjetske matrice, morate primijeniti njezine vrijednosti pomoću funkcije SetTransform:

    UINT iTime = timeGetTime ()% 5000; FLOAT FAngle = iTime * (2.0f * D3DX_PI) /5000.0f; D3DXMatrixRotationX (& MatrixWorld, fAngle); pDirectDevice-> SetTransform (D3DTS_WORLD, & MatrixWorld); Postavljanje matrice prikaza

    Postavite kameru na pravo mjesto i usmjerite je prema objektu

  • D3DXMatrixLookAtLH (& MatrixView, - rezultat funkcije
  • & D3DXVECTOR3 (0.0f, 0.0f, -8.0f), - točka na kojoj se kamera nalazi
  • & D3DXVECTOR3 (0.0f, 0.0f, 0.0f), - točka u kojoj gledamo
  • & D3DXVECTOR3 (0,0f, 1,0f, 0,0f)); - vrh objekta

    Nakon izračuna, morate primijeniti dobivene vrijednosti.

  • Motor ne pokreće brod. Brod ostaje na mjestu, a motor pomiče svemir u odnosu na njega.

    Ovo je vrlo važan dio lekcija, svakako ga pročitajte nekoliko puta i dobro razumite.

    Homogene koordinate

    Do sada smo 3-dimenzionalnim vrhovima operirali kao (x, y, z) trojke. Uvedimo još jedan parametar w i operirajmo s vektorima oblika (x, y, z, w).

    Zauvijek zapamti to:

    • Ako je w == 1, tada je vektor (x, y, z, 1) pozicija u prostoru.
    • Ako je w == 0, tada je vektor (x, y, z, 0) smjer.

    Što nam to daje? Ok, ovo ništa ne mijenja za rotaciju, jer u slučaju rotacije točke iu slučaju rotacije vektora smjera, dobivate isti rezultat. Međutim, u slučaju prijenosa postoji razlika. Pomicanjem vektora smjera dobit će isti vektor. Kasnije ćemo se detaljnije zadržati na tome.

    Homogene koordinate nam omogućuju rad s vektorima u oba slučaja koristeći jednu matematičku formulu.

    Transformacijske matrice

    Uvod u matrice

    Najlakši način da zamislite matricu je kao niz brojeva, sa strogo definiranim brojem redaka i stupaca. Na primjer, matrica 2x3 izgleda ovako:

    Međutim, u 3D grafici ćemo koristiti samo 4x4 matrice koje će nam omogućiti transformaciju naših vrhova (x, y, z, w). Transformirani vrh rezultat je množenja matrice sa samim vrhom:

    Matrica x Vertex (tim redoslijedom !!) = Transformacija. vrh

    Prilično jednostavno. Koristit ćemo ovo prilično često, pa ima smisla dati upute računalu da to učini:

    U C ++ koristeći GLM:

    glm :: mat4 myMatrix; glm :: vec4 myVector; glm :: // Obratite pažnju na narudžbu! On je važan!

    Na GLSL-u:

    mat4 myMatrix; vec4 mojVektor; // Ne zaboravite ispuniti matricu i vektor traženim vrijednostima ovdje vec4 transformedVector = myMatrix * myVector; // Da, ovo je vrlo slično GLM-u :)

    Pokušajte eksperimentirati s ovim isječcima.

    Prijenosna matrica

    Matrica prijenosa izgleda ovako:

    gdje su X, Y, Z vrijednosti koje želimo dodati našem vektoru.

    Dakle, ako želimo pomaknuti vektor (10, 10, 10, 1) za 10 jedinica u smjeru X, dobivamo:

    … Dobivamo (20, 10, 10, 1) homogeni vektor! Ne zaboravite da 1 u parametru w znači položaj, a ne smjer, a naša transformacija nije promijenila činjenicu da radimo s položajem.

    Pogledajmo sada što se događa ako je vektor (0, 0, -1, 0) smjer:

    ... i dobivamo naš izvorni vektor (0, 0, -1, 0). Kao što je već rečeno, vektor s parametrom w = 0 ne može se prenijeti.

    A sada je vrijeme da to premjestite u kod.

    U C ++, s GLM:

    #uključiti // poslije glm :: mat4 myMatrix = glm :: translate (glm :: mat4 (), glm :: vec3 (10,0 f, 0,0 f, 0,0 f)); glm :: vec4 myVector (10,0 f, 10,0 f, 10,0 f, 0,0 f); glm :: vec4 transformedVector = myMatrix * myVector;

    Na GLSL-u:

    vec4 transformedVector = myMatrix * myVector;

    Zapravo, to nikada nećete učiniti u shaderu, najčešće ćete izvršiti glm :: translate () u C ++ da izračunate matricu, proslijedite je GLSL-u i u shaderu izvršite množenje.

    Jedinična matrica

    Ovo je posebna matrica koja ne radi ništa, ali mi je diramo, jer je važno zapamtiti da A pomnoženo s 1,0 daje A:

    U C ++:

    glm :: mat4 myIdentityMatrix = glm :: mat4 (1.0 f);

    Matrica skaliranja

    Također izgleda jednostavno:

    Dakle, ako želite primijeniti vektorsko skaliranje (položaj ili smjer - nije važno) za 2,0 u svim smjerovima, tada trebate:

    Imajte na umu da se w ne mijenja, a također imajte na umu da je matrica identiteta poseban slučaj matrice skaliranja s faktorom skale 1 na svim osi. Također, matrica identiteta je poseban slučaj matrice prijenosa, gdje je (X, Y, Z) = (0, 0, 0), respektivno.

    U C ++:

    // dodaj #include i #uključiti glm :: mat4 myScalingMatrix = glm :: mjerilo (2,0 f, 2,0 f, 2,0 f);

    Matrica rotacije

    Teže nego što je prethodno raspravljano. Ovdje ćemo preskočiti detalje jer ne morate biti sigurni za svakodnevnu upotrebu. Za više informacija, možete pratiti poveznicu Matrices and Quaternions FAQ (prilično popularan resurs i možda je vaš jezik tamo dostupan)

    U C ++:

    // dodaj #include i #uključiti glm :: vec3 myRotationAxis (??, ??, ??); glm :: rotirati (kut_u_stupnjevima, moja os zakretanja);

    Spajanje transformacija

    Dakle, sada možemo rotirati, prevesti i skalirati naše vektore. Sljedeći korak bi bilo lijepo kombinirati transformacije, koje se implementiraju pomoću sljedeće formule:

    Transformirani vektor = TranslationMatrix * RotationMatrix * ScaleMatrix * OriginalVector;

    PAŽNJA! Ova formula zapravo pokazuje da se prvo vrši skaliranje, zatim rotacija i tek zadnje je prijevod. Ovako funkcionira množenje matrice.

    Obavezno zapamtite kojim redoslijedom se sve to radi, jer redoslijed je stvarno važan, na kraju to možete i sami provjeriti:

    • Napravite korak naprijed i skrenite lijevo
    • Skrenite lijevo i napravite korak naprijed

    Razliku je jako važno razumjeti jer ćete se s tim stalno suočavati. Na primjer, kada radite s likovima igre ili nekim objektima, uvijek prvo napravite skaliranje, zatim rotirajte i tek onda prenesite.

    Zapravo, gornji redoslijed je ono što vam je obično potrebno za likove i druge stavke koje se mogu igrati: prvo ga povećajte ako je potrebno; zatim postavite njegov smjer i zatim ga pomaknite. Na primjer, za model broda (zaokreti su uklonjeni radi jednostavnosti):

    • Pogrešan način:
      • Premjestite brod na (10, 0, 0). Njegovo središte sada je 10 jedinica od početka.
      • Svoj brod povećavate 2x. Svaka koordinata se množi sa 2 "u odnosu na original", što je daleko... Dakle, nađete se u velikom brodu, ali njegovo središte je 2 * 10 = 20. Nije ono što ste htjeli.
    • Pravi put:
      • Svoj brod povećavate 2x. Dobivate veliki brod u središtu izvora.
      • Vi nosite svoj brod. I dalje je iste veličine i na ispravnoj udaljenosti.

    U C ++, s GLM:

    glm :: mat4 myModelMatrix = myTranslationMatrix * myRotationMatrix * myScaleMatrix; glm :: vec4 myTransformedVector = myModelMatrix * myOriginalVector;

    Na GLSL-u:

    mat4 transformacija = mat2 * mat1; vec4 out_vec = transform * in_vec;

    Matrice svijeta, pogleda i projekcije

    Za ostatak ovog tutoriala pretpostavit ćemo da znamo kako prikazati naš omiljeni 3D model iz Blendera - majmun Suzanne.

    Matrice svijeta, pogleda i projekcije prikladan su alat za odvajanje transformacija.

    Svjetska matrica

    Ovaj model, baš kao i naš crveni trokut, postavljen je skupom vrhova čije su koordinate postavljene u odnosu na središte objekta, tj. vrh s koordinatama (0, 0, 0) bit će u središtu predmet.

    Zatim bismo željeli premjestiti naš model, budući da igrač njime upravlja pomoću tipkovnice i miša. Sve što radimo je skaliranje, zatim rotiranje i prevođenje. Te se radnje izvode za svaki vrh, u svakom okviru (izvode se u GLSL-u, a ne u C ++!) I tako se naš model kreće po ekranu.

    Sada su naši vrhovi u svjetskom prostoru. To je prikazano crnom strelicom na slici. Prešli smo iz prostora objekata (svi vrhovi su relativni u odnosu na središte objekta) u svjetski prostor (svi vrhovi su relativni u odnosu na središte svijeta).

    Shematski je prikazano kako slijedi:

    Pregledna matrica

    Da ponovno citiram Futuramu:

    Motor ne pokreće brod. Brod ostaje na istom mjestu, a motor pomiče svemir oko njega.

    Pokušajte to zamisliti u odnosu na kameru. Na primjer, ako želite fotografirati planinu, onda ne pomičete kameru, već pomičete planinu. To nije moguće u stvarnom životu, ali je nevjerojatno jednostavno u računalnoj grafici.

    Dakle, u početku je vaša kamera u središtu svjetskog koordinatnog sustava. Da biste pomaknuli svijet, morate ući u drugu matricu. Recimo da želite pomaknuti kameru za 3 jedinice DESNO (+ X), što je ekvivalent pomicanju cijelog svijeta za 3 jedinice LIJEVO (-X). U kodu to izgleda ovako:

    // Dodaj #include i #uključiti glm :: mat4 ViewMatrix = glm :: translate (glm :: mat4 (), glm :: vec3 (- 3,0 f, 0,0 f, 0,0 f));

    Opet, slika ispod to u potpunosti ilustrira. Prešli smo sa svjetskog koordinatnog sustava (svi vrhovi su specificirani u odnosu na središte svjetskog sustava) na koordinatni sustav kamere (svi vrhovi su specificirani u odnosu na kameru):

    I dok vaš mozak to probavlja, mi ćemo pogledati funkciju koju nam pruža GLM, odnosno glm :: LookAt:

    glm :: mat4 CameraMatrix = glm :: LookAt (cameraPosition, // Položaj kamere u svjetskom prostoru cilj kamere, // Označava kamo gledate u svjetskom prostoru upVektor // Vektor usmjeren prema gore. Obično (0, 1, 0));

    A evo i dijagrama koji pokazuje što radimo:

    Međutim, ovo nije kraj.

    Projekciona matrica

    Dakle, sada smo u prostoru kamere. To znači da će vrh koji prima koordinate x == 0 i y == 0 biti prikazan u sredini ekrana. Međutim, kod prikaza objekta veliku ulogu igra i udaljenost do kamere (z). Za dva vrha s istim x i y, vrh s višom vrijednošću z izgledat će bliže od drugog.

    To se zove perspektivna projekcija:

    I na našu sreću, 4x4 matrica može napraviti ovu projekciju:

    // Stvara stvarno teško čitljivu matricu, ali unatoč tome ovo je standardna 4x4 matrica glm :: mat4 projectionMatrix = glm :: perspektiva (glm :: radijani (FoV), // Okomito vidno polje u radijanima. Obično između 90 ° (vrlo širok) i 30 ° (uzak) 4,0 f / 3,0 f, // Omjer širine i visine. Ovisi o veličini vašeg prozora. Imajte na umu da je 4/3 == 800/600 == 1280/960 0,1 f, // Blizu ravnine odsijecanja. Mora biti veći od 0. 100,0 f // Daleka ravnina za odsijecanje.);

    Prešli smo iz Camera Space (svi vrhovi su postavljeni u odnosu na kameru) u Homogeneous space (svi vrhovi su u maloj kocki. Sve unutar kocke je prikazano).

    Pogledajmo sada sljedeće slike kako biste bolje razumjeli što se događa s projekcijom. Prije projekcije imamo plave objekte u prostoru kamere, dok crveni oblik prikazuje pogled kamere, odnosno sve što kamera vidi.

    Korištenje projekcijske matrice ima sljedeći učinak:

    Na ovoj slici, pogled kamere je kocka i svi objekti su deformirani. Predmeti koji su bliže kameri izgledaju veliki, a oni dalje mali. Baš kao u stvarnosti!

    Ovako će to izgledati:

    Slika je kvadratna, pa se primjenjuju sljedeće matematičke transformacije kako bi se slika proširila tako da odgovara stvarnoj veličini prozora:

    A ova slika je ono što će se zapravo prikazati.

    Transformacije spajanja: matrica ModelViewProjection

    ... Samo standardne matrične transformacije koje već volite!

    // C ++: izračunavanje matrice glm :: mat4 MVPmatrix = projekcija * pogled * model; // Zapamtiti! Obrnutim redoslijedom!

    // GLSL: Primijeni matricu transformirani_vrh = MVP * in_vrh;

    Stavljajući sve zajedno

    • Prvi korak je kreiranje naše MVP matrice. To se mora učiniti za svaki model koji prikazujete.

    // Projekcioni niz: 45° vidno polje, 4:3 omjer slike, raspon: 0,1 jedinica<->100 jedinica glm :: mat4 Projekcija = glm :: perspektiva (glm :: radijani (45,0 f), 4,0 f / 3,0 f, 0,1 f, 100,0 f); // Ili, za ortokameru glm :: mat4 View = glm :: lookAt (glm :: vec3 (4, 3, 3), // Kamera je u svjetskim koordinatama (4,3,3) glm :: vec3 (0, 0, 0), // I usmjeren prema ishodištu glm :: vec3 (0, 1, 0) // "Glava" je na vrhu); // Matrica modela: matrica identiteta (model je na početku) glm :: mat4 Model = glm :: mat4 (1.0 f); // Za svaki model pojedinačno // Rezultirajuća matrica ModelViewProjection, koja je rezultat množenja naše tri matrice glm :: mat4 MVP = Projekcija * Pogled * Model; // Zapamtite da se množenje matrice vrši obrnutim redoslijedom

    • Drugi korak je proslijediti ovo GLSL-u:

    // Dobivanje ručke varijable u shaderu // Samo jednom tijekom inicijalizacije. GLuint MatrixID = glGetUniformLocation (ID programa, "MVP"); // Proslijedite naše transformacije trenutnom shaderu // To se radi u glavnoj petlji, budući da će svaki model imati različitu MVP matricu (barem dio M) glUniformMatrix4fv (MatrixID, 1, GL_FALSE, & MVP [0] [0]);

    • Treći korak je korištenje primljenih podataka u GLSL-u za transformaciju naših vrhova.

    // Vertex ulazni podaci, različiti za sva izvršenja ovog shadera. raspored (lokacija = 0) u vec3 vertexPosition_modelspace; // Vrijednosti koje ostaju konstantne za cijelu mrežu. uniforma mat4 MVP; void main () ( // Izlazni položaj našeg vrha: MVP * pozicija gl_Position = MVP * vec4 (vertexPosition_modelspace, 1); )

    • Spreman! Sada imamo isti trokut kao u lekciji 2, koji se još uvijek nalazi na ishodištu (0, 0, 0), ali sada ga vidimo u perspektivi iz točke (4, 3, 3).

    U lekciji 6 naučit ćete kako dinamički mijenjati te vrijednosti pomoću tipkovnice i miša kako biste stvorili kameru koju ste navikli vidjeti u igrama. Ali prvo ćemo naučiti kako našim modelima dati boje (lekcija 4) i teksture (lekcija 5).

    Zadaci

    • Pokušajte promijeniti perspektivne vrijednosti glm ::
    • Umjesto korištenja perspektivne projekcije, pokušajte koristiti ortografski (glm: ortho)
    • Modificirajte ModelMatrix za pomicanje, rotiranje i skaliranje trokuta
    • Koristite prethodni zadatak, ali s drugačijim redoslijedom operacija. Obratite pažnju na rezultat.

    Perspektivna projekcija

    Razmotrili smo važne projekcije koje se koriste u afinoj geometriji. Prijeđimo sada na razmatranje geometrije perspektive i nekoliko novih vrsta projekcija.

    Na fotografijama, slikama, ekranu slike nam se čine prirodnim i ispravnim. Te se slike nazivaju perspektivne slike. Njihova svojstva su takva da se udaljeniji objekti prikazuju u manjem mjerilu, paralelne linije općenito nisu paralelne. Kao rezultat toga, geometrija slike ispada prilično složena i teško je odrediti veličinu određenih dijelova objekta iz gotove slike.

    Konvencionalna perspektivna projekcija je središnja projekcija na ravninu s ravnim zrakama koje prolaze kroz točku - središte projekcije. Jedna od projekcijskih zraka okomita je na ravninu projekcije i naziva se glavna. Točka presjeka ove zrake i ravnine projekcije glavna je točka slike.

    Postoje tri koordinatna sustava. Obično programer radi i čuva podatke o geometrijskim objektima u svjetskim koordinatama. Kako bi se povećao realizam u pripremi za prikaz slike na ekranu, podaci o objektima iz svjetskih koordinata pretvaraju se u koordinate prikaza. I tek u trenutku prikaza slike izravno na zaslonu zaslona prelaze na koordinate ekrana, a to su brojevi piksela zaslona.

    Prva dva sustava mogu se koristiti u višedimenzionalnim koordinatnim sustavima, ali potonji samo u 2D. Operacije su nepovratne, odnosno nemoguće je vratiti trodimenzionalnu sliku iz dvodimenzionalne projekcijske slike.

    U ovoj matrici elementi a, d, e odgovorni su za skaliranje, m, n, L- za pomak, str, q, r- za projektovanje, s- za složeno skaliranje, x- za rotaciju.

    Projekcija u jednoj točki na ravninu z = 0

    Bit ove projekcije je sljedeća: što je objekt dublji, to je veća vrijednost z-koordinate i nazivnik rz + 1, a samim tim i manji objekt izgleda na ravnini projekcije. Izvršimo jednostavne izračune i objasnimo ih grafički:
    jednadžba x "/ F = x / (F + z pr) je ekvivalentna: x" = xF / (F + z pr) = x / (1 + z pr / F) = x / (1 + rz pr) , gdje je r = 1 / F, F - fokus.

    Za točke koje leže na pravcu paralelnom s osi z, nisu se izgubile jedna za drugom, koristi se projekcija u jednoj točki na pravu (vidi transformacijsku matricu i riža. 4.2); z-koordinata je nestala, ali budući da su udaljeni objekti postali manji od slično bliskih, gledatelj ima osjećaj dubine. Zapamtite: ovo je prvi način prenošenja dubine na avion!

    Vrhunski povezani članci