Si të konfiguroni telefonat inteligjentë dhe PC. Portali informativ

Kamera virtuale. projeksion perspektiv

Dalja grafike zakonisht kryhet në një zonë drejtkëndore të ekranit ose dritares. Në interpretimin OpenGL, kjo zonë quhet porta e daljes. Është në këtë zonë drejtkëndore që imazhi i krijuar do të vendoset nga biblioteka. Dimensionet e tij janë në lidhje me këndin e sipërm të majtë të dritares dhe maten në piksele. Për të përcaktuar portën e daljes, aplikacioni duhet të dëgjojë për ngjarjen e ndryshimit të madhësisë së dritares dhe të përcaktojë portën e daljes duke përdorur funksionin:

void glViewport (GLint x, GLint y, GLsizei gjerësia, GLsizei lartësi);

Argumentet (x, y) specifikojnë pozicionin e këndit të sipërm majtas të portës së daljes dhe gjerësia dhe lartësia specifikojnë dimensionet e saj. Si parazgjedhje, biblioteka e shtrin portën e daljes në të gjithë dritaren OpenGL.

Sistemi i koordinatave

Përpara se të shfaqet në ekran, një kulm i specifikuar në sistemin koordinativ të skenës duhet të kalojë në një proces projeksioni. Për të përshkruar dhe kryer transformimet e sistemeve të koordinatave në bibliotekën OpenGL, përdoret një aparat matricë. Vetë sistemi i koordinatave dhe transformimet e tij përshkruhen me matrica në të ashtuquajturat koordinata homogjene.

Së pari, kulmi konvertohet në një matricë 1X4 ku tre elementët e parë janë koordinata x, y, z. Numri i katërt është faktori i shkallës w, i cili zakonisht është 1.0. Kulmi shumëzohet me matricën e pamjes, e cila përshkruan transformimet e sistemit koordinativ të pamjes. Merrni kulmin në koordinatat e pamjes. Ai, nga ana tjetër, shumëzohet me matricën e projeksionit dhe marrim kulmin në koordinatat e projeksionit. Në këtë fazë, disa kulme janë hedhur poshtë (për shkak të mungesës së vëllimit të interpretimit). Më pas kulmet normalizohen për të përcjellë perspektivën (përveç nëse koordinata w është 1.0). Projeksioni përfundimtar i kulmit në sipërfaqen e ekranit 2D trajtohet nga vetë biblioteka OpenGL dhe nuk mund të ndërhyhet.

Matrica e projeksionit

Matrica e projeksionit është përgjegjëse për sa hapësirë ​​do të jepet, si do të projektohen kulmet e primitivëve grafikë në sipërfaqen dydimensionale të ekranit të monitorit. Transformimet e matricës së projeksionit çojnë në faktin se i gjithë imazhi do të ndryshojë (i shkallëzuar, lëvizur ose rrotulluar). Në OpenGL është e mundur të përdoren dy mënyra të matricës së projeksionit: perspektiva dhe ortografike.

Në projeksionin perspektiv përdoret fakti që për syrin e njeriut punon me një objekt të tipit të largët, përmasat e të cilit kanë përmasa këndore. Sa më larg të jetë një objekt, aq më i vogël na duket. Kështu, vëllimi i hapësirës që vizualizohet është një piramidë.

Matrica e projeksionit të perspektivës përcaktohet duke përdorur funksionin:

void glFrustum (GLdyfish majtas, GLdyfish djathtas, GLdyfish poshtë, GLdouble lart, GLdyfish afër, GLdouble larg);

(majtas, poshtë, -afër) dhe (djathtas, lart, -afër) përcaktojnë koordinatat e kutisë së prerjes afër; afër dhe larg janë gjithmonë vlera pozitive dhe përcaktojnë distancën nga këndvështrimi në kutitë e prerjes së afërt dhe të largët.

Ju gjithashtu mund të përdorni funksionin gluPerspective() për të vendosur matricën e projeksionit të perspektivës, e cila ka argumente të tjera

void gluPerspective(GLdouble fovy, GLdouble aspect, GLdouble afer, GLdouble larg);

Argumenti fovy (fusha e shikimit) përcakton fushën e shikimit, dhe aspekti është raporti i gjerësisë së kutisë së prerjes me lartësinë. afër dhe larg janë gjithmonë vlera pozitive dhe përcaktojnë distancën nga këndvështrimi në kutitë e prerjes së afërt dhe të largët.

Projeksioni i perspektivës përdoret zakonisht në lojëra dhe aplikacione që kërkojnë që objektet të jenë shumë realiste, në krahasim me paraqitjen me sy. Projeksioni drejtshkrimor përdoret zakonisht për vizualizimin 2D dhe 3D të të dhënave shkencore dhe teknike. Konsideroni të vendosni fillimisht një projeksion ortografik për interpretimin 2D. Me një projeksion drejtshkrimor, vëllimi i hapësirës që jepet është një paralelipiped:

E veçanta e projeksionit drejtshkrimor është se distanca nga kamera tek objektet nuk ndikon në imazhin përfundimtar.

Për të vendosur dhe më pas për të ndryshuar matricën e projeksionit, ekzekutoni funksionin glMatrixMode(GL_PROJECTION). Për të filluar, duhet të anuloni gjithashtu të gjitha cilësimet dhe transformimet e mëparshme, duke e bërë matricën e projeksionit identitet duke përdorur funksionin glLoadIdentity (). Matrica e projeksionit drejtshkrimor vendoset duke përdorur një funksion që ka prototipin e mëposhtëm:

void glOrtho(GLdyfish majtas, GLdyfish djathtas, GLdouble fund, GLdouble lart, GLdouble afer, GLdouble larg);

(majtas, poshtë, -afër) dhe (djathtas, lart, -afër) janë pika që përcaktojnë kutinë e prerjes së afërt. (majtas, poshtë, -larg) dhe (djathtas, lart, -larg) janë pika që përcaktojnë kutinë e prerjes së largët. Pas aplikimit të kësaj komande, drejtimi i projeksionit është paralel me boshtin z drejt vlerave negative

//Funksioni për ndryshimin e madhësisë dhe vendosjen e koordinatave
Riforma e zbrazët (gjerësia int, lartësia int)
{
//Vendosja e portës së daljes
glViewport (0, 0, gjerësia, lartësia);

//Modaliteti i matricës së projektimit
glMatrixMode(GL_PROJECTION);
// Matrica e identitetit
glLoadIdentity();

//Krijimi i një sistemi koordinativ ortografik 2D
glOrtho(-50., 50., -50., 50., -1., 1.);

//Shiko modalitetin e matricës
glMatrixMode(GL_MODELVIEW);
}

Veçanërisht për paraqitjen 2D, mund të përdorni një funksion që ka prototipin e mëposhtëm:

void gluOrtho2D (GLdyfish majtas, GLdouble djathtas, GLdouble fund, GLdouble top);

Ky funksion është i ngjashëm me glOrtho() në të cilin argumenti është afër=-1.0 dhe far=1.0. Gjatë paraqitjes 2D, koordinata z e kulmeve ka vlerën 0, domethënë objektet janë në rrafshin e mesëm.

Nëse është e nevojshme të ruhen përmasat, sistemi i koordinatave duhet të vendoset duke marrë parasysh raportin e gjerësisë dhe lartësisë së dritares.

//Vendosja e sistemit të koordinatave drejtshkrimore
aspekti i dyfishtë=gjerësia/dyfishi(lartësia);
nëse (gjerësia>=lartësia)
{
gluOrtho2D(-50.*aspekti, 50.*aspekti, -50., 50.);
}
tjetër
{
gluOrtho2D(-50., 50., -50./aspekt, 50./aspekt);
}

Shiko matricën

Matrica e pamjes është përgjegjëse për sistemin koordinativ të modelit të krijuar 3D. Në procesin e krijimit të një modeli, matrica e pamjes mund të ndryshohet shumë herë për të modifikuar imazhin e primitivëve individualë grafikë (kthejeni një katror në një drejtkëndësh, një kub në një paralelipiped, një sferë në një elipsoid). Për të vendosur dhe më pas për të ndryshuar matricën e pamjes, ekzekutoni funksionin glMatrixMode(GL_MODELVIEW). Për të filluar, duhet të anuloni gjithashtu të gjitha cilësimet dhe transformimet e mëparshme, duke e bërë matricën e projeksionit identitet duke përdorur funksionin glLoadIdentity (). Nëse koordinatat e kulmeve të objektit janë specifikuar gjatë krijimit të tij, atëherë nuk kërkohen transformime shtesë të sistemit të koordinatave. Megjithatë, kjo është shpesh e vështirë ose thjesht e pamundur.

OpenGL ofron tre funksione për të kryer transformimin e sistemit të koordinatave: glTranslated(), glRotated() dhe glScaled(). Këto komanda gjenerojnë matrica përkthimi, rrotullimi dhe shkallëzimi që shumëzohen me matricën e pamjes duke përdorur funksionin glMultMatrix(). Siç mund ta shihni, OpenGL merr operacionet e matricës dhe i kryen ato sipas algoritmeve speciale, të shpejta me efikasitet maksimal. Për shembull:

Transferimi

Nëse argumenti është më i madh se 0, atëherë objekti do të zhvendoset drejt vlerave pozitive përgjatë boshtit kur të shfaqet. Nëse argumenti është më i vogël se 0, atëherë objekti do të zhvendoset drejt vlerave negative përgjatë boshtit kur të shfaqet.

Zmadhoni

Zbatohet duke përdorur një funksion që ka prototipin e mëposhtëm:

Nëse argumenti është më i madh se 1, atëherë objekti do të zmadhohet kur të shfaqet. Nëse argumenti është më i vogël se 1, atëherë objekti do të reduktohet kur të shfaqet. Nëse argumenti është negativ, atëherë objekti gjithashtu do të pasqyrohet kur shfaqet. Një vlerë e argumentit null lejohet, por do të rezultojë që dimensionet e objektit të jenë zero.

Kthehuni

Zbatohet duke përdorur një funksion që ka prototipin e mëposhtëm:

void glRotated (kënd i dyfishtë, x i dyfishtë, dyfishi y, z i dyfishtë); glRotated(30.,0.,0.,1.); //Rrotullimi rreth z
glFillimi (GL_LINE_LOOP);
// Vendos kulme
glVertex2d(-2., 2.);
glVertex2d(2., 2.);
glVertex2d(2., -2.);
glVertex2d(-2., -2.);
glend();

Argumenti i parë përcakton këndin e rrotullimit, dhe tre të tjerët - koordinatat e vektorit rreth të cilit kryhet rrotullimi.

Stack matricë

Një mekanizëm i dobishëm për ndërtimin e imazheve komplekse është rafti i matricës. Duke përdorur funksionet glPushMatrix() dhe glPopMatrix(), mund ta ruani matricën aktuale në pirg dhe ta rivendosni atë pas çdo ndryshimi në sistemin e koordinatave.

Shfaq listat

Listat janë një mekanizëm interesant dhe shumë efektiv për krijimin e një skene. Ky është një mekanizëm që ju lejon të mbani mend sekuencat e komandave OpenGL dhe t'i ekzekutoni ato përsëri. Kjo mund të rrisë ndjeshëm efikasitetin e paraqitjes së një numri të madh objektesh identike.

Çdo listë e shfaqur duhet të ketë një ID. Ky mund të jetë një numër i plotë arbitrar që mund ta caktoni vetë. Për të shmangur konfliktet e ID-së së listës, OpenGL rekomandon përdorimin e funksionit

GLuint glGenLists (varg GLsizei);

e cila gjen një identifikues të lirë dhe e kthen atë. Argumenti i funksionit specifikon numrin e listave të njëpasnjëshme për të cilat duhet të merren identifikuesit. Nëse nuk ka mbetur identifikues i lirë, atëherë funksioni kthen zero.

Për të filluar formimin e një liste, duhet të telefononi funksionin

void glNewList (lista GLuint, modaliteti GLenum);

Argumenti i parë specifikon identifikuesin e listës së krijuar, dhe i dyti specifikon nëse lista do të formohet vetëm (GL_COMPILE) ose menjëherë dhe do të shfaqet (GL_COMPILE_AND_EXECUTE). Tjetra mund të jenë komandat OpenGL që duhet të ruhen në listë. Jo të gjitha komandat mund të përfshihen në të.

Formimi i listës përfundon me funksionin:

void glEndList(void);

Pasi të krijohen, listat e ekranit ruhen në strukturën e brendshme të të dhënave të një dritareje OpenGL dhe do të fshihen kur dritarja mbyllet ose shkatërrohet.

Për të ekzekutuar listën e ekranit, përdorni komandën:

void glCallList (lista GLuint);

e cila merr një identifikues liste si argument.

Funksioni glCallList() mund të thirret në çdo program memte kur kërkohet të ekzekutohen komandat e ruajtura në listë.

Konsideroni një shembull:

void Barazim (i pavlefshëm)
{
//Pastrimi i buferit të ngjyrave

glColor3d (1.0, 1.0, 0.0);

glFillimi (GL_LINES);
glVertex2d(-50., .0);
glVertex2d (50., .0);

Për(int i=-50; i<50; i++)
{
glVertex2d(i, .0);
nëse (i % 5)
{
glVertex2d(i, -1.);
}
ndryshe nëse (i % 10)
{
glVertex2d(i, -2.);
}
tjetër
{
glVertex2d(i, -3.);
}
}
glend();

glFillimi (GL_LINES);
glVertex2d(.0, -50.);
glVertex2d(.0, 50.);
për(int j=-50; j<50; j++)
{
glVertex2d(.0, j);
nëse (j % 5)
{
glVertex2d(-1., j);
}
ndryshe nëse (j % 10)
{
glVertex2d(-2., j);
}
tjetër
{
glVertex2d(-3., j);
}
}
glend();
//Ekzekutimi i plotë i komandave
glFlush();
}

void Barazim (i pavlefshëm)
{
//Pastrimi i buferit të ngjyrave
glClear(GL_COLOR_BUFFER_BIT);
//Cakto ngjyrën e ekranit
glColor3d (1.0, 1.0, 0.0);

//Formimi i boshtit
boshti int = glGenLists(1);
nëse (boshti != 0)
{
glNewList(boshti, GL_COMPILE);
glFillimi (GL_LINES);
glVertex2d(0., .0);
glVertex2d (100., .0);

Për(int i=0.; i<97; i++)
{
glVertex2d(i, .0);
nëse (i % 5)
{
glVertex2d(i, 1.);
}
ndryshe nëse (i % 10)
{
glVertex2d(i, 2.);
}
tjetër
{
glVertex2d(i, 3.);
}
}
glend();
// Forma e shigjetës mund të shtohet më vonë
glFillimi (GL_LINE_STRIP);
glVertex2d(97., 1.);
glVertex2d(100.,.0);
glVertex2d(97., -1.);
glend();
glEndList();
}
//Vizatoni boshtin horizontal
glPushMatrix();
glPërkthyer(-50.,0.,0.);
glRotated(180.,1.,0.,0.);
glLista e Thirrjeve (boshti);
glPopMatrix();

//Vizatimi i boshtit vertikal
glPushMatrix();
glPërkthyer(0.,-50.,0.);
glRotated(90.,0.,0.,1.);
glLista e Thirrjeve (boshti);
glPopMatrix();

//Ekzekutimi i plotë i komandave
glFlush();
}

Motori nuk e lëviz anijen. Anija qëndron në vend dhe motori lëviz universin në lidhje me të.

Kjo është një pjesë shumë e rëndësishme e mësimeve, sigurohuni që ta lexoni disa herë dhe ta kuptoni mirë.

Koordinatat homogjene

Deri më tani, ne kemi vepruar në kulme 3-dimensionale si treshe (x, y, z). Prezantojmë edhe një parametër w dhe do të operojmë me vektorë të formës (x, y, z, w).

Mbani mend përgjithmonë se:

  • Nëse w == 1, atëherë vektori (x, y, z, 1) është një pozicion në hapësirë.
  • Nëse w == 0, atëherë vektori (x, y, z, 0) është drejtimi.

Çfarë na jep kjo? Ok, për rrotullim nuk ndryshon asgjë, sepse në rastin e rrotullimit të pikës dhe në rastin e rrotullimit të vektorit të drejtimit ju merrni të njëjtin rezultat. Megjithatë, në rastin e një transferimi, ka një ndryshim. Lëvizja e vektorit të drejtimit do të japë të njëjtin vektor. Në këtë do të ndalemi më në detaje më vonë.

Koordinatat homogjene na lejojnë të operojmë me vektorë në të dyja rastet duke përdorur një formulë matematikore.

Matricat e transformimit

Hyrje në matricat

Mënyra më e lehtë është të mendosh për një matricë si një grup numrash, me një numër të përcaktuar rreptësisht të rreshtave dhe kolonave. Për shembull, një matricë 2x3 duket si kjo:

Megjithatë, në 3D, ne do të përdorim vetëm matricat 4x4, të cilat do të na lejojnë të transformojmë kulmet tona (x, y, z, w). Kulmi i transformuar është rezultat i shumëzimit të matricës me vetë kulmin:

Matrica x kulm (në atë renditje!!) = Transformo. kulm

Shumë e thjeshtë. Ne do ta përdorim këtë mjaft shpesh, kështu që ka kuptim t'ia lëmë këtë kompjuterit:

Në C++, duke përdorur GLM:

glm::mat4 myMatrix; glm::vec4 myVector; glm :: // Kushtojini vëmendje porosisë! Ai është i rëndësishëm!

Në GLSL:

mat4 myMatrix; vec4 myVector; // Mos harroni të plotësoni matricën dhe vektorin me vlerat e kërkuara këtu vec4 transformedVector = myMatrix * myVector ; // Po, është shumë e ngjashme me GLM :)

Provoni të eksperimentoni me këto fragmente.

Matrica e transferimit

Matrica e transferimit duket si kjo:

ku X, Y, Z janë vlerat që duam t'i shtojmë vektorit tonë.

Pra, nëse duam ta zhvendosim vektorin (10, 10, 10, 1) 10 njësi në drejtimin X, marrim:

... marrim (20, 10, 10, 1) një vektor homogjen! Mos harroni se 1 në parametrin w do të thotë pozicion, jo drejtim, dhe transformimi ynë nuk e ndryshoi faktin që ne jemi duke punuar me pozicionin.

Tani le të shohim se çfarë ndodh nëse vektori (0, 0, -1, 0) përfaqëson një drejtim:

… dhe merrni vektorin tonë origjinal (0, 0, -1, 0). Siç u përmend më herët, një vektor me parametrin w = 0 nuk mund të zhvendoset.

Dhe është koha për ta zhvendosur atë në kod.

Në C++, me GLM:

#përfshi // pas 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;

Në GLSL:

vec4 transformedVector = myMatrix * myVector ;

Në fakt, ju kurrë nuk do ta bëni këtë në një shader, më shpesh do të bëni glm::translate() në C++ për të llogaritur matricën, do ta kaloni atë në GLSL dhe më pas do ta bëni shumëzimin në shader.

Matrica e identitetit

Është një matricë e veçantë që nuk bën asgjë, por ne po e prekim atë sepse është e rëndësishme të mbani mend se A herë 1.0 jep A:

Në C++:

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

Matrica e shkallëzimit

Duket gjithashtu e thjeshtë:

Pra, nëse dëshironi të aplikoni shkallëzimin e vektorit (pozicioni ose drejtimi - nuk ka rëndësi) me 2.0 në të gjitha drejtimet, atëherë duhet:

Vini re se w nuk ndryshon, dhe gjithashtu vini re se matrica e identitetit është një rast i veçantë i një matrice shkallëzimi me një faktor shkallëzimi prej 1 në të gjitha akset. Gjithashtu, matrica e identitetit është një rast i veçantë i matricës së transferimit, ku përkatësisht (X, Y, Z) = (0, 0, 0).

Në C++:

// shtoni #include dhe #përfshi glm :: mat4 myScalingMatrix = glm :: shkalle(2.0 f , 2.0 f , 2.0 f );

Matrica e rrotullimit

Më e vështirë se sa u diskutua më parë. Ne do t'i heqim detajet këtu, pasi nuk keni nevojë të dini saktësisht për përdorim të përditshëm. Për më shumë informacion, mund të shkoni te lidhja FAQ për Matricat dhe Kuaternionet (një burim mjaft i njohur dhe gjuha juaj ndoshta është e disponueshme atje)

Në C++:

// shtoni #include dhe #përfshi glm :: vec3 myRotationAxis ( ?? , ?? , ?? ); glm :: rotate(kënd_në_gradë, myRotationAxis);

Duke bashkuar transformimet

Pra, tani ne mund të rrotullojmë, përkthejmë dhe shkallëzojmë vektorët tanë. Hapi tjetër do të ishte mirë për të kombinuar transformimet, i cili zbatohet nga formula e mëposhtme:

TransformedVector = TranslationMatrix * RotationMatrix * ScaleMatrix * OriginalVector ;

KUJDES! Kjo formulë në fakt tregon se shkallëzimi kryhet fillimisht, më pas rrotullimi dhe i fundit kryhet vetëm përkthimi. Kështu funksionon shumëzimi i matricës.

Sigurohuni që të mbani mend rendin në të cilin është bërë e gjithë kjo, sepse rendi është me të vërtetë i rëndësishëm, në fund mund ta kontrolloni vetë:

  • Bëni një hap përpara dhe kthehuni majtas
  • Ktheni majtas dhe bëni një hap përpara

Dallimi është vërtet i rëndësishëm për t'u kuptuar, pasi do ta hasni vazhdimisht këtë. Për shembull, kur punoni me personazhet e lojës ose disa objekte, gjithmonë shkallëzoni së pari, pastaj rrotullojeni dhe vetëm më pas përktheni.

Në fakt, renditja e mësipërme është ajo që dëshironi zakonisht për personazhet e luajtshëm dhe artikujt e tjerë: shkallëzoni së pari nëse është e nevojshme; pastaj vendosni drejtimin e tij dhe më pas lëvizni atë. Për shembull, për një model anijeje (kthesa të hequra për thjeshtësi):

  • Mënyra e gabuar:
    • Ju e zhvendosni anijen në (10, 0, 0). Qendra e saj tani është 10 njësi nga origjina.
    • Ju shkallëzoni anijen tuaj me 2x. Çdo koordinatë shumëzohet me 2 "në lidhje me origjinalin", që është larg... Pra, ju futeni në një anije të madhe, por qendra e saj është 2 * 10 = 20. Jo ajo që dëshironit.
  • Rruga e duhur:
    • Ju shkallëzoni anijen tuaj me 2x. Ju merrni një anije të madhe, të përqendruar në origjinën.
    • Ju jeni duke lëvizur anijen tuaj. Është ende e njëjta madhësi dhe në distancën e duhur.

Në C++, me GLM:

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

Në GLSL:

mat4 transformim = mat2 * mat1 ; vec4 out_vec = transformim * in_vec ;

Matricat e botës, pamjes dhe projeksionit

Për pjesën tjetër të këtij tutoriali, ne do të supozojmë se dimë se si të japim modelin e preferuar 3D të Blenderit, majmunin Suzanne.

Matricat e botës, pamjes dhe projeksionit janë një mjet i dobishëm për ndarjen e transformimeve.

Matrica Botërore

Ky model, si dhe trekëndëshi ynë i kuq, përcaktohet nga një grup kulmesh, koordinatat e të cilave vendosen në lidhje me qendrën e objektit, d.m.th., kulmi me koordinatat (0, 0, 0) do të jetë në qendër të objektit.

Më pas, ne do të donim ta lëviznim modelin tonë teksa lojtari e kontrollon atë me tastierë dhe miun. Gjithçka që bëjmë është të aplikojmë shkallëzim, më pas të rrotullojmë dhe përkthejmë. Këto veprime kryhen për çdo kulm, në çdo kornizë (të kryera në GLSL, jo në C ++!) dhe kështu modeli ynë lëviz në ekran.

Tani kulmet tona janë në hapësirën botërore. Kjo tregohet nga shigjeta e zezë në figurë. Ne kemi lëvizur nga hapësira e objektit (të gjitha kulmet janë në lidhje me qendrën e objektit) në hapësirën botërore (të gjitha kulmet janë në lidhje me qendrën e botës).

Skematikisht, kjo tregohet si më poshtë:

Shiko matricën

Për të cituar sërish Futurama:

Motori nuk e lëviz anijen. Anija qëndron në të njëjtin vend dhe motori lëviz universin rreth saj.

Mundohuni ta imagjinoni këtë në lidhje me kamerën. Për shembull, nëse doni të bëni një foto të një mali, atëherë nuk e lëvizni kamerën, por lëvizni malin. Nuk është e mundur në jetën reale, por është tepër e lehtë në grafikën kompjuterike.

Pra, fillimisht kamera juaj është në qendër të sistemit të koordinatave botërore. Për të lëvizur botën ju duhet të futni një matricë tjetër. Le të themi se dëshironi ta lëvizni kamerën 3 njësi Djathtas (+X), që do të ishte ekuivalenti i lëvizjes së gjithë botës 3 njësi Majtas (-X). Në kod duket kështu:

// Shto #include dhe #përfshi glm::mat4 ViewMatrix = glm::translate(glm::mat4(), glm::vec3(- 3.0 f, 0.0 f, 0.0 f));

Përsëri, imazhi më poshtë e tregon këtë të plotë. Ne kaluam nga sistemi i koordinatave botërore (të gjitha kulmet vendosen në lidhje me qendrën e sistemit botëror) në sistemin e koordinatave të kamerës (të gjitha kulmet vendosen në lidhje me kamerën):

Dhe ndërsa truri juaj po e tret këtë, le t'i hedhim një sy funksionit që na ofron GLM, më konkretisht, glm::Shiko:

glm::mat4 CameraMatrix = glm::LookAt(Pozicioni i kamerës, // Pozicioni i kamerës në hapësirën botërore objektivi i kamerës, // Tregon se ku po shikoni në hapësirën botërore lartVektor // Një vektor që tregon drejtimin lart. Zakonisht (0, 1, 0));

Dhe këtu është një diagram që tregon se çfarë po bëjmë:

Megjithatë, ky nuk është fundi.

matrica e projeksionit

Pra, tani jemi në hapësirën e kamerave. Kjo do të thotë se kulmi që merr koordinatat x == 0 dhe y == 0 do të shfaqet në qendër të ekranit. Sidoqoftë, kur shfaqni një objekt, distanca nga kamera (z) gjithashtu luan një rol të madh. Për dy kulme me të njëjtat x dhe y, kulmi me vlerën më të lartë z do të shfaqet më afër se tjetri.

Ky quhet projeksion perspektiv:

Dhe për fatin tonë, një matricë 4x4 mund të bëjë këtë projeksion:

// Krijon një matricë vërtet të vështirë për t'u lexuar, por është ende një matricë standarde 4x4 glm::mat4 projeksionMatrix = glm::perspektivë( glm::radians(FoV), // Fusha vertikale e shikimit në radianë. Zakonisht midis 90° (shumë i gjerë) dhe 30° (i ngushtë) 4.0f / 3.0f, // Raporti i pamjes. Varet nga madhësia e dritares suaj. Vini re se 4/3 == 800/600 == 1280/960 0.1f // Pranë aeroplanit të prerjes. Duhet të jetë më i madh se 0. 100,0 f // Plani i prerjes së largët.);

Ne kemi lëvizur nga Kamera Space (të gjitha kulmet janë vendosur në lidhje me kamerën) në Hapësirë ​​Uniform (të gjitha kulmet janë në një kub të vogël. Çdo gjë brenda kubit shfaqet).

Tani le të shohim imazhet e mëposhtme në mënyrë që të kuptoni më mirë se çfarë po ndodh me projeksionin. Para projeksionit kemi objekte blu në hapësirën e kamerës, ndërsa figura e kuqe tregon pamjen e kamerës, pra gjithçka që shikon kamera.

Aplikimi i Matricës së Projeksionit ka efektin e mëposhtëm:

Në këtë imazh, pamja e kamerës është një kub dhe të gjitha objektet janë të deformuara. Objektet që janë më afër kamerës shfaqen të mëdha, dhe ato që janë më larg - të vogla. Ashtu si në realitet!

Kështu do të duket:

Imazhi është katror, ​​kështu që transformimet e mëposhtme matematikore aplikohen për të shtrirë imazhin për t'iu përshtatur dimensioneve aktuale të dritares:

Dhe kjo imazh është ajo që do të shfaqet në të vërtetë.

Kombinimi i transformimeve: matrica ModelViewProjection

… Vetëm transformimet standarde të matricës që tashmë i doni!

// C++ : llogaritja e matricës glm :: mat4 MVPmatrix = projeksion * pamje * model ; // Mbani mend! Në rend të kundërt!

// GLSL: aplikoni matricën kulmi_i transformuar = MVP * në kulm ;

Duke i bashkuar të gjitha

  • Hapi i parë është krijimi i matricës sonë MVP. Kjo duhet të bëhet për çdo model që shfaqni.

// Matrica e projektimit: fushëpamja 45°, raporti i pamjes 4:3, diapazoni: 0,1 njësi<->100 njësi glm :: mat4 Projeksion = glm :: perspektivë (glm :: radian (45,0 f ), 4,0 f / 3,0 f , 0,1 f , 100,0 f ); // Ose, për një ortokamerë glm :: mat4 Pamje = glm :: lookAt ( glm :: vec3 (4 , 3 , 3 ) // Kamera është në koordinatat botërore (4,3,3) glm::vec3(0, 0, 0), // Dhe drejtuar kah origjina glm::vec3(0 , 1 , 0 ) // "Koka" është në krye); // Matrica e modelit: matrica e identitetit (Modeli është në origjinë) glm::mat4 Model = glm::mat4(1.0 f ); // Individualisht për secilin model // Matrica që rezulton ModelViewProjection, e cila është rezultat i shumëzimit të tre matricave tona glm :: mat4 MVP = Projeksion * Shiko * Model ; // Mos harroni se shumëzimi i matricës bëhet në rend të kundërt

  • Hapi i dytë është t'ia kaloni këtë GLSL:

// Merrni dorezën e ndryshores në shader // Vetëm një herë gjatë inicializimit. GLuint MatrixID = glGetUniformLocation(ID-ja e programit, "MVP"); // Kalojmë transformimet tona në shader aktual // Kjo bëhet në ciklin kryesor pasi çdo model do të ketë një matricë të ndryshme MVP (të paktën pjesën M) glUniformMatrix4fv(MatrixID, 1, GL_FALSE, & MVP [0][0]);

  • Hapi i tretë është përdorimi i të dhënave që rezultojnë në GLSL për të transformuar kulmet tona.

// Të dhënat hyrëse vertex, të ndryshme për të gjitha ekzekutimet e këtij shader. faqosja (vendndodhja = 0 ) në vec3 vertexPosition_modelspace ; // Vlerat që mbeten konstante për të gjithë rrjetin. uniforme mat4 MVP ; void main()( // Pozicioni i daljes së kulmit tonë: pozicioni MVP * gl_Pozicioni = MVP * vec4 (vertexPosition_modelspace , 1 ); )

  • Gati! Tani kemi të njëjtin trekëndësh si në mësimin 2, ende i vendosur në origjinë (0, 0, 0), por tani e shohim në perspektivë nga pika (4, 3, 3).

Në mësimin 6, do të mësoni se si t'i ndryshoni këto vlera në mënyrë dinamike duke përdorur tastierën dhe miun për të krijuar llojin e kamerës që jeni mësuar të shihni në lojëra. Por së pari, ne do të mësojmë se si t'u japim modeleve tona ngjyra (Mësimi 4) dhe tekstura (Mësimi 5).

Detyrat

  • Provoni të ndryshoni vlerat glm::perspective
  • Në vend që të përdorni projeksionin e perspektivës, provoni të përdorni ortogonal (glm:ortho)
  • Modifikoni ModelMatrix për të lëvizur, rrotulluar dhe shkallëzuar trekëndëshin
  • Përdorni detyrën e mëparshme, por me një renditje të ndryshme operacionesh. Kushtojini vëmendje rezultatit.

Motori nuk e lëviz anijen.
Anija mbetet në vend, dhe
motorët lëvizin universin
Rreth tij.

Futurama

Ky është një nga mësimet më të rëndësishme. Lexojeni me mend të paktën tetë herë.

Koordinatat homogjene

Në mësimet e mëparshme, supozuam se kulmi ndodhet në koordinatat (x, y, z). Le të shtojmë një koordinatë më shumë - w. Tani e tutje, ne do të kemi kulme në koordinatat (x, y, z, w)

Së shpejti do të kuptoni se çfarë është ajo, por tani për tani, merreni si të mirëqenë:

  • Nëse w==1, atëherë vektori (x,y,z,1) është një pozicion në hapësirë
  • Nëse w==0 atëherë vektori (x,y,z,0) është drejtimi.

Mos harroni këtë si një aksiomë pa prova!!!

Dhe çfarë na jep? Epo, asgjë për rrotullim. Nëse rrotulloni një pikë ose një drejtim, do të merrni të njëjtin rezultat. Por nëse e rrotulloni lëvizjen (kur lëvizni një pikë në një drejtim të caktuar), atëherë gjithçka ndryshon në mënyrë dramatike. Çfarë do të thotë "të lëvizësh drejtimin"? Asgje speciale.

Koordinatat homogjene na lejojnë të operojmë me një material të vetëm për të dyja rastet.

Matricat e transformimit

Hyrje në matricat

E thënë thjesht, një matricë është vetëm një grup numrash me një numër fiks rreshtash dhe kolonash.

Për shembull, një matricë 2 me 3 do të duket kështu:

Në grafikat 3D, ne pothuajse gjithmonë përdorim matrica 4x4. Kjo na lejon të transformojmë kulmet tona (x,y,z,w). Është shumë e thjeshtë - ne shumëzojmë vektorin e pozicionit me matricën e transformimit.

Matricë*Kulmi = kulm i transformuar

Nuk është aq e frikshme sa duket. Drejtojeni gishtin e dorës së majtë drejt a dhe gishtin e djathtë drejt x. Kjo do të jetë sëpatë. Lëvizni gishtin e majtë te numri tjetër, b dhe lëvizni gishtin e djathtë poshtë te numri tjetër, y. ia dolëm. Edhe një herë - cz. Dhe edhe një herë - dw. Tani përmbledhim të gjithë numrat që rezultojnë - ax+nga +cz +dw . Ne kemi marrë x-në tonë të re. Përsëriteni të njëjtën gjë për çdo rresht dhe do të merrni një vektor të ri (x,y,z,w).

Sidoqoftë, ky është një operacion mjaft i mërzitshëm, ndaj lëreni kompjuterin ta bëjë atë për ne.

Në C++ duke përdorur bibliotekën GLM:

glm::mat4 myMatrix;


glm::vec4 myVector;



glm:: vec 4 Vektor i transformuar = myMatrix * myVector; // Mos harroni porosinë!!! Ky është arkiv!!!

Në GLSL:

mat4 myMatrix;


vec4 myVector;


// mbushim matricën dhe vektorin me vlerat tona... ne e kapërcejmë këtë


vec 4 Vektor i transformuar = myMatrix * myVector; // saktësisht e njëjtë si në GLM

(Disi më duket se nuk e kopjove këtë pjesë të kodit në projektin tënd dhe nuk e provove ... hajde, provoje, është interesante!)

Matrica e lëvizjes

Matrica e zhvendosjes është ndoshta matrica më e thjeshtë nga të gjitha. Këtu është ajo:


Këtu X, Y, Z janë vlerat që duam të shtojmë në pozicionin tonë të kulmit.

Pra, nëse duhet ta zhvendosim vektorin (10,10,10,1) me 10 pikë, në pozicionin X, atëherë:
(Provojeni vetë, ju lutem!)

...Dhe marrim (20,10,10,1) në një vektor homogjen. Siç shpresoj ta mbani mend, 1 do të thotë që vektori përfaqëson një pozicion, jo një drejtim.

Dhe tani le të përpiqemi të transformojmë drejtimin (0,0,-1,0) në të njëjtën mënyrë:

Dhe në fund, morëm të njëjtin vektor (0.0, -1.0).
Siç thashë, lëvizja e drejtimit nuk ka kuptim.

Si mund ta kodojmë këtë?

Në C++ me GLM:

#përfshi // pas


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 Vektor i transformuar = myMatrix * myVector; // dhe cili do të jetë rezultati?


Dhe në GLSL: Në GLSL, kjo bëhet rrallë. Më shpesh me funksionin glm::translate(). Së pari ata krijojnë një matricë në C++, dhe më pas e dërgojnë atë në GLSL, dhe atje bëhet vetëm një shumëzim:

vec4 transformedVector = myMatrix * myVector;

Matrica e identitetit

Kjo është një matricë e veçantë. Ajo nuk bën asgjë. Por e përmend sepse është e rëndësishme të dini se shumëzimi i A me 1.0 rezulton në A:

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

Matrica e shkallëzimit

Matrica e shkallëzimit është gjithashtu mjaft e thjeshtë:

Pra, nëse doni të dyfishoni një vektor (pozicion ose drejtim, nuk ka rëndësi) në të gjitha drejtimet:

Dhe koordinata w nuk ka ndryshuar. Nëse pyetni: "Çfarë është shkallëzimi i drejtimit?". Jo shpesh i dobishëm, por ndonjëherë i dobishëm.

(vini re se shkallëzimi i matricës së identitetit me (x,y,z) = (1,1,1))

C++:

// Përdorni#përfshi dhe#përfshi


glm::mat4 myScalingMatrix = glm::shkallë(2.0f, 2.0f ,2.0f);

Matrica e rrotullimit

Por kjo matricë është mjaft e ndërlikuar. Prandaj, nuk do të ndalem në detajet e zbatimit të tij të brendshëm. Nëse vërtet dëshironi, më mirë lexoni (FAQ të matricave dhe kuaternioneve)

ATMe++:

// Përdorni#përfshi dhe#përfshi


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


glm::rotate(kënd_në_gradë, myRotationAxis);

Transformimet e kombinuara

Tani ne e dimë se si t'i rrotullojmë, lëvizim dhe shkallëzojmë vektorët tanë. Do të ishte mirë të dije se si t'i bashkosh të gjitha. Kjo bëhet thjesht duke shumëzuar matricat me njëra-tjetrën.

TransformedVector = TranslationMatrix * RotationMatrix * ScaleMatrix * OriginalVector;

Dhe përsëri porosit! Së pari ju duhet të ndryshoni madhësinë, pastaj lëvizni dhe vetëm pastaj lëvizni.

Nëse i zbatojmë transformimet në një mënyrë tjetër, nuk do të marrim të njëjtin rezultat. Këtu provoni:

  • Bëni një hap përpara (mos e rrëzoni kompjuterin nga tavolina) dhe kthehuni majtas
  • Ktheni majtas dhe bëni një hap përpara.

Po, duhet të mbani mend gjithmonë rendin e veprimeve kur menaxhoni, për shembull, një personazh të lojës. Fillimisht, nëse është e nevojshme, bëjmë shkallëzim, më pas vendosim drejtimin (rotacionin) dhe më pas lëvizim. Le të shohim një shembull të vogël (e hoqa rrotullimin për lehtësinë e llogaritjes):

Mënyra e gabuar:

  • Zhvendosni anijen në (10,0,0). Qendra e saj tani është 10 x nga qendra.
  • Ne e rrisim madhësinë e anijes sonë me 2 herë. Çdo koordinatë shumëzohet me 2 në lidhje me qendrën, e cila është shumë larg... Dhe si rezultat, marrim një anije të madhësisë së kërkuar por në pozicionin 2*10=20. E cila nuk është saktësisht ajo që ne dëshironim.

Rruga e duhur:

  • Ne e rrisim madhësinë e anijes me 2 herë. Tani kemi një anije të madhe të vendosur në qendër.
  • Ne lëvizim anijen. Madhësia e anijes nuk ka ndryshuar dhe është e vendosur në vendin e duhur.
Shumëzimi i një matrice me një matricë bëhet në të njëjtën mënyrë si shumëzimi i një matrice me një vektor. Nuk do të hyjmë në detaje, por lërini kureshtarët ta lexojnë nga burime të specializuara. Ne thjesht do të mbështetemi në bibliotekën GLM.
BC ++:
glm::mat4 myModelMatrix = myTranslationMatrix * myRotationMatrix * myScaleMatrix;
glm::vec4 myTransformedVector = myModelMatrix * myOriginalVector;
Në GLSL:
mat4 transformim = mat2 * mat1;
vec4 out_vec = transformim * in_vec;

Matricat e modelit, pamjes dhe projektimit

Për ilustrim, supozojmë se tashmë dimë të vizatojmë në OpenGL modelin tonë të preferuar Blender 3D - kokën e majmunit Suzanne.

Matricat e modelit, pamjes dhe projektimit janë një metodë shumë e përshtatshme për ndarjen e transformimeve. Nëse vërtet dëshironi, nuk mund t'i përdorni ato (ne nuk i kemi përdorur në mësimet 1 dhe 2). Por unë rekomandoj fuqimisht që t'i përdorni ato. Thjesht, pothuajse të gjitha bibliotekat 3D, lojërat, etj., i përdorin ato për të ndarë transformimet.

Matrica e modelit

Ky model, si trekëndëshi ynë i preferuar, përcaktohet nga një grup kulmesh. Koordinatat X, Y, Z janë në lidhje me qendrën e objektit. Pra, nëse kulmi ndodhet në koordinatat (0,0,0), atëherë është në qendër të të gjithë objektit

Tani ne jemi në gjendje të lëvizim modelin tonë. Për shembull, sepse përdoruesi e kontrollon atë duke përdorur tastierën dhe miun. Është shumë e lehtë për ta bërë: shkallëzoni * rrotulloni * lëvizni dhe kaq. Ju aplikoni matricën tuaj në të gjitha kulmet në çdo kornizë (në GLSL dhe jo në C++) dhe gjithçka lëviz. Çdo gjë që nuk lëviz ndodhet në qendër të “botës”.

Kulmet janë në hapësirën botërore. Shigjeta e zezë në figurë tregon se si lëvizim nga hapësira e modelit në hapësirën botërore (Të gjitha kulmet janë vendosur në raport me qendrën e modelit, dhe tani ato janë vendosur në raport me qendrën e botës)

Ky transformim mund të përfaqësohet nga diagrami i mëposhtëm:


Shiko Matricën

Le të lexojmë përsëri citimin nga Futurama:

“Motori nuk e lëviz anijen. Anija qëndron në vend, dhe motorët lëvizin universin rreth saj.


E njëjta gjë mund të zbatohet për kamerën. Nëse dëshironi të fotografoni një mal nga çdo kënd, mund të lëvizni kamerën...ose malin. Në jetën reale, kjo është e pamundur, por shumë e lehtë dhe e përshtatshme në grafikën kompjuterike.

Si parazgjedhje, kamera jonë është në qendër të Koordinatave Botërore. Për të lëvizur botën tonë, ne duhet të krijojmë një matricë të re. Për shembull, duhet ta zhvendosim kamerën tonë 3 njësi djathtas (+X). Kjo është e njëjtë me lëvizjen e gjithë botës 3 njësi në të majtë (-X). Dhe ndërsa truri juaj po shkrihet, le të provojmë:


// Përdorni #include dhe #përfshi


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

Fotografia më poshtë tregon këtë: ne po lëvizim nga hapësira botërore (të gjitha kulmet janë në lidhje me qendrën e botës siç bëmë në seksionin e mëparshëm) në hapësirën e kamerës (të gjitha kulmet janë në lidhje me kamerën).

Dhe përpara se koka juaj të shpërthejë vërtet, shikoni këtë veçori të mrekullueshme nga GLM-ja jonë e vjetër:

glm::mat4 CameraMatrix = glm::LookAt(


Pozicioni i kamerës, // Pozicioni i kamerës në koordinatat botërore


kameraTarget, // pika që duam të shikojmë në koordinatat botërore


lartVektor// më shumë gjasa glm:: vec3(0,1,0) dhe (0,-1,0) do të tregojnë gjithçka me kokë poshtë, gjë që ndonjëherë është gjithashtu e lezetshme.


Këtu është një ilustrim i sa më sipër:


Por për gëzimin tonë, kjo nuk është e gjitha.

Matrica e Projeksionit

Tani kemi koordinata në hapësirën e kamerës. Kjo do të thotë se pas gjithë këtyre transformimeve, kulmi i të cilave ndodh të jetë x==0 dhe y==0 do të jepet në qendër të ekranit. Por ne nuk mund të përdorim vetëm koordinatat X,Y për të kuptuar se ku të vizatojmë kulmin: duhet të merret parasysh edhe distanca nga kamera (Z)! Nëse kemi dy kulme, atëherë njëra prej tyre do të jetë më afër qendrës së ekranit se tjetra, pasi ka një koordinatë më të madhe Z.

Ky quhet projeksion perspektiv:


Dhe për lumturinë tonë, matrica 4x4 mund të përfaqësojë gjithashtu transformime premtuese:

glm::mat4 projeksionMatrix = glm::perspektivë(


FoV, // Kutia e pamjes horizontale në gradë. Ose magnitudëpërafrim. sisikur « lente» kamera. Zakonisht midis 90 (super i gjerë, si një sy peshku) dhe 30 (si një gotë e vogël spiun)


4.0 f / 3.0 f, // Raporti i pamjes. Varet nga madhësia e dritares suaj. Për shembull, 4/3 == 800/600 == 1280/960 tingëllon e njohur, apo jo?


0.1 f, // Pranë fushës së prerjes. Duhet të vendoset sa më i madh, përndryshe do të ketë probleme me saktësinë.


100.0 f // Fusha e prerjes së largët. Duhet ta mbani sa më të vogël.


);

Le të përsërisim atë që sapo bëmë:

Ne kemi kaluar nga hapësira e kamerës (të gjitha kulmet janë vendosur në koordinata në lidhje me kamerën) në hapësirë ​​homogjene (të gjitha kulmet janë në koordinatat e një kubi të vogël (-1,1). Çdo gjë që është në kub është në ekran.)

Dhe diagrami përfundimtar:


Këtu është një foto tjetër për ta bërë më të qartë se çfarë ndodh kur shumëzojmë gjithë këtë marrëzi të matricës së projeksionit. Përpara se të shumëzojmë me matricën e projeksionit, kemi objekte blu të përcaktuara në hapësirën e kamerës dhe një objekt të kuq që përfaqëson fushën e shikimit të kamerës: hapësira që bie në lentet e kamerës:

Pas shumëzimit me matricën e projeksionit, marrim sa vijon:

Në foton e mëparshme, porta e shikimit është kthyer në një kub të përsosur (me koordinata kulmore nga -1 në 1 në të gjitha boshtet.), dhe të gjitha objektet janë deformuar në perspektivë. Të gjitha objektet blu që janë afër kamerës bëhen të mëdha, dhe ato që janë më larg bëhen të vogla. Ashtu si në jetë!

Ja pamja që kemi nga “lentet”:

Megjithatë, ai është katror dhe duhet të zbatohet një transformim matematikor për t'iu përshtatur imazhit që t'i përshtatet dritares.

Aksonometria është një projeksion paralel. Në tabelën 3.3, së pari jepen matricat e projeksioneve drejtshkrimore në rrafshet e koordinatave të marra nga përkufizimet e tyre.

Tabela 3.3 Matricat e projektimit të transformimeve dhe projeksioneve

Projeksion drejtshkrimor në XOY

Projeksion drejtshkrimor në YOZ

Projeksion drejtshkrimor në XOZ

Projeksioni drejtshkrimor në rrafshin x=p

Matrica e transformimit trimetrik në rrafshin XOY

Matrica e transformimit izometrik në rrafshin XOY

Matrica izometrike e projeksionit në rrafshin XOY

Matrica e zhdrejtë e projeksionit në XOY

Matrica e projeksionit falas në XOY

Matrica e projektimit të kabinetit në XOY

Matrica e transformimit të perspektivës me një pikë zhdukjeje (rrafshi i figurës është pingul me boshtin x)

Matrica e transformimit të perspektivës me një pikë zhdukjeje (rrafshi i figurës është pingul me boshtin y)

Matrica e transformimit të perspektivës me një pikë zhdukjeje (rrafshi i figurës është pingul me boshtin e aplikuar)

Matrica e transformimit të perspektivës me dy pika zhdukjeje (rrafshi i figurës paralel me boshtin y)

Matrica e transformimit të perspektivës me tre pika zhdukjeje (rrafshi i figurës së pozicionit arbitrar)

Izometria, dimetria dhe trimetria përftohen nga një kombinim i rrotullimeve të ndjekura nga një projeksion nga pafundësia. Nëse keni nevojë të përshkruani projeksionin në rrafshin XOY, atëherë së pari duhet të transformoni rrotullimin me një kënd në lidhje me boshtin y, pastaj me këndin rreth boshtit x. Tabela 3.3 tregon matricën e transformimit trimetrik. Për të marrë një matricë transformimi dimetrik, në të cilën, për shembull, koeficientët e shtrembërimit përgjatë boshteve të abshisës dhe të ordinatave do të jenë të barabartë, marrëdhënia midis këndeve të rrotullimit duhet t'i bindet varësisë

Kjo është, zgjedhja e një këndi , ju mund të llogarisni këndin dhe përcaktoni një matricë projeksioni dimetrik. Për një transformim izometrik, marrëdhënia e këtyre këndeve kthehet në vlera të përcaktuara rreptësisht, të cilat janë:

Tabela 3.3 tregon matricën e transformimit izometrik, si dhe matricën izometrike të projeksionit në rrafshin XOY. Nevoja për matrica të tipit të parë qëndron në përdorimin e tyre në algoritme për heqjen e elementeve të padukshëm.

Në projeksionet e zhdrejtë, linjat e projektimit formojnë një kënd të ndryshëm nga 90 gradë me planin e projeksionit. Tabela 3.3 tregon matricën e përgjithshme të projeksionit të zhdrejtë në rrafshin XOY, si dhe matricat e projeksioneve të lira dhe të kolltukut, në të cilat:

Projeksionet e perspektivës (Tabela 3.3) përfaqësohen gjithashtu nga transformimet e perspektivës dhe projeksionet e perspektivës në rrafshin XOY. V X , V Y dhe V Z janë qendra projeksioni - pika në boshtet përkatëse. –V X , -V Y , -V Z do të jenë pikat në të cilat konvergojnë lapsat e drejtëzave, paralelisht me boshtet përkatëse.

Sistemi koordinativ i vëzhguesit është majtas sistemi i koordinatave (Fig.3.3), në të cilin boshti z e drejtohet përpara nga pikëpamja, boshti x e drejtohet djathtas dhe boshti y e lart. Një rregull i tillë është miratuar për koincidencën e boshteve x e dhe y e me boshtet x s dhe y s në ekran. Përcaktimi i vlerave të koordinatave të ekranit x s dhe y s për pikën P çon në nevojën për t'u pjesëtuar me koordinatat z e. Për të ndërtuar një imazh të saktë të perspektivës, është e nevojshme të ndahet me koordinatat e thellësisë së secilës pikë.

Tabela 3.4 tregon vlerat e përshkruesit të kulmit S(X,Y,Z) të modelit (Fig.2.1) që i nënshtrohet rrotullimit dhe transformimeve izometrike.

Tabela 3.4 Modeli i përshkruesve të kulmeve

model origjinal

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

Sot do të hedhim një vështrim më të afërt në pajisjen e kamerës virtuale. Le të fillojmë me foton.

Në figurë shohim hapësirën koordinative të kamerës. Drejtimi ("pamja") e kamerës përkon gjithmonë me drejtimin pozitiv të boshtit z, dhe vetë kamera ndodhet në origjinë.

Hapësira e brendshme e piramidës e paraqitur në figurë është ajo pjesë e botës virtuale që përdoruesi do të shohë.

Kushtojini vëmendje tre aeroplanëve. E para ndodhet në një distancë prej 1 përgjatë boshtit z. Ky është avioni afër. Ajo që është përpara saj, lojtari nuk do ta shohë kurrë. Në këtë rast, vlera e z është e barabartë me një, por në përgjithësi mund të jetë çdo gjë. Është me rrafshin e afërt që lidhet një defekt në shfaqjen e grafikëve. Ky defekt manifestohet kryesisht në revole (për shkak të lirisë së madhe të kamerës). Kur i afroheni shumë një objekti, mund të përfundoni "in". Nga lojërat e fundit, ky defekt ishte veçanërisht i theksuar në Left 4 dead: kur një turmë zombish grumbullohej mbi lojtarin, shumë shpesh ishte e mundur të shikoni brenda personazheve të tjerë.

Rrafshi i vendosur në një distancë prej 100 njësi përgjatë boshtit z quhet rrafshi i largët. Përsëri, vlera mund të jetë arbitrare. Përdoruesi nuk do të shohë kurrë objekte më larg se ky aeroplan.

Gjashtë plane që përcaktojnë hapësirën që përdoruesi do të shohë quhen plane prerëse: majtas, djathtas, lart, poshtë, afër dhe larg.

Aeroplani i vendosur midis afërsisë dhe largësisë është rrafshi i projektimit. Në të ardhmen këtë rrafsh do ta vendosim në z=1, d.m.th. do të përputhet me më të afërtin. Këtu kam ndarë planin e afërt dhe atë të projeksionit për të treguar se nuk janë e njëjta gjë. Plani i projeksionit është menduar për transformimin e fundit të koordinatave: transformimin nga hapësira 3D e kamerës në hapësirën 2D.

Është falë planit të projektimit që përdoruesi do të shohë botën virtuale. Në fakt, ky aeroplan është ajo që përdoruesi do të shohë. Plani i projeksionit lidhet drejtpërdrejt me koncepte të tilla si tamponat kryesore / sfondi, dritarja e programit dhe ekrani i përdoruesit. Të gjitha këto koncepte mund të konsiderohen si një fotografi drejtkëndëshe, e cila përfaqësohet në kujtesën e kompjuterit nga një grup numrash.

Shndërrimi i koordinatave nga një botë tredimensionale në një plan projeksioni është më i vështiri nga ato që kemi studiuar deri më tani.

Fusha e shikimit / fusha e shikimit (fusha e shikimit)

Në figurën e mësipërme, rrafshi i projektimit (dhe kështu imazhi që përdoruesi do të shohë) ka një gjerësi më të madhe se lartësia e tij. Gjerësia dhe lartësia e planit të projektimit vendosen duke përdorur kënde. Ka emra të ndryshëm për këto kënde: fushëpamje ose fushëpamje. Në anglisht - fushat e shikimit.

Zonat e shikimit përcaktohen nga dy kënde. Le t'i quajmë ato: fovx - zona e pamjes horizontale, fovy - zona e pamjes vertikale. Më shumë rreth zonave të pamjes: më poshtë.

Z-buffer / w-buffer / depth buffer (z-buffer / w-buffer / depth buffer)

Le të shohim foton, e cila tregon dy trekëndësha: në një distancë prej 25 dhe 50 njësi nga kamera. Figura (a) tregon vendndodhjen e trekëndëshave në hapësirë ​​(pamja e sipërme), dhe figura (b) tregon imazhin përfundimtar:

Siç mund ta merrni me mend, imazhi duhet të vizatohet duke filluar nga elementët më të largët dhe duke përfunduar me ato më të afërt. Zgjidhja e qartë: llogarisni distancën nga origjina (nga kamera) në çdo objekt dhe më pas krahasoni. Grafika kompjuterike përdor një mekanizëm pak më të avancuar. Ky mekanizëm ka disa emra: z-buffer, w-buffer, depth buffer. Madhësia e z-buferit për sa i përket numrit të elementeve është e njëjtë me madhësinë e sfondit dhe buferëve kryesorë. Z-komponenti i objektit më afër kamerës futet në z-buffer. Në këtë shembull, ku trekëndëshi blu mbivendoset me atë të gjelbër, koordinatat z të blusë do të futen në tampon të thellësisë. Ne do të flasim më shumë rreth z-buffers në një tutorial të veçantë.

Projeksion ortografik / paralel (projeksion drejtshkrimor / paralel)

Operacioni në të cilin ka një ulje të dimensionit të hapësirës (kishte një hapësirë ​​tre-dimensionale, ajo u bë dy-dimensionale) quhet projeksion. Fillimisht na intereson projeksioni perspektiv, por fillimisht do të njihemi me paralelen (projeksionin paralel ose drejtshkrimor).

Për të llogaritur një projeksion paralel, mjafton të hidhet poshtë koordinata shtesë. Nëse kemi një pikë në hapësirën [ 3 3 3 ], atëherë me një projeksion paralel në rrafshin z=1, ajo do të projektohet në një pikë.

Projeksioni perspektiv në planin e projeksionit

Në këtë lloj projeksioni, të gjitha linjat konvergojnë në një pikë. Kështu funksionon vizioni ynë. Dhe pikërisht me ndihmën e projeksionit të perspektivës modelohet "look" në të gjitha lojërat.


Krahasoni këtë vizatim me vizatimin që tregon koordinata homogjene nga mësimi i mëparshëm. Për të kaluar nga hapësira tredimensionale në hapësirën dydimensionale, duhet të ndani dy komponentët e parë të vektorëve me të tretën: [ x/z y/z z/z ] = [ x/z y/z 1 ].

Siç shkrova më lart, rrafshi i projektimit mund të vendoset kudo midis afërsisë dhe largësisë. Planin e projeksionit do ta vendosim gjithmonë në z=1, por në këtë tutorial do të shikojmë opsionet e tjera. Le të shohim foton:


Le ta shënojmë distancën nga rrafshi i projektimit nga origjina si d. Do të shqyrtojmë dy raste: d=1 dhe d=5. Një pikë e rëndësishme: komponenti i tretë i të gjithë vektorëve pas projeksionit duhet të jetë i barabartë me d - të gjitha pikat janë të vendosura në të njëjtin rrafsh z=d. Kjo mund të arrihet duke shumëzuar të gjithë komponentët e vektorit me d: [ xd/z yd/z zd/z ]. Me d=1, marrim: [ x/z y/z 1 ], kjo formulë është përdorur për të transformuar koordinatat homogjene.

Tani, nëse e zhvendosim rrafshin e projeksionit në pikën z=5 (përkatësisht d=5), fitojmë: [ xd/z yd/z zd/z ] = [ 5x/z 5y/z 5 ]. Formula e fundit projekton të gjithë vektorët hapësinorë në një plan, ku d=5.
Këtu kemi një problem të vogël. Formula e mëparshme funksionon me vektorë 3D. Por ne ramë dakord të përdorim vektorë katër-dimensionale. Komponenti i katërt në këtë rast thjesht mund të hidhet poshtë. Por ne nuk do ta bëjmë këtë, pasi përdorimi i tij jep disa veçori specifike, të cilat do t'i diskutojmë më vonë.

Është e nevojshme të gjendet një pjesëtues i përbashkët i përbërësve të tretë dhe të katërt, gjatë pjesëtimit me të cilin vlera d mbetet në përbërësin e tretë dhe një në të katërtin. Ky pjesëtues është d/z. Tani nga vektori i zakonshëm [ x y z 1 ] duhet të marrim një vektor gati për projeksion (pjestim) [ x y z z/d ]. Kjo bëhet duke përdorur matricën e transformimit (kontrolloni rezultatin duke shumëzuar çdo vektor me këtë matricë):


Transformimi i fundit nuk është ende një projeksion. Këtu thjesht sjellim të gjithë vektorët në formën që na nevojitet. Ju kujtoj se rrafshin e projeksionit do ta vendosim në d=1, që do të thotë se vektorët do të duken kështu: [ x y z z ].

Matrica e transformimit të perspektivës

Ne do t'i hedhim një vështrim matricës së transformimit të perspektivës së përdorur në DirectX:

Tani e dimë se për çfarë shërben elementi _34. Ne gjithashtu e dimë se elementët _11 dhe _22 e shkallëzojnë imazhin horizontalisht dhe vertikalisht. Le të shohim se çfarë saktësisht fshihet pas emrave xScale dhe yScale.

Këto variabla varen nga zonat e pamjes për të cilat folëm më sipër. Duke rritur ose ulur këto kënde, ju mund të shkallëzoni (shkallëzoni ose zmadhoni) imazhin - të ndryshoni madhësinë dhe raportin e pamjes së planit të projektimit. Mekanizmi i zmadhimit të kujton në mënyrë të paqartë zmadhimin në kamera / kamera - parimi është shumë i ngjashëm. Merrni parasysh figurën:


Këndin fov e ndajmë në dy pjesë dhe konsiderojmë vetëm gjysmën. Ajo që shohim këtu: duke rritur këndin fov / 2 (dhe, në përputhje me rrethanat, këndin fov), ne rrisim mëkatin e këndit dhe ulim cos. Kjo çon në një rritje të planit të projektimit dhe, në përputhje me rrethanat, në një ulje të objekteve të projektuara. Këndi ideal për ne do të ishte fov/2 = P/4. Si kujtesë, një kënd në radian P/4 është 45 gradë. Në këtë rast, fov do të jetë e barabartë me 90 gradë. Pse një kënd 45 gradë është i mirë për ne? Në këtë rast, nuk ka shkallëzim, por cos(P/4)/sin(P/4)=1.

Tani mund ta shkallëzojmë lehtësisht imazhin vertikalisht (horizontalisht) duke përdorur sinusin dhe kosinusin e zonës së gjysmës së pamjes (funksioni kotangjent në C++ quhet cot):

yScale = cos(fovY/2)/sin(fovY/2) = cot(fovY/2)
DirectX përdor vetëm zonën e pamjes vertikale (fovY), dhe shkallëzimi horizontal varet nga zona e pamjes vertikale dhe raporti i pamjes.

Ju kujtoj se dritarja në programet tona është 500x500 në madhësi. Raporti i aspektit: 1 me 1. Prandaj, variablat do të jenë të barabarta: xScale=1, yScale=1.

Raporti standard i pamjes së monitorit/TV: 4:3. Ky raport korrespondon me rezolucionet e ekranit: 640x480, 800x600, 1600x1200. Nuk do të prekim ende modalitetin e ekranit të plotë, por mund të ndryshojmë madhësinë e dritares së programit. Ju mund të ndryshoni madhësinë e dritares (në parametrat aktualë), për shembull, në 640X480. Por për të mos zgjatur gjithçka (katroret do të duken si drejtkëndësha), mos harroni të ndryshoni variablat përkatëse në matricën e projeksionit.

Unë pothuajse harrova, forumën për xScale në DirectX:

xScale = yScale / raporti i pamjes
Raportet e aspektit vendosen thjesht: 1/1, 4/3, 16/9 - këto janë standarde.

Mbetet për të gjetur qëllimin e elementeve _33, _34 të matricës së transformimit të perspektivës. zf është z-koordinata e rrafshit të largët (nga larg - larg), dhe zn është z-koordinata e rrafshit të afërt (nga afër - afër). Vini re se elementi _43 = _33 * -zn.

Mënyra më e lehtë për të kuptuar se çfarë saktësisht bëjnë këto formula është përmes shembujve. Shumëzoni vektorin standard [ x y z w ] me matricën e treguar më sipër. Unë rekomandoj që ta bëni këtë duke marrë një fletë letre dhe një laps (shpresoj të mbani mend se si të shumëzoni dy matrica). Komponentët e vektorit do të marrin formën e mëposhtme.

1 = x*xShkalla
2 = y*yShkalla
3 = z*(zf/(zf-zn)) + w*(-(zn*zf)/(zf-zn)) = (zf/(zf-zn))*(z - w*zn)
4 = (w*z)/d
Le të bëjmë një transformim projeksioni (i ndajmë të gjithë elementët në komponentin e 4-të, ndërsa supozojmë se d=1 dhe w=1):

1 = (d*x*xScale)/(w*z) = (x*xScale)/z
2 = (d*y*yScale)/(w*z) = (y*xScale)/z
3 = (zf/(zf-zn))*(z - w*zn)*(w*d/z) = (zf/(zf-zn))*(1 - zn/z)
4 = 1
Si rezultat, ne morëm një vektor të formës:

[ x/(z*xShkalla) y/(z*yShkalla) (zf/(zf-zn))*(1-zn/z) 1 ]
Tani, nëse specifikoni vlera specifike për zf dhe zn, do të gjeni sa vijon (për vlerat pozitive): nëse vektori ndodhet përpara planit të afërt, atëherë komponenti z pas transformimit do të jetë më i vogël se zero, nëse vektori ndodhet prapa rrafshit të largët, atëherë komponenti z do të jetë njësi më të mëdha.

Nuk ka dallim se ku ndodhen saktësisht rrafshi i afërt dhe i largët: zn=1, zf=10 ose zn=10, dhe zf=100 (ose ndonjë vlerë tjetër) - pas transformimit, zona e dukshme do të vendoset në interval. nga zero në një, përfshirëse.

Për këtë synohen formulat në elementet _33, _34 të matricës së projeksionit - për të projektuar distancën nga rrafshi i afërt në atë të largët në segment. Kontrolloni këtë duke llogaritur vlerat e disa vektorëve për vlera specifike të zn,zf (po, në një copë letër!!!).

Artikujt kryesorë të lidhur