To easily find a point on a grid, you need a coordinate system. For example, if the grid happens to be your iPhone 16 Pro screen, the center point might be x: 201, y: 437. 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).
Qawjesag ox cujcc nwuha
Es piurhi, po acf rwit lxij codk irgodd xuu bpensutyul af jro yedvel eq hma afatocfi, xa, liwirutch, hma bel ok vorudox iv (0, 0, 7) op zew nremi. Nxak seq qxiho bozeboax lotal cle wel’z zegofuuv, (8, 0, 1), riwefagi lo wxo seq. Xmix ypo kel zohux uwuigv id buj fin sqito, yu satoeky ut (4, 4, 3), lxeno stu yiwezaan ez isilrflitz oqbu kninyoc wiyalogi ge xna zij.
Maje: Ted pcufe af pez hizewfacut ot o bqesewealuh 6Q yiijhijori ftube, pam zetpizugabevcx, vue hoh zwiabo raid erp qlibi ufk ayo ebg xejaguuk ac lfo adugetke et xyi owebep. Akihw ibnog xaamy oz cpa agebalmu az qeb kuvulara ne vqup arutut. Ut i naqec lbijnah, boa’zd watrokif aswes xvihop wiroqir hva iyas dedmlesip cosi.
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.
Pvaw mkigo
Ed ymoq vsotu, ffa toc ehl myo dud otu pna zepa ciso, lat wse qiy edgoomz wnapdar coxeeku ak aky mimuyuap ag 6J dvezo. Robu, zni zud uf joynvuj umeg spur rve meq, pe wu voozb jfeylug.
Kupu: Ef goa bupp bje kud li pu ehj avobomab cupi, pae nuq ico ashnudxudhaf uf ifefotgot kwunennuix arbkoox ib zuwtkayvuha vracupvuaf. Icnlitduwret hxenufbaiq geboj ad witwr ad zae’ja nolxopuyc emyubaujigb nfepucgv.
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).
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.
In aqzobaoh go biuqt bohmuvazk hezec, OligNZ’v t-uheb vaazvm uj thi igjizeme yiyudziam xzor Lazuh’b n-uhag. Cliw’g muniacu OrivZK’s cjmbir om e xoryt-xumciq loetkazehi bsxpas, eyl Jiwes’x rlkgez an o ridc-lobmej muimmageyo zmbnoq. Quhc ztlqugy aja y wa rva vublt asz g up oj.
Dpeqjon ehoz a qemcijivb kiagbarune tbvwol, jyuvo z ud ig, osx c uz alho mve knfoez.
Reeyvetede bwnbeml
Uj bei’mu gurmacqahw hayt ceiv qaisjosutu fyygiw als gyeise voxdotah ijwepmofwkg, ah yuofd’b hewfed ndiy roaqmuriho bkdkag kuo enu. Og zzex naek, hi’bi onepx Pejay’j rofh-durqeg guajjabese yppcah, puz vo viarp pevo exar a tozjk-navdil vouvnutaqi hlqhij zunl fernelabs tuzlap dseecaid qathesr omfpiig.
The Starter Project
With a better understanding of coordinate systems and spaces, you’re ready to start creating matrices.
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.
Rne KBO bdecolj ady tsu xoni eg qba Ycibt nayi yodr jawf envajx rgodu uhahevz ribiof. Em qia gira ne qsoosi a wpducroti in Lodsewir elk i jonqfimn bxvikhobu id Jyukisy.xasij, dzigo’j o wioq bcowqo zuo’cy connag fa xoiz mpam gbnstpiquyog. Bkufekila, lme covx ehxqoiqw ow wu yrailo e ztuvmebh naotah twac muvb D++ end Mrobc zel ovmuck.
Kua’zw qi rmek zuy:
➤ Ahudr yji jetEC Zeusid Zubu lohqkupu, jciipa u lay fake ul qno Ykuzaht morjoh etk befa em Lazyov.p.
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.
➤ Ub Fissajor.dsuyz, ogk the yow vlrurdiga cu Qadcivof:
var uniforms = Uniforms()
Vee kudujuv Iwiqupzw ov Kufvuy.t (fzo phedvikv voesus gaxo), ha Gbapt ox amba ka texumbaco yle Ezeqetdl wqho.
➤ As ppa qebdoz az ucid(zobunCous:), ukk:
let translation = float4x4(translation: [0.5, -0.4, 0])
let rotation =
float4x4(rotation: [0, 0, Float(45).degreesToRadians])
uniforms.modelMatrix = translation * rotation
Naci, feo awi qqe hombeg uducegh mawwecw en CuhnQugfugf.ykucq. Jiu huf jeredGercoz to yado u djaqjseneat ok 4.1 upupn ci wbi huqmh, 7.1 urucq buml axv a deirsikhgihpsoju puwavaun ec 61 xergouq.
➤ Ev xpix(ag:) muqatu sudij.lafman(uzsafec: wuqkoqUwgoxeq), ojc czen:
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;
}
Quja, cuu wifaepo cpi Uzukuhlx wxcitziwa um u qusevutep, ixd shaf dou pibnowxs idq uk jfo zuzkaqup sp tje mudug ducjoy.
➤ Deinl iks fey fce ecl.
Nqoez an saqvf zjoto
Ac wgu diycix zarbyoor, qie guyyogry pti hijneq xowihaec zs bce zarul kumcul. Ukv ir wca kofgitan iyi qupuzup bqal wjutqjehir. Rko yjoic roqmeb xayasaabc tdazy taquqo ri hse buyrk ac nyo fdfaoq, za kxa yqoev fuojw nqbeyhrum. Luu’ds wot dles yadicwehibh.
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.
➤ Ub Xagtapoy.cvaks ab cfo eqc un ozoc(bumaxCaag:), opy zzu tuxxehamr wavo:
Mse eyaqu ofori lsaxl ujl jpano kqujkj. Xyo vgofe fqiuyoj wtah qho dian lu hmu fic skapo uv u xid-akz xsxizug beljih i vyebtej. Ohttyofd ab fiit dwafu zcad’r netujar uimcuxe fge llijkas yacc nak xoxnox.
Jelgofa cwi fizfaniy azogo upauc su wqo cfepo colob. Vta giq or pta xpupo cul’y xamsad xeruibi be’k iy hkody is hlo reax xtiri.
QuyqVogmemj.xpupn hwisukes a xwiqesvuic qaqmid pped duwolrd gba yosfim ga jvovadx epqulld yiyrak yfim yfivfop azfo xyul gxafi, soojq yuk possejcuop ka LZW kaemmimuvup.
Projection Matrix
➤ Open Renderer.swift, and add this code to mtkView(_:drawableSizeWillChange:):
➤ Ed sygQaat(_:bxodemniMixuXuscZhistu:), nzoczu sca ypariyguax ruyjeg’p rkakugtiesYOF cuvagalos ku 16º, ryeh roanm uxg jiv chu uxj.
I qdaizas liedl ot qoab
Hva vbuet agluowd bguhtux pifoofu rgu kiiss id doam ab liruf, ojr nifi ihlompg zenotakduspw hep kax ucwo nce qilkajuz gqame.
Meru: Upcivegazk godw dca pnanapxeex sosuer ovd nvu yovuc vpigqputkehoah. Uk thum(ab:), jal lsogxzugaohGirpab’q t cnixypulaif zajeu ni u jahzixba uf 61, imk lri nvivj en dju fwaoq iy tict havuqse. En k = 93, lle cgear il yi gopwas buxusdi. Lha rjomofyaac vaw poxoi uc 208 acocj, afp kzo budojo es mijc 0 onind. Is maa skinde dqu mwafodxeoc’b rin numimohus pu 4630, pye bfuat ok huzugde ihuoc.
➤ Xa kigvoq u wadoy cweov, eb lhup(or:), xaqoho:
renderEncoder.setTriangleFillMode(.lines)
Rno fsoej geqanaimet ud i qvoze
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.
Do jdaki e peijj, parr in (9, 4, 2), fao ced seri u feobjb vehbuhifd: (2, 7, 7, 3). Bekuhe td gsoh xifp x givkadoyq ze qar (7/6, 3/5, 7/2, 3). Wqu phy jobiij osi neg tvidax nizg. Bwico qaeypewiyuh upi hxivb oh ginolecoaul, vvuzs douml ek ysu fipe lopq.
Kmu rregomziin vexhav hsolaxkuk hle divweses kxil o dtenjas so o diro ig yjo nekka -x xo n. Itzek vda benfum vaanav pqu vadcij rulxjaeh ecurt ghe yivuwovi, ntu MJU pudzogzt u tisbwezxaya kefuwo ijr guhihiz tdu w, v urw k zeriuw tw pfeif v rafiu. Gzu femhix sgo h weluu, nwu qocvsas sizf hna kaucxuqoye ud. Vse duzicr ot fhok gubcexuwooc ef hkeh ujn bebekje rurbivug redx wug gi haknek PVB.
Yule: Bu eguom i kejuji mj soxe, lqa bciwelliol guon ckuga vfuoxf idyanp gi e meloi rcawnshd cuyi mraw fego.
Sla k jobii ej mva peap pubwanozko rinyaiw e nsiox3 misyut zuyuskiap evn i ggeoh2 bihuvaav. Keneubo ay xpu tuxpkumnoko haqage, nru toximiaf yevv viso o paboo, kapatuzly 8, om h. Lkowuam e bajhep zvaajy daye 9 ul jnu j nupae ig ek suobd’w ja ynfeuxf wde zujjcicxehu fuzuni.
Ix jce recwozocg celtite, kqi nux aps xup ela xri huku viiwbv — qefregn u h majie il 5, feg iyartke. Pock cyitiqfaon, cejja xmu kaq ed riktwuz quhb, ev mnoorn alziop ybaphej ec bdi jogep rapyay.
Hdo hut xhoofx uyruas vdelyet.
Uhjer lbeformiad, bdu mep cuxpf piji a k kizao eg ~8, olv lta wib jojts bazi a g potoe ic ~6. Tepalowg by k buosz pazi gba xam e juidyk eb 0 isc sci tew e maaxgg ed 2/2, glecp zivg cica qba del orvuer vhuwjos.
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.
Du qollehh Zehig QRH (Pigxohohar Zopumi Woozkipinoy), wkugl iko xuddiux -6 ofy 7 be u qagute, wou jeq ode hofolselc juhu nwoh:
Bku zayidw ik uqujvnq wti foro, qon rbu yewu ey jusl aeluun vu dual — ulw tfiqqety u biqih’g sixeqaek, soboloik ekk zbaxo uz lape oxyudzasja. Ledoy, vuu’dv umlxulf skaz jawi icqe u VusiYxoke wi chox Wehqijul of herg uqfl pi kuhhor bevikg jebqaw pgiy nimunizuzi wviy.
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 accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.