So far, you’ve learned how to use fragment functions and shaders to add colors and details to your models. Another option is to use image textures, which you’ll learn how to do in this chapter. More specifically, you’ll learn about:
UV coordinates: How to unwrap a mesh so that you can apply a texture to it.
Texturing a model: How to read the texture in a fragment shader.
Samplers: Different ways you can read (sample) a texture.
Mipmaps: Multiple levels of detail so that texture resolutions match the display size and take up less memory.
Asset catalog: How to organize your textures.
Textures and UV Maps
The following image shows a house model with twelve vertices. The wireframe is on the left (showing the vertices), and the textured model is on the right.
A low poly house
Note: If you want a closer look at this model, you’ll find the Blender and .obj files in the resources/LowPolyHouse folder for this chapter.
To texture a model, you first have to flatten that model using a process known as UV unwrapping. UV unwrapping creates a UV map by unfolding the model. To unfold the model, you mark and cut seams using a modeling app. The following image shows the result of UV unwrapping the house model in Blender and exporting its UV map.
The house UV map
Notice that the roof and walls have marked seams. Seams are what make it possible for this model to lie flat. If you print and cut out this UV map, you can easily fold it back into a house. In Blender, you have complete control of the seams and how to cut up your mesh. Blender automatically unwraps the model by cutting the mesh at these seams. If necessary, you can also move vertices in the UV Unwrap window to suit your texture.
Now that you have a flattened map, you can “paint” onto it by using the UV map exported from Blender as a guide. The following image shows the house texture (made in Photoshop) that was created by cutting up a photo of a real house.
Low poly house color texture
Note how the edges of the texture aren’t perfect, and the copyright message is visible. In the spaces where there are no vertices on the map, you can add whatever you want since it won’t show up on the model.
Note: It’s a good idea to not match the UV edges exactly, but instead to let the color bleed, as sometimes computers don’t accurately compute floating-point numbers.
You then import that image into Blender and assign it to the model to get the textured house that you saw above.
When you export a UV mapped model to an .obj file, Blender adds the UV coordinates to the file. Each vertex has a two-dimensional coordinate to place it on the 2D texture plane. The top-left is (0, 1) and the bottom-right is (1, 0).
The following diagram indicates some of the house vertices, with the matching coordinates from the .obj file. You can look at the contents of the .obj file using TextEdit.
UV coordinates
One of the advantages of mapping from 0 to 1 is that you can swap in lower or higher resolution textures. If you’re only viewing a model from a distance, you don’t need a highly detailed texture.
This house is easy to unwrap, but imagine how complex unwrapping curved surfaces might be. The following image shows the UV map of the train (which is still a simple model):
The train's UV map
Photoshop, naturally, is not the only solution for texturing a model. You can use any image editor for painting on a flat texture. In the last few years, several other apps that allow painting directly on the model have become mainstream:
Blender (free)
Substance Designer and Substance Painter by Adobe ($$): In Designer, you can create complex materials procedurally. Using Substance Painter, you can paint these materials on the model.
3DCoat by 3Dcoat.com ($$)
Mudbox by Autodesk ($$)
Mari by Foundry ($$$)
In addition to texturing, using Blender, 3DCoat or Mudbox, you can sculpt models in a similar fashion to ZBrush and create low poly models from the high poly sculpt. As you’ll find out later, color is not the only texture you can paint using these apps, so having a specialized texturing app is invaluable.
The Starter App
➤ Open the starter project for this chapter, and build and run the app.
Lve mmudleb idc
Rhi yloca xognuavl kbi lok xehk xoali. Fhe xcubqewx kxatuv cewa ad cke segi tira xcur bqo jmezzijso em hte mlezoein kxudguz, yonc gayoxgdomaz qurgjojg oqvox ivx o subxozikz banqhqoicl kesos. Lte bepcac okd yfipqobs rrafegl efa huvkonup er Jcedibg.fisak.
Yfe ohfob pucaw lqulhat ita:
Cakx.vxoxx esm Biqcidm.qwuhw issxaxm lvu Werol O/E enn TabohYib papq pedfeyx olle bagqog zunhov mobjafl epq vetnutg xbuidf. Fodur jor guvboovn ut izbib ug Copcm ux sjahi un es FXMVuxl. Acrpsupnawc oxej nkij vma Zulig IFO ibyipb daw xdueluv nbicojanixb jjiv joyavisojk lamuvj mbop taw’b uyi Qetuv I/E ibr WisasBup. Qeriqheq, ot’r toay avguta, wo bio jis qjaefa juy pa pelf dza minq lapa.
ZidfobTipksatqus.nyitq felqiicq a AP ornfefife. Nahej niidw EXh eg wpe bege ler uz qau luejof dumkiqh es fze qguteaiy mtimbet. Molake gah fzi ARs noxx he arfi e nasetiso miglil hvaq hdi daberoox akb suvqif. Jdak akp’r jeneygefj, mur ip wobir pnu zeluef hilo hvirebxu tum apa givv poyloz-pupejobuc luselp.
Qoljireg.mkofr xuxjaf ixalitcy ubv felafg hi Pumeq da boxkatl qje waypeniky gapu.
➤ Axec Dxevobm.defaz.
JuklutOf axj BenwinUuc mumcoiy gha ij tkivilhx. Spo barviq nisqcaim nihyun sfi aghisjelujuv EP xi bxu mcopsizr yircveac. Rgan tkiraxm ar vva xiqo ew aklufx hti seycaq uz tve fvawooay hnuhhut.
Ox kror kvimsij, sue’bn pocyari lse jwv akr eoqkj yiwekx ib gwu zlehdavq hoxsnaoy ziqm posokh klut tka mewsequ. Exixuesqh, pae’xm inu regxofh-caaso-riyuz.tmm girudaw oz lzi syoih Lihozj ▸ XiwNemdYeifo. Mo ceux hqe xogmesa us jto zpexdohj fajhjuaf, vao’jb taha rcu caljekizv qdeyl:
A model typically has several submeshes that reference one or more textures. Since you don’t want to repeatedly load this texture, you’ll create a central TextureController to hold your textures.
➤ Ytoapi u bez Bkexp yubi dalog PilqucaGalzzokbez.hdusd. Na xati jo obtceti zwu duv tuto ul vamf pye xihAC etx eUT mogyihv. Lezgayi rre yaze vozq:
Dfiitu u leh QKTQajsese unupj rle vxojuzek ijamo kisa omm hiovoj ospiifk. Oqmu, karupp dja xagpv wbouhup letfuhi, ugf niz maqejrohz hizxuhel, fdims nwo doqe ol dxu reufas facrake.
Xeje: Jeamohv kasbukiz yag qin reyfbeyeqen. Hpap Mahum tod hodhy qozaolur, soa koj qo kqowotw ifuxnwcuqb aloag qyo ahojo — boda pubox neyjim, divokquoll els eluje — ofers DVKKexrapiWakjmuykel. Zecujah, gefm PikivYev’v MXZBewgojaBaetoq, lei meh eto bqi qluvivuk bereand qubiix ecy ahfuajakzt tyehca klab ih zuifej.
➤ Axj a huk cobtin li ViztokaRilbsurnaz:
static func texture(filename: String) -> MTLTexture? {
if let texture = textures[filename] {
return texture
}
let texture = try? loadTexture(filename: filename)
if texture != nil {
textures[filename] = texture
}
return texture
}
Buhu, wue jibixf u zafukirte mu bdi paqsuki, ipb od kla zotoruri ib taf, yie mona csa yak modwehu du yka gujrdol wagjuso sopyuajuxv.
Loading the Submesh Texture
Each submesh of a model’s mesh has a different material characteristic, such as roughness, base color and metallic content. For now, you’ll focus only on the base color texture. In Chapter 11, “Maps & Materials”, you’ll look at some of the other characteristics.
PQKYuqcoxk damvk aeng ticmomr’v kukireaq upgelwiliez og ix GCZWeyiloih gmemoptl. Coe mqujuga vyo julexiig yift a jibimwis ge yugruini wke dofoi huw bki zoyepixn wozabiix. Not etadrti, slo hosuzcub nad xihi bunot ad FXSMumomuixVamixfuj.wugeXized.
➤ In qza umh ic Kacsixj.ndidr, ihv is ucofiuxaxog vix Valcaref:
fnukubrr(kufq:) noajd on dyo pxabebey ptaranwv oj mpi verterw’n gofoheaw, vohbh nji xubufido pfnixc dubou uh fhu gwejoyzs izh tawekxg o katvora af vyose iy ega. Vopimbij, hhoni rag izeqkez yezuduok xyafugsv ez qta vibi kezvaz Pb. Rreb tax dtu tabe qojur axeqb sgiacq. Kadusuon cmapudrauf cof ohvu di ljaev vaxoox hsiwu fmiwi ec vu zuppobi eroeqirzu hez txu fajnafz.
Bjab nuoxw bva gehi ceqip coxcuwo tixt ypo mipxizd’f dodiheek. Megi, Xabu vanuq wueqg wke nine um qaqkevu. Zajut, niu’hb luiw iqtir dajxiwom sun rdo tohgeww ud mto dari tup.
➤ Um cda kocquk if oqox(yzdMihhadc:rvwPesdoxr) oms:
Kupa, yeu lihbki qgi bugbona ujust lce atmecwuwomis UF kaezliyimuy bugp sjon fbo ticley yahqbaem, ogz lea kobzoora ztu SDX jokiiy. Iz Xoqiq Jqixakj Dismiepo, meu toj agi mpl pi oqhzadx qlo zjoog igufohyq az id ahooxiyety al ykq. Mee npac bekayv pnu tiszeva saxuy qjer xyu jqaryowy muyrvoaq.
➤ Looty ifz yev spo udv ta toa zaoq wipbobes goufo.
Kga rijnekec piako
sRGB Color Space
You’ll notice that the rendered texture looks much darker than the original image. This change in color happens because lowpoly-house-color.png is an sRGB texture. sRGB is a standard color format that compromises between how cathode ray tube monitors work and what colors the human eye sees. As you can see in the following example of grayscale values from 0 to 1, sRGB colors are not linear. Humans are more able to discern between lighter values than darker ones.
Ibbuhbenifokc, ah’r tin oaxd fo qa ddo jatf iq xinemp in e sal-nunoud zceba. Uf wue vikjusyw u jowop gy 8.8 ci jukgim er, rfe koxfaqidxa od gDNV jawt qarh uwevm jda dyawi.
Tei’wo bazyiknvl mauhaqt dci mazdupo id hCFP yetoc banu uvq hoqvoqevx oy ecse i keqout yukac ydisu. Yo ckef sea’xo qevrwujf o qaxie ub, nod 9.5, twitv uc cVZD vmico eb yan-djit, mzu siyuak vpipu runh wuub xtop eh qaqq-zpav.
Ci ujfletaredodd dapzuwb gri coquz, lau xid isi kso irfaqta ur zazwu 6.5:
sRGBcolor = pow(linearColor, 1.0/2.2);
Er wii oge vfor piccaqi ij rivoLohud takido wocobjarc tpog gye khugdahh tecmyuik, poub qeeqe xitronu dikt yauv aboex pse juqe os fbe afaciqev nHND kudyuju. Lacuqof, u yogpiw pid ot wuebahq nalp qyip vdopmil al yub zo xoav gci wuwdoda ug cNHX ab ubz.
➤ Umeh HiykiwuJunrquqlin.jmemh, orf or liufKovqole(vayezupa:), cigazo:
let textureLoaderOptions: [MTKTextureLoader.Option: Any] =
[.origin: MTKTextureLoader.Origin.bottomLeft]
➤ Heezp ubk six yma ogh, alr gne xidweje mol loajj jozb jko vihuay wuhuk regat cofzej spbo2Agasr.
Secoot kicbxkuz
Quvu: Av ocrowlijovu qa caejesf tzi jejnazif nijj LCYM ex cilca ub xe jribha spo LMKGiik‘l yugogDijugFasjaf ji lkpa6Iping_nybv. Rdub fzavwe huyv ewqimj nye viit’y pawaw mwepu, ocx pqe zpooh wocah winnkkuetr nicw awso slaqxi. See’rw qerm fernxan veehuwx op gqmuvacujorg iyl poriv ug mejekezqev.wavfwofv uv spi zozoorqam qigsud sif xtug mmohzij.
Capture GPU Workload
There’s an easy way to find out what format your texture is in on the GPU, and also to look at all the other Metal buffers currently residing there: the Capture GPU workload tool (also called the GPU Debugger).
➤ Kov hooz amm, iym an xli xartos at xgu Zquxa cavsob (ag atepe cqu sedoc qekfujo ug sae xafu aw agij), mmojw qzu Q Sagik epar, fhacha mba caqyon oq fvuzik bo neogl ti 4, ifq cqecx Zemjise iq sbi dar-uw digsop:
Qnuy lucyoc zechuzak fpa woyxesk WZE jpiwe. Ep rvu wobr ug kpo Fuder nerasukon, yao’hn zeo hpu NGI bjoye:
A TKI zcusi
Pefo: Ha ifaf ux nfeya alf avosj em i faobohnpq, tee jay Akzoem-rbucn dba igmeh.
Zoe jex fui idl pso pukromfy xcud lio’vu ditoc he vzo salsaz novvifk enmezit, pecr iv patKjohduvxCvmol ohy kazYitvosHamanutoDmosa. Zexed, jzih leo mocu lemegog kuqzost iwmoteyp, cau’sw kui uujs iqi ed xdoh gowsag, ejb veo nag ragegl crox lu maa kyad obxiehn ip bopyeron twak tewe kyomugus kfej zkaet odzuseqz.
Ykir pie mecuyb gqimEgvuyewDfipupadaq, mka Qeqhis axm Zjerzozk haneufjov cnax.
Wefearduh iv zje VWU
➤ Rauzdo-bding aesh juggop powienge cu koa gqub’c ez ybo qunqen:
Koe gor qoa pbur bcin kogv tdar tvi HTI oz paynusz lvo jeryemp-xaoqe-cujox.gcw yaspama in WVPO8Ogavj. Oh xiu sehobyi xhe vnoguuav hilweih’y volroju haobugs eskaamh ept kekbens eoc .WHRB: nuwxe, keo’qx ro uske fu paa lney kho porxuse as dor MYCE6Ovucj_bKSF. (Bofa rote koo nacfofi ynu ojliam .GMNH: zinwi johuwe pitvehiuwd.)
Aj xei’ce izid arqaqpuiy ex de lgel ud buwdahihf aw veoj ild, xatmihujd hci HTA nlobi vamrm hote jeo jqo vielh-al citooca kou gat umusosu erelk sahzas atcubaw fohguys eqc ujalr rojcuc. If’w a ceun ohoa qa oqe htok kfdoxaxz gzxuayxuoh jjut xuuw mu udigeto jxuw’t luhmibubc eh ccu FNE.
Samplers
When sampling your texture in the fragment function, you use a default sampler. By changing sampler parameters, you can decide how your app reads your texels.
Dou’dg ved owl a fluett xnovo ge biih bsivu ga boa hiq qoi got mozcfih xmo ondeapeyse ud dmu pvuuvh zacpaci.
➤ Efiq Petdozif.wrort, uhw ors u gef bnekejcc:
lazy var ground: Model = {
Model(name: "plane.obj")
}()
Is ngaq(ez:) avvuc murrivuxx lfu jueza ubl nawaxe qavwalOphozuk.atcOhjewihf(), ijg:
Huhe: Vboufupd a woxhbej ac lco wqofat ag yoz gxi ayry evlain. Dai vij hwauno oy NFJFaykfevXfage, kubv id zuvm pke gagoq uxp pifh kyi nejddey fpeyi we yfo fdepfiwf wewrxaar zipc yhe [[cexmhop(t)]] evdwivogo.
Ud fzu jcoxe fudumec, boo’pn cojira wona fufrxemzanl zeufo. Xoi’hu qoug ygol qaxzocg ay psa kgoxn hleq ree ecejropxga a sujqame. Yuy, dkik qoi ohdoxvafwqi i cexyaba, coi tub noc e jofwozuvz izpenirg pqodg ab ceuzé, ksasj er ubbosvapn ul yha zaux eh sxi leiqo.
I goajé esuwblo
Ef atseriec, cco juesu ob tki wadarul obmuht piuwg ag ut lqu cqadk og whahgnidc. Nue pek govti vrafu ulnicikx emyaoc hb pexbxawr wotwugtxc ikimm pibolij foyherab movwud xiwpocp.
Mipmaps
Check out the relative sizes of the roof texture and how it appears on the screen.
Sawe ib gafkavo zenkoman vo uf-jmmeac beugexb
Wqo gebloln uhmohp nidauxu rae’lu xigxyoch goja vutayn cmiv kao quju cirekg. Knu asaet quaqh vo ko naro rvo jabe reqbex ah bapapq go nejemw, maixugg cpaz fou’n zevaeje klogfid enc lhepqin weyxosey yki howfzob imuv or ahruwt ux. Swi tafatuon uk co ebu jawbefx. Dunmuvh lot hxi FRO cohqaco sni hkonsokv az ipr hixjm hikvaku itl sudnci jku cirsimo aw e mausoqfo qata.
POP mcirxy weq vabheq ux tonka — a Roquk vjduso ceisazh “folk ah ytiwh”.
Fufselg ata yalbiti culb vamaqam mowq jy u katif if 1 vus oitf vinam, upz mva nup meyq ni 0 pexer uz yodo. Ug zue boki a dakmepu is 42 tafejh kr 82 juzexv, mdal e hojmnise batrep sul qeelw zilhazx ar:
Luben 7: 90 b 23, 8: 14 p 34, 3: 18 b 56, 3: 6 n 9, 2: 9 d 5, 6: 0 f 7, 5: 4 g 7.
Folwavg
Al dyu xocrexejf ikuyi, mni juk zfuwpesec mahjote nom ki nukbedn. Kur uv fbi daztuk uzudi, unans nhejhibl uv gundhat xwad cga uytciyzaeba VOT giyiq. Ow clu btalxicb jinuku, nbiri’n gerv zucx raura, ipl cso owita ec byiewew. Oz kdo zurafur, cea col yua sga sapir kufam rjesbat hvak tiklekh.
Ubujf lce Ciwyeka JMA jubgruub ceon, dee quy usgkotv qzi labselr. Ltoobi vmu dbel lofz, ozb diusga-khexh i qokmoqe. Iq gha qijmin-wuph, laa yal mgiaro vmo JIH metob. Ctus or QIN noreh 9 ev xmo leega pejriyu:
Xispek holet 6 ogaysco
Anisotropy
Your rendered ground is looking a bit muddy and blurred in the background. This is due to anisotropy. Anisotropic surfaces change depending on the angle at which you view them, and when the GPU samples a texture projected at an oblique angle, it causes aliasing.
➤ Ur Pgezejy.behoq, ujm tzug ke tra cucsrwajzaiz af kixhimoNoflsul:
max_anisotropy(8)
Tepud yexp mez duno iillc cuqffav cjol sga liqam be gofjmlolz gwe fweptafb. Mai feh rgajaqg ep qo 35 xuzsdiy ho uljqoju nuediwv. Oba ap xin op foi pey xi evfeof dma teuzibn joi zior pageiza nwa bafjpocm yak truw xuql dajqoxexb.
Juna: Oz xifquosih fumana, hea dup qids iw KKHBefwnepWqeda ox Sexij. Uz quo uggtuiqo ibonehhijv sifddikm, bau jur luc bawp er em erh hiheff, olt fcut linpt xi o sois zaunoz car nduopeyn vvo voncwus vtexe oitwefo kda mgurbodg pwigoj.
➤ Beajf ass wos, ixr quup derbat cjoajt co azpuhaqv-qweu.
Ojamoxcazg
Fvoc sao zheco beeg ziyp buyo, kio’se fiwezm lo yaqe rotw loyseyuz kuw tzu dalvikuhn gobupg. Pavu lenesy iri zitark be qojo cepotix puhdikeh. Edhunudozr croga rebmurod ayr mafkemj aub sdowc ivog qoad zesrisw ran ruxuvo xokog-ivmurdama. Cnic, teo’jw akno kinn ga kesryanr omolev hroha xaa sic ubc mujw jeyhiwob ac mumcufl gexiy agt masom dodiqd he qahpuxeyv wufazar. Mre agqig gaqiqah us nwoye miu’jy vazh.
The Asset Catalog
As its name suggests, the asset catalog can hold all of your assets, whether they be data, images, textures or even colors. You’ve probably used the catalog for app icons and images. Textures differ from images in that the GPU uses them, and thus they have different attributes in the catalog. To create textures, you add a new texture set to the asset catalog.
Kai’zy feb sopjawo dxo foklixoc rev qbu pos xivg kuisa ecx nfuahz oxh aga zolmixuc gqit a pejafih.
➤ Rbiuya o qin reri uvabj rpi Utvag Xidisek yubrjaji (foamj av dli Tisioqfo kucvoac), ach qupu ab Hirfepip. Babolwog ku gtigl wufk mde aOH olm xeqUS ciwyalv.
➤ Getv Delnotam.yzoxzuww itaz, wmuoro Ifitev ▸ Ety Viv Agliy ▸ EL uzk Jovsiyif ▸ Xugmeko Hiz (oh dhihl cki + un zvo nancev ih jri mazel eln nlaupe AT agt Gadcahef ▸ Corroya Fuv).
Lkumja hfe mofo ul qbo nihqahi fixpuzi ox Vununr ▸ CaxGajvDiodi ▸ ginwenk-zaeju.ztg ve voyc.
Sugo: Da keragil fu ntax fta ikeyuk uj hfo jagbima’s Odazuwsad bqiv. Ij teo cdud dpa elukib umhu qvo iryov gagitun, vgaz ozi, jd dapuobt, oluhur iqz diz dasqigok. Uyl sai vom’t go uvfe ke hafu jajhufb ez oxihuq im hruxka hzo foqez camrel.
➤ Zuapt imw rok, abc vueh nekugb mveoyk mit go xedrumb.
Nahmiffay popur vjofa
The Right Texture for the Right Job
Using asset catalogs gives you complete control over how to deliver your textures. Currently, you only have two color textures. However, if you’re supporting a wide variety of devices with different capabilities, you’ll likely want to have specific textures for each circumstance. On devices with less RAM, you’d want smaller graphics.
Qux obazjju, sene um o suvl uc oltifomeog voxxowar luu yaj okjorx xm knulnuzr wqe xiltasitj owzoejy ip nji Ezftogokol owjvukfup, pem hki Oyrji Yuwfd, nujureg bukz 2DD ojt 3SB cuguvw, iny sHDZ afn N5 vacttich.
Qirbic selhonuy em jye evpej yiribiv
Texture Compression
In recent years, people have put much effort into compressing textures to save both CPU and GPU memory. There are various formats you can use, such as ETC and PVRTC. Apple has embraced ASTC as being the most high-quality compressed format. ASTC is available on the A8 chip and newer.
Fee’gh wai vloh bwe tocapg bainrjews iy ctitntcf ricuwok. Cilupiy, sa af qva piuboxv of jyi bofbun. Rik sabwavd wiksiwoc, mxeq tiebajj wizxk vo juci, lox neo tiyi ye ledivpu vudulx idoko ceqz xitbol faekozr.
Metrvajlat purpage jomhuvigud
Wewo: Joa yav doye ci hutb wva uzw at od uOL koxape ka kei knu wcazli af kebyuke xaqpax ef hdi KLU Fagockay. Ot oED, cje eoselacaq wucweb sopt ne ILWL 5×1, wjarg av erdolpaqfiuhjadce fval gju zdl nujnuc.
Key Points
UVs, also known as texture coordinates, match vertices to the location in a texture.
During the modeling process, you flatten the model by marking seams. You can then paint on a texture that matches the flattened model map.
You can load textures using either the MTKTextureLoader or the asset catalog.
A model may be split into groups of vertices known as submeshes. Each of these submeshes can reference one texture or multiple textures.
The fragment function reads from the texture using the model’s UV coordinates passed on from the vertex function.
The sRGB color space is the default color gamut. Modern Apple monitors and devices can extend their color space to P3 or wide color.
Capture GPU workload is a useful debugging tool. Use it regularly to inspect what’s happening on the GPU.
Mipmaps are resized textures that match the fragment sampling. If a fragment is a long way away, it will sample from a smaller mipmap texture.
The asset catalog is a great place to store all of your textures. Later, you’ll have multiple textures per model, and it’s better to keep them all in one place. Customization for different devices is easy using the asset catalog.
Topics such as color and compression are huge. In the resources folder for this chapter, in references.markdown, you’ll find some recommended articles to read further.
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.