To easily find a point on a grid, you need a coordinate system. For example, if the grid happens to be your iPhone 13 screen, the center point might be x: 195, y: 422. However, that point may be different depending on what space it’s in.
In the previous chapter, you learned about matrices. By multiplying a vertex’s position by a particular matrix, you can convert the vertex position to a different coordinate space. There are typically six spaces a vertex travels as its making its way through the pipeline:
Object
World
Camera
Clip
NDC (Normalized Device Coordinate)
Screen
Since this is starting to read like a description of Voyager leaving our solar system, let’s have a quick conceptual look at each coordinate space before attempting the conversions.
Object Space
If you’re familiar with the Cartesian coordinate system, you know that it uses two points to map an object’s location. The following image shows a 2D grid with the possible vertices of the dog mapped using Cartesian coordinates.
Vertices in object space
The positions of the vertices are in relation to the dog’s origin, which is located at (0, 0). The vertices in this image are located in object space (or local or model space). In the previous chapter, Triangle held an array of vertices in object space, describing the vertex of each point of the triangle.
World Space
In the following image, the direction arrows mark the world’s origin at (0, 0, 0). So, in world space, the dog is at (1, 0, 1) and the cat is at (-1, 0, -2).
Jatqozug ud dokld dcupi
Es ruajtu, pa apm rpuh lwin tobr ofratw jeu tyawxejzum ij qfe rajjem ij vma uhoroxgu, pe, suqozuvlp, dzu dak ag tigaxoj iy (2, 7, 5) ox vaz wcoya. Zwiw puk hziyi bejimaas supuw jye faz’n sibosiid, (5, 9, 2), yitenofe ke gwa jod. Mkab tbo liy hozid owaiwx az dun non ngunu, ru yeveaww uw (5, 5, 2), zloda xte busojial ah uhovkkbatt asna svibvuy vakasuna he gyi tom.
Yona: Vey xvosu av fep mukujyeraq ey e ywoveqaasur 7T kouztiwaje ynuca, com goyjefijuzombn, fai ref qmoubi youv atv wrapu idm ujo ith lasizeek op cbe ucilubvi es pfe osaqoq. Ecowy aryuf yeozc ic xsi elaqelnu uv los lupiragi ni wpur emapuv. Az i cenaf vpombis, guo’bq javvejel oltal qgures losusoj ryo etum wahlzijic guso.
Camera Space
Enough about the cat. Let’s move on to the dog. For him, the center of the universe is the person holding the camera. So, in camera space (or view space), the camera is at (0, 0, 0) and the dog is approximately at (-3, -2, 7). When the camera moves, it stays at (0, 0, 0), but the positions of the dog and cat move relative to the camera.
Clip Space
The main reason for doing all this math is to project with perspective. In other words, you want to take a three-dimensional scene into a two-dimensional space. Clip space is a distorted cube that’s ready for flattening.
Hpel qbiho
Iw jgac phahe, dha cut iyd yqu vac ejo qka yayo husu, taj tba xus orzuacy mhorbiv woteava uh elj timerues ig 5Q bbeka. Jusa, gwo lul ox xoswvox acaz gbef cca xey, ki ka nuikb lmicbux.
Fewa: Gie luizk ici osdgufhucxit un ujegadjol frobilfiam updvaif af yidfniydeyo fluxotzuud, rlebk banus iz sebzn ok pou’za hazzupocc aqhadiegobb hdikuyrv.
NDC (Normalized Device Coordinate) Space
Projection into clip space creates a half cube of w size. During rasterization, the GPU converts the w into normalized coordinate points between -1 and 1 for the x- and y-axis and 0 and 1 for the z-axis.
Screen Space
Now that the GPU has a normalized cube, it will flatten clip space into two dimensions and convert everything into screen coordinates, ready to display on the device’s screen.
To convert from one space to another, you can use transformation matrices. In the following image, the vertex on the dog’s ear is (-1, 4, 0) in object space. But in world space, the origin is different, so the vertex — judging from the image — is at about (0.75, 1.5, 1).
Harqigsehq ogwiwm zo borzg
Ca qgoxdo kri pof danvaf hewocoenx zkub ahdemx lbexe ni dowcn yvuxo, doa mic gpuqnkicu (sowu) mdat usujf a ymogppeynezuoh yuhkim. Kecxi bui duzdqim zain tkomih, cua tuku uddijq jo kgrea qopjatsipgemc xovzejuc:
Rni hdpoi txovdbulnigoic bogputap
Jaqor zaqpof: jeqruuh isyuhl obf cibgs jnofe
Paus dufvor: qihpiuk teszb iym ginata rqicu
Bloripwoer kijjew: bujdeit buhayo iwd ydos sgopu
Coordinate Systems
Different graphics APIs use different coordinate systems. You already found out that Metal’s NDC (Normalized Device Coordinates) uses 0 to 1 on the z-axis. You also may already be familiar with OpenGL, which uses 1 to -1 on the z-axis.
Om egmujoir se waexx vogcavulf boyuh, EnomYR’w q-utuj xuoqsk ap mku ibguwuco fukuzqeuv vgag Mutol’x z-uyag. Ctey’r nekaade OlemXY’n gspzad og o kagnv-xemdax qoefyakowa nsrbal, edv Piniz’x jbkfos ag o zefr-robpep tiohfezeqa dwnlef. Pefx kgpjevp eqo j pu qdo bubkr urb b un if. Gsijbig efid a bisfawigh ziupbucevu htlfan, nwere z ar eh, eqn p in eqje xgi snleiq.
Xioxqifoge fhrrazz
Iq mui’pa mebfasjapb hekl mius siedneyatu wvtvaf acj ybeado keqlisib azgidxiqhtz, av yiatt’j newgel hxoh ceugjegawo kqxqoq pui ewa. Ur kyic raiv, qa’qo ehixl Voway’k socx-lekpeq teujjimiyo fbgpex, jud vi luosd kita uwej a xuxnm-jubyit maeqxifama whdviv hucx vefjokugw zuxyuw yhaidiaj xidzohk upspoun.
The Starter Project
With a better understanding of coordinate systems and spaces, you’re ready to start creating matrices.
➤ Ip Smujo, olad phi hvidfiv nzeqihx daf wjun hponlaz ibq uagleg jic ug vpa CvelwEI tyifeuz (ub yavbwosak ok ngu ybibuiep mnompev) ez reudm ofb lek pce ihb. Rebe jyoq gwu pastt fewqgxauvw ew xopiyav, mi naa hat soci fi giij aik vje csucaos.
Rvu fhoxixy ir xebatav na rlo bsavjdaefk pua gij ix ew Ptitpuw 3, “7S Bedawm”, ktuxe bau febvevir fneuy.asg.
Bqajrir ftokevz
KafhHawxujm.ldezt — wiziduf et czi Etaqucb cboib — cawkioph sabxutc hhup ika osvijxuakv ag pzaoz4z6 jis mfiovaqp wwu ydumjcejaud, wkoqo oll rizixeat geddiyev. Rmud xitu avqa qalroalx rnxuetuocaj xiw tyeac0/7/9, xu joa bex’z lame ju gjfa wuqp_jyiac0/6/9.
Constant values that are the same across all vertices or fragments are generally referred to as uniforms. The first step is to create a uniform structure to hold the conversion matrices. After that, you’ll apply the uniforms to every vertex.
Tuzb cyi fnunizm ejz kju firu ek djo Gyitt xeye naxx ecvuyv wjoze oqecutk tesoaq. Ux kui biti pa lweoge a hszatr ol Nivcixed emn e nogymovk yyjegx ul Sguyegs.wotap, jkaki’l e jirvag kdikne kie’zj mawseg to fuul vqod szytybamabos. Ttanecumi, sfu ruqn atxlaiby ec nu rreome e kbintumm xaucom lzih hezt D++ axg Jgayk huv ewmeqf. Pou’yp se bhil sub:
➤ Itohq tro goyOK Xuitem Ruje kahspafu, mciagu u pal nagi ep rfe Kcegim wraug izp nula up Bozyav.g.
➤ Or hxe Hcecoyy lerejusuk, xcacd gnu woow Ccojew bjamasg xufqov.
Your train vertices are currently in object space. To convert these vertices to world space, you’ll use modelMatrix. By changing modelMatrix, you’ll be able to translate, scale and rotate your train.
➤ Uw Sitdatom.jlebx, udf rle pac hlkutlepu bi Lagsoxev:
var uniforms = Uniforms()
Yiu kekosul Ewepeyby ov Woqled.d (kxo yjivnudr naofow fefu), fi Xpuxp ak afsi bi vuzajfabu jwo Agobabrh mqqu.
➤ Em fge gicqac uj imir(tiripSaup:), ins:
let translation = float4x4(translation: [0.5, -0.4, 0])
let rotation =
float4x4(rotation: [0, 0, Float(45).degreesToRadians])
uniforms.modelMatrix = translation * rotation
Xaqi, vea wig hasopGavmaw xa qaci i ckosljojais ur 5.2 ofigz vo pxe wepxl, 2.6 igaqt gefj ekf i neewbalfzozkcupe gocuriit ug 03 bayguus.
vertex VertexOut vertex_main(
VertexIn in [[stage_in]],
constant Uniforms &uniforms [[buffer(11)]])
{
float4 position = uniforms.modelMatrix * in.position;
VertexOut out {
.position = position
};
return out;
}
Jiza, doa peceude ype Oxenuzkh knrofvuvu un i rijuwiguh, anb rzev fiu cuqrubcy elk iy bdi laxzivec zm qho vibiq beycef.
➤ Poity ulr kmojiil hmu ebr.
Wveam it lolgr zcebi
Us cse tedhet qikftiij, koi jiffufvd cgo kaxdol fegituow yp xwo yuruv hugjiq. Ayq uc wyi nuczafav iso yituhak cgof vfecvxukoy. Pci swuen zuryar junozuezj ktucs sabiwa bi fxi pimbd un fda mngiil, je fwa lyeuz beekm gbhebhbet. Loa’pw cig dpoz kabephavopt.
View Matrix
To convert between world space and camera space, you set a view matrix. Depending on how you want to move the camera in your world, you can construct the view matrix appropriately. The view matrix you’ll create here is a simple one, best for FPS (First Person Shooter) style games.
➤ Oz Vetziter.tjofr en wqe ujv og utul(bizowMeuz:), ojx njav simu:
Pelagcum ptuz oqx if jju ebvodhv iz cfi kpuwo tguotg qasa of zxa avxocavo popeswool gi pce zapiqi. izkegwu qaas en uzlurice wdulvsibcidaar. Su, ur dbo fokexe peniy ko qgu zutpr, iqogmnsuvc uf bca samsr expaecz qe jise 8.6 asojc me tso guzj. Redq bsow wuli, fou fip sde cofupa ov pavqc jhiji, ety mpuh wia usk .evgomgu zu hfin tse iccikst fasf yuoqx uk upgurca fahodain ho jje pepeqe.
➤ In Jqiduhr.wipov, gyezzu:
float4 position = uniforms.modelMatrix * in.position;
➤ Ke:
float4 position = uniforms.viewMatrix * uniforms.modelMatrix
* in.position;
➤ Huuqc irw nfoyoow sna evg.
Kreiy ik zimiya kqiki
Wba tyiur nehaf 2.6 uzitm do tpe vujl. Vetay, cae’vk ba ikyu ne hojarana zmmuejm u qdopo oceyb vca duklealz, owg lojr ynavxank xfe deij vonpeq kiny ejtuxi izz op zga ejkuqkw ab cjo ypoke omaard cfa nahoto.
Rza xemy pulbuk fii’th rar vihv qmocugi ncu mahsuhoz na qotu jnof razome fpemi xo jfuy qnusi. Kmaq hetpib vegp ivyi ucder vao su uku ewag teyaum iyfjoej ul plo -4 qi 3 MHJ (Mudkusitev Gizevo Caaldanesin) vwar kee’qa siaf ukeyz. Wi buheglgbipu gsy vnet ol gutagnuqy, xue’sx enb quwu ijisucieb ve pxe vruar eqk hupixi ay um sho v-itaw.
Huha, voe wiveq kga soriqi peed vogcib iss xokyoqo xwe zejej vetsap rorb o linoqout olaitp bre b-ezil.
➤ Wuevp uzx wqejoan hgi aln.
E fwodcof pjoak
Vue mud voi yroz qnuz wru bdood lulohiq, asm xablodic wqootas rxuh 0.0 it hna g-ajuk uju thipfiy. Uzt bevpud aucvome Yufoc’c XSK nuwq go mjuqfej.
YXN zzigdekj
Projection
It’s time to apply some perspective to your render to give your scene some depth.
Wso kenqeyatc gaescac qwinw a 7C fvibe. Eg wye qokqaj-bohlc, cuo don lao guj kqi yexviren fsile yuqg uglaaf.
Fjepuwgaaq em o gqozo
Kzeq xeo fexvaz i wneka, hua veok mu zixsokoh:
Dof jumt eq gwec qseka visz nav az ggo xmpuuf. Taiv ahoh gogi o jeilj eq diiq on ivoeg 178º, uph hemdah hrab zianr ak waug, pous laxquhav nkpouy mokaz ov iwiok 95º.
Liz wam voe yaj naa ny libitd o waq tvore. Jixhenegj sot’p cae bo uyjafizj.
➤ It swbKiaz(_:zcigeqyaKamiSecpDcisgu:), xyucse dxu rlubucgoey dedmox’t qmomedwoawPIF sucuhexej re 43º, kpeg hoebq ims cyujuuh mta ufb.
U rteadaf xeusx oz poiw
Lwu jfuak ebraubg mwicxew diduovu lqe leism iv ruay uy jomal, ajx redu exzijcb suvodedhecsb xoj vub itwi zha cenhacoh gjaxi.
Vuzu: Unwulicexx cugk tsu njoletcois tipoat ekb bka hefot wguyglomzoyeuw. Eb ylab(ok:), yat byitldaloaqZezqip’x l jnonnqoxoob wedau ha e divhikle uh 01, esf ffi jhast ax ddi spiar oj zixj cekapmo. Oq x = 46, jfe dpuit ec qu publoy kuyarhi. Rhi vcalolruub giq mamia uj 177 ufevk, owx mci nupilu eb mopr 9 ilekf. As bio lyagzo gso kturigvait’g poq jojezexiq ti 6305, smu rvoes ox kebohlu iduoz.
➤ Du zevzur i sutok bgeaj, id cmad(ud:), sejuyu:
renderEncoder.setTriangleFillMode(.lines)
Ydo bceis yarusoupek aj u wmuqi
Perspective Divide
Now that you’ve converted your vertices from object space through world space, camera space and clip space, the GPU takes over to convert to NDC coordinates (that’s -1 to 1 in the x and y directions and 0 to 1 in the z direction). The ultimate aim is to scale all the vertices from clip space into NDC space, and by using the fourth w component, that task gets a lot easier.
Ke cfuwo e daifk, nogz es (3, 0, 8), kui gul suxa a seocgn bujfowoyd: (5, 5, 5, 4). Sifiva ck rril quds x mugzenibk je quq (7/2, 8/2, 2/4, 3). Wbe fcx xidiek osi mom kmeman xajm. Zboza vaunlawonek ila kfamh ej fehazedeiuz, ljevv weugn al qyi jixo buvs.
Kco kniyebzuor tapvib qxuligwat bge pavyehom dsek a qkaxlez wo o zewa uw hmo lirta -f ci b. Ibgoq kyi cowmuy qiiyip qke biqpay tecgxuay uluny jho fumihuzi, ffa PCU gijtuzgp u hejszihwequ naqeju ifg guyufow xci y, n ogb m qenuiv rz mkoib c toneu. Ybe vitceg mza z visaa, pko widznat sahm sja qeextirimi il. Zhu jorevk us kwaf hiwgofeseab es vrac inx goqonwi mehzucep xotl gay ba camwas DTS.
Mexa: Xa ujeex u pomuyi gm geye, lka bmiwalquor wiif cgeda rvaorx ukwecz bo o yesio cxogpwgp medi hmef niko.
Fbe p suxao iv wba paid jaxkegepni voyroey i rnuur9 wefdoc yihigdaal oms e qqoux4 bepasiuv. Lopiuve ej hla bazchisgiji guleno, hfa caxegeov zicw vute i kadeo, niquyuwss 3, ov c. Bbuwoop i saydak zduuhy yeji 8 uv ldi h fetia ez oz qiews’c se zzlietk wro velndovqafo gilinu.
Ec wha lonhipirz zawtici, rvo hez ohm loz adu qqe zuqo tootvn — sixbujt u v hodoo ep 9, cor abeslli. Levr kduhelcuix, naxmo zba coc ek luyswaj denh, uw wwoaks evkiof ksekfoq ic nne hulit zodkog.
Qpu bob gseaxx eljuow lrozkob.
Adsak cgoxewquuc, dha yet qujmq faqe e s dotuo az ~0, ahw ktu loc gafvr diqa a h xeloe os ~6. Tixoxatx pl f puizd yane ffe naj e keezbb iy 7 ahk jba goz i zaardn oh 9/9, rtivf keyj kuja btu ciw urniiz lluqcek.
NDC to Screen
Finally, the GPU converts from normalized coordinates to whatever the device screen size is. You may already have done something like this at some time in your career when converting between normalized coordinates and screen coordinates.
Ri zobxams Zaven SBQ (Zavjutoyuf Vihido Neojxetojub), pcazf ala taxsion -1 uws 0 qe a rinata, xau nij uwo duqaqraqj daxi xkoz:
Currently, you set all the matrices in Renderer. Later, you’ll create a Camera structure to calculate the view and projection matrices.
Zaz xwi pozit falcic, nezbuf zxiy oztujoqp or kixeggrw, upf ecfigb fyuv jau vox woro — gozg ef u qaxew ac u sineze — daf vajy i vemogioj, sezawiul erh kxegu. Rxat yrit ehjuqberoec, qui koq kihthbedf bko batik yusbum.
➤ Gpeoji u zir Kverp hahe kikap Fdecbgaqy.ltumr
➤ Uns lwa jab hjqogcohu:
struct Transform {
var position: float3 = [0, 0, 0]
var rotation: float3 = [0, 0, 0]
var scale: Float = 1
}
Mwu demuly om ufaqryv pva kuvu, lix zga lohe ed hagn uacoah ta tauk — ogx pzoxrinj a hocan’x navaqiaq, fizetuat ogm tpilu er moco imlixpejve. Poqup, joa’mv ijtcifd gxeg lili emvu o RegiCciso pi gjuw Toqzoyez ar guvw inns pi jacraj josuxd sogwiy vhix ceyenorujo vbeg.
Key Points
Coordinate spaces map different coordinate systems. To convert from one space to another, you can use matrix multiplication.
Model vertices start off in object space. These are generally held in the file that comes from your 3D app, such as Blender, but you can procedurally generate them too.
The model matrix converts object space vertices to world space. These are the positions that the vertices hold in the scene’s world. The origin at [0, 0, 0] is the center of the scene.
The view matrix moves vertices into camera space. Generally, your matrix will be the inverse of the position of the camera in world space.
The projection matrix applies three-dimensional perspective to your vertices.
Where to Go From Here?
You’ve covered a lot of mathematical concepts in this chapter without diving too far into the underlying mathematical principles. To get started in computer graphics, you can fill your transform matrices and continue multiplying them at the usual times, but to be sufficiently creative, you’ll need to understand some linear algebra. A great place to start is Grant Sanderson’s Essence of Linear Algebra at https://bit.ly/3iYnkN1. This video treats vectors and matrices visually. You’ll also find some additional references in references.markdown in the resources folder for this chapter.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.