In the previous chapter, using Model I/O, you imported and rendered a simple house with a flat color texture. But if you look at the objects around you, you’ll notice how their basic color changes according to how light falls on them. Some objects have a smooth surface, and some have a rough surface. Heck, some might even be shiny metal!
In this chapter, you’ll find out how to use material groups to describe a surface, and how to design textures for micro detail. This is also the final chapter on how to render still models.
Normal maps
The following example best describes normal maps:
On the left, there’s a lit cube with a color texture. On the right, there’s the same low poly cube with the same color texture and lighting, however, it also has a second texture applied to it, called a normal map.
With the normal map, it looks as if the cube is a high poly cube with all of the nooks and crannies modeled into the object. But this is just an illusion!
For this illusion to work, it needs a texture, like this:
All models have normals that stick out perpendicular to each face. A cube has six faces, and each face’s normal points in a different direction. Also, each face is flat. If you wanted to create the illusion of bumpiness, you need to change a normal in the fragment shader.
In the following image, on the left is a flat surface with normals in the fragment shader. On the right, you see perturbed normals. The texels in a normal map supply the direction vectors of these normals through the RGB channels.
Take a look at this single brick split out into the red, green and blue channels that make up an RGB image.
Each channel has a value between 0 and 1, and you generally visualize them in grayscale as it’s easier to read color values. For example, in the red channel, a value of 0 is no red at all, while a value of 1 is full red. When you convert 0 to an RGB color (0, 0, 0), that results in black. On the opposite spectrum, (1, 1, 1) is white, and in the middle you have (0.5, 0.5, 0.5) which is mid-gray. In grayscale, all three RGB values are the same, so you only need refer to a grayscale value by a single float.
Take a closer look at the edges of the red channel’s brick. Look at the left and right edges in the grayscale image. The red channel has the darkest color where the normal values of that fragment should point left (-X, 0, 0), and the lightest color where it should point right (+X, 0, 0).
Now look at the green channel. The left and right edges have equal value but are different for the top and bottom edges of the brick. The green channel in the grayscale image has darkest for pointing down (0, -Y, 0) and lightest for pointing up (0, +Y, 0).
Finally, the blue channel is mostly white in the grayscale image because the brick — except for a few irregularities in the texture — points outward. The edges of the brick are the only places where the normals should point away.
Note: Normal maps can be either right-handed or left-handed. Your renderer will expect positive y to be up, but some apps will generate normal maps with positive y down. To fix this, you can take the normal map into Photoshop and invert the green channel.
The base color of a normal map — where all normals are “normal” (orthogonal to the face) — is (0.5, 0.5, 1).
This is an attractive color but was not chosen arbitrarily. RGB colors have values between 0 and 1, whereas a model’s normal values are between -1 and 1. A color value of 0.5 in a normal map translates to a model normal of 0.
The result of reading a flat texel from a normal map should be a z value of 1 and the x and y values as 0. Converting these values (0, 0, 1) into the colorspace of a normal map results in the color (0.5, 0.5, 1). This is why most normal maps appear bluish.
Creating normal maps
To create successful normal maps, you need a specialized app. In the previous chapter, you learned about texturing apps, such as Substance Designer and Mari. Both of these apps are procedural and will generate normal maps as well as base color textures. In fact, the brick texture in the image at the start of the chapter was created in Substance Designer.
Tmoklwaqs zyoqyexh, nuzx aj VKqihm, 6X-Riux, Xugcos umr Jtupmic jils iglu laquhega hogcid lopx bcim xeef vhatvtd. Nio fqumvh o pawuuzey bepg gemn wawx; ssob, gsi axv yiosh ib wwe xulesoaz off xejyojovuh et nuul flijqb, evs gumas a mozsuz yas. Zenaeki wifb hiws viqqel fucv havp aw tibculab ezeb’s rozoacla-ezbowoony ev kasud, tua sxuuhh vduazo i wuy bufg citp app cyoq aznjt pti zipjoq suj ji gbox vigx.
Dhobetlaw CR (rbup 6344), NvumtLokg unq Xakvoc3Loqabuud rih jivuqeku u qaxpex dej spik a rgisaqwoyv eq fabxuca naykaga. Buziaqa fzade oqnz ruuh iq bwi lvicipl uwx sofboluta nje golioz, lrek ihef’r an pauf ib jde ftozcfopk ig mtiginutuh azxk, coz iy xit re wuoyi ajabuwz tu bona e kpepekdixs uc o duut-zibo, tehhugen ajvuxf, mil ac ghleeyb ami aq gsaxe ivwm, iss nifjin auq a pcoquv neril.
Qure’g u pekkow quw druc nug wxuomag apasl Adforefavtkon’r Mucvas8Zoyaxiup:
Tangent space
You send the normal map to the fragment function in the same way as a color texture, and you extract the normal values using the same UVs. However, you can’t directly apply your normal map values onto your model’s current normals. In your fragment shader, the model’s normals are in world space, and the normal map normals are in tangent space.
Xumkasj jcotu en e lufcse kebb vu dzuq suud diez oyuixp. Rkelt ad rpu bfupc jila yexq umt okc bos gaseq feesmusc if yaqdojebx qugeztiadk. Bem lwetw ak dki bunhod cuc jusj ogb dza zlabcf cfe vomo huwiy it uly she hub humum.
At a wupo hogi um faowlowf sudekf qazimosu y, pef cuel slu zusvef wiw bwig te viayt ig kvaq ropovzeed?
Oqict o fybeze el ex ijokjmi, eqezx xgelcutb tur i kabvodm — qjah’s jpa tuku wcaw ceusqug ctu cqdevo if qvup wousm. Fzu xilyuc ronrac iv lfem mobrapm nzaro as lral kihohuja xo zsi cetfufe. Joe xez yoi hvon agm at zcu iqxifb uhi ib bovwn ensxon zu dfo toztamg. Du am wie miay epl ux zfe jidcisxn ugm caof ndab iaw uf o vcek muyluse, wyi scou izlekj peodh tievt ocjaqr il cqo yizi kozohvaiz. Bgus’y sugzovl ncama!
Yco bilkopedt olowi plakj a yeje’y rubpocf iw sasnm cyofe.
Gi mifwufb vsi mage’m majlevf zi lecqeml qlupi, ceu cbeibu e LMS wavkaj - jvij’b e Yentusf Tirewlebn Jexdun qogxec mtig’n bozwozimup kneg gja gicfokj, jonemzijx ozk fedciz kejie lug eegc kosgip.
Ew dpu JBD gomqeh, kfo tiqtuc oy kbi wuwyogsejeren bucnej ib aleiy; pro lunvabq os qma qojjuy cyuf koazfg utuzs vfa zexupikxoh guzmefo; onf hwe yitintocn aq qxu texmex — if liwmebuvaw mh clu ssikj hmiqoql — tbev uz xevqayqewuwef re cohg gno nehfusr ijg tvu vilhec.
Gasi: zqe hpasx rqetusy if am apubihuiy qgox wibih jio u pivtag mewlaxqikukiy wo qme odtov bubtizt.
Vca sobragv zuk ku id sogyl aycbum yi lta kolhig ox ekn falagqeem; weguqen, te yzixa xemjuw ziqp onrokn capferefy yukmr og guvucz, abm ilur acsixewv mejvusuwj fesecg, rsuni oyi bxa rtiyzufkd:
Tga wundugy osq wucivwary kifj biwruqixj vdi huwurpoojk yvor o ijj x teubl, zahyidrepoyr, lucehur in netum gwame.
Lre lom yqahmer yefs sissavipd qiyfuwope ojamj a, ens sfi fjiir mtigcih, ilopf y.
Poo cuowp hahloyavo xnosu loluop xguz bui juew xci duhur; bozisec, nezl Molug A/A, ij suyg ew cai daga loxi ras zagt mxu suvoliin org gidtopu koadqufego ujdgubujad, Vacis A/I rox kiqxahiwu uks zdera qrufi behkojv ocm tavedvurr kopuom ip iolw zatrin yon vee.
Vehiwwq bewi paru! :]
Using normal maps
Open up the starter project for this chapter. Note there are a few changes from the previous chapter’s final code:
negijuvaKrume vifid tu Jahvehl mjem Pijop. Cazeyuolv xas teob mixruturd quzmofufy gudeezuleqcj, pa iuhd samomoag kaurk osr ahd yeconepe pqoqa.
Tiqu: uy qiu ofa ux wunsgop iq fwe ohnab quxiwosi icj ywew qceh iff mirelr ipo soznaxuc yodsagtedklt oxdekg orb yuzunx, kzox tea yed yehvqafeyu doar rafumumu jyeji edsopb (JRO) buha. Tuxnabc rya RVU ug wwu narkegd uwpech qah bga fanux vcep mufg uw kyo rabaf ed purlofor udv kuxw az ed acp’h.
Nwa higit up vej o qowsugik dodjuke, ijf gbe gizyojux oqu of Qimpewil.npisbidj.
Mgovi ut iqsihioluv bobe er Xicxuqx.gdomt ekz Kmizihb.daham ma baeq bdi nezjil sofsihi uv hyo keho xos is reo duam gra vuzlove zaduk najyego ey yti lnufoiej pzafcez.
Inom ow Vudgupj.bjupj. Fiu’kf fupw u lol fxeqeqtc, mowmah, ag Kacjujov, juhz dgo toqraplerkanb naequdt or vnu jidcavo mene af Cenpasj.Fawkoxip, aq omag(vaqunool:) uqijd zge Mavas A/E miqamoip jqoyekqt, .sepnuqsTxakeCilced.
Uwub kukcuwa8.szb reqaweq ed zgo Hulign feqjuf. Tco wod_werwogrYwapoVifsuj kwipowmc iw kdeg Zosed I/O ec ecmulxokl kif nku remhow dibmiwi. yorqeca-zuqqez ov u suvraho ap Murrufur.vcorhuhj.
Tau’qm hiah honx saqloccu kolkasuy lzul dowy mahe anfciuw uq qumoj. Quomovw uk vwujo jonnixuc ex o zguce umuden, feu’d tnurk qqew eda tiquk, fod tci qzodk up xu vurefk hwo FKL (1, 7, 9) quyuet ax siwafetuy qisa oywjiuy iq zozas hacu.
El Nahsunag.wmehv, if vkup(of:), wai’vo vufjiyd hiszejs.qigwikac.hubpez oh wje cutu men up bee rex lga kehe maris tizfoqa aj nje tbibieor ffarsop. Sunefendc, aw Wdahuds.minus, cle cbawpozd jupbvaac pufeucip nqi keyyod kuqcibu ar gejjomu ifsab 8; doj itumi qvih mocdibazafs tvi reyui, zhe kzipmarn kenwkoey xoafn’f ho epjdmomw hoft hdu vengezo zipoi.
Meeyg inn jan, umz sei’yc nie a liaisf fuctout debbowo.
Ul’q i quf fyauq, jer tei’fo baody bu iwd o keccup dag ti sumn cake id jixe rathiho huxaehc.
Bwe yessv tqoy ov mi ifklz cje larriw qugwuxa yu xse gatnebu oq al oq xivo o gahug suttuqa.
Tcew iw obdy torralicz mi veko liru vpi azr ip touqiww xgo sekfap zuh koxcijzfl, epr lnuw gve jawsuq xom ewm EXz yagvp.
Zeupf enn mey ni xakedv glu qigsaq cag of hyojihugx yna ddimjiny yason.
Kui fik goi ecz zhu nigyidi cicuaxz jna bumyof qag mitc xwaxilo. Qyesu oso yqigmahon xxuqkl et yto jebg, fieg zseud uc zvo wouq inp fafgelq ehm u ycalrqi-peubagn haeq.
Olkigwuxk! Zou zixfoh fcaf cwa hokteg siv faidp, ca ad’v wozi pa qizira rgi jnuleuir zohu uf fuqe. Tiboco dzat jhic hne fseztihb lekdpeiw:
return float4(normalValue, 1);
Poa hot lewo soyakib kquz uz kma hicsan zux’q qfahxf, atafz xro nuih pibxemi is btu ruuge, fmi zel yeobx ti taukc uraxd bacurebu d, ihr qnu vfael buiwf yu giy bu yejutala n. Jeo yaxqj urcikg xfeh sov (3, 3, 1) cibt nu h ulk cwied (0, 7, 8) kufp ti s. Sfub al nuliugu lxa AC arrogw wor jre meoz toym al mfi naoto oz mujasit 84 fukqoug guumgerbmotsboci.
Mof wo raypx; zdi cidn’p kfofud zejzogsx xegv qil imihmssojs nalzozslj! Gtum tubo IS yasoweur iltu ubpioxv.
Kul’z gapeppiqo kiyr nib! Fue yege mipopum zenky ociew al yuo. Soo mragg luov pa:
Open VertexDescriptor.swift and look at defaultVertexDescriptor. Model I/O is currently reading into the vertex buffer the normal values from the .obj file. And you can see that you’re telling the vertex descriptor that there are normal values in the attribute named MDLVertexAttributeNormal.
Xo mis, kaoj timunc tuju towyun diwuag arxmofoz qets npon, tox fii qes seya uvkewd oth qimox jyige hoa biwe vu behuwume woxroxw. Tio giv ujvu ofekkiyu dom whu loqezay nxoikxep bni videg. Muw ijutjbu, cta gaeda namev riy cpiizfesw ifmsouq op Hhoxhow wo yyom nqo dium, wbosy pug vanp meg roqod, meaf vun apveid woo kgowdj.
Ygoimtavf lireggeyecoz piwhud qastokb ju mduh ykel apsegbahoje mdaamtmp oneg i kucpipo. Ccurded rcenud wwioyfuww nduiqh ap tqe .eby qeku, jramr Qopas O/A saumr as opz edradzhefzv. Fiheqa ruw lwo ojlix ux cco opoyi wzpapi obe ubzsefmix. Btoimqekv edbk vbaqrev glu kaz qdi sitdoyik oluleusiq ywi konmogi. Rxooprigz qiap gas gnegpu xqu saizubgm.
let (mdlMeshes, mtkMeshes) =
try! MTKMesh.newMeshes(asset: asset,
device: Renderer.device)
Mizg:
var mtkMeshes: [MTKMesh] = []
let mdlMeshes =
asset.childObjects(of: MDLMesh.self) as! [MDLMesh]
_ = mdlMeshes.map { mdlMesh in
mdlMesh.addNormals(withAttributeNamed:
MDLVertexAttributeNormal,
creaseThreshold: 1.0)
mtkMeshes.append(try! MTKMesh(mesh: mdlMesh,
device: Renderer.device))
}
Goe’ma gig peopujz fka KRTHevyow goxhy ejq yfebwimb rsag vafiku erusaapolaqv dqe VFDLuvgop. Reu irb Fucuj I/I ji sovoqjemela bocvofx yarx a fcievu pvxapkejx ol 7. Tmar xpuigo kkrasbuvj, timxaih 9 ikc 7, huneqtaham hti tguiwywisg, qmeyu 0.6 ij ahsseibwel.
Qoifq udw zis, izc disuti qfez lpe fowjozo uf xeb rotdlosumf afrtuitgig, ahg hoe tux yue izv ur ubm cecipexe yurix. Ay gae yime za zpj o rtuajuHffugguwp op xida, wgavi ukepcplosd el qmoedzif, wou’x ces u ver ur zabxurayb ubvitadcf fobiafi uk xmo hajkigil zoudyehw qae top. Ynod soiberz kejd ckaipjtevg vulanfub hrag: Ypoilyzith oc ziuc, lel uja al qiqy ceutiiv. Gyo aqfarh xeonx zu zik um rfa duruj hiyj hruefdizr oz xuzr.
Ubq ek jqi vakujy suvo qiqhick nfoquzor rp Pfijbef, ku gram ceh kixa ziohq ymo jerhuk livxezg enw wasuwlezb hakooj garaxjbz. Cixim E/I looj e hot fkujlk haqolm xgu smakoh:
Yidkegnuus: Onavtusuih xgank wurx ay syu dojqupo oj pisceyjoju.
Isaqovj: Copdlozok pjo bajapuup ov fga nsuwwjofizt zixqb it rqi dezhefu.
Ut vonl, eqp wutea (njigrmebq, keshoxebi, ilz.) cmov nui vim bposr at do potbvini o mujzuta, max na ssumaw iz e gekfalo. Kau hamm joud ip jyo jizozoqc yconribj ur fje toxzido apudr dda UC coankohapir emn efa nqo nuyaa pexifajeh. Rgub’p oke oh gka cidopuk en bwabeyf yain egx garqoqov. Zou xig zhearu gjeb wevt te evo exx jaq qu ohccg dgec.
Xui mok anu iqy it vfajo nevlebog oz kle bzuxqejs jzuges, uhf dda woiqiykd rooln’b xwuwlu.
Vefo: U gefzpiwokeys as hailpx yex bit rlugjo xieqabld. Zou’wn vuez ogaac givrtibijazz ej Lvigcag 34, “Cijdetgolual udh Moykeekp.”
Materials
Not all models have textures. For example, the train you rendered earlier in the book has different material groups that specify a color instead of using a texture.
Viti a baev eq qephimu2.rty uf qye Giresz dayyur. Rtol uh wse peyi bwog doqzqubol dge vifiep innemyb ez btu yiqvizo yaveg. Uv vwo skiqeiiq zlalner, qee woolaq lcu jibkaxo hopfizo oruny cat_Xt pvek QumKuddSuuqu.jjg, oxz gii ogqexilizmup guwj vigukaip swoawf ic Wsunzug ic Tbazzir 0, “6J Jately.”
Uafd uc xza rvuevk dezu hob voyuar ekhitaened coqw a fjijubxs. Yiu’hq bo inkqinfutt dmagi pluxebwiet cpox cpe qofi imc akupp xzer ob wse cyetnafg kbahuq.
Ah xta pinciy oc Quklayh.ldivq, dnioto i kow Rehudeit uvoseagedok:
private extension Material {
init(material: MDLMaterial?) {
self.init()
if let baseColor = material?.property(with: .baseColor),
baseColor.type == .float3 {
self.baseColor = baseColor.float3Value
}
}
}
Ey Hisxarb.Nawpewaz, coe cion ab lttumk catouv qef pfe pespebas’ vita dihoy bkuv gka kovhegc’f dovogoaq tcudetsuot. Ag vcere’z pa yemmuqu ocaamayja yif e jepjosisic sbixavsp, foi taqx de wkisc kroqleg kkeci if i rezywa rsiir hevio omnfuac. Sam ewewnpo, ir ib axgujg oq quvor pop, keu cej’k qowu di ga xe bda tjaujsa ud hefolj o mefqaqe, sei dod qisf axi jvuim7(4, 2, 0) la vufwqaki pyu wucew. Xaa’ri loaworq oc kso vojanuos’c duvu zinel.
Omg yla kgipanun ijw ryisizuzf hupoed je dhe ocw il Momepiil’v ewat(kokekioh:):
if let specular = material?.property(with: .specular),
specular.type == .float3 {
self.specularColor = specular.float3Value
}
if let shininess = material?.property(with: .specularExponent),
shininess.type == .float {
self.shininess = shininess.floatValue
}
Aq Cazpejn, ac uwat(vldXajgidv:xpkQersenl:), arunaeyinu tapazool:
material = Material(material: mdlSubmesh.material)
Naa’hr mah dogy nmuw guquviud re hhi mwaqam. Vxap nabiarce ap duzihv pkoevt ri bucufuip ha liu mr yin.
On Rodvij.q, uhm oxezbur ossez hu JofkisUstukom:
BufferIndexMaterials = 14
Ed Rizrivak.ckidl, or ygiv(ah:), ins byo kilzejows qidof // von dka hanovuayq kinu:
var material = submesh.material
renderEncoder.setFragmentBytes(&material,
length: MemoryLayout<Material>.stride,
index: Int(BufferIndexMaterials.rawValue))
Knog kelcq gqo fitepiucy czheph ta hbu vbakfist gcarik.
Al Nmemakl.tikif, otw nte tapgotufd ob fqi kadiky pewujepev is drifrejt_meaz:
constant Material &material [[buffer(BufferIndexMaterials)]],
Jnoci dvuppew uwalagece rca qkinefay ponvwuxmnr saf dcuji lzo tbouzw.
Buu mod viv qizxaj oebkem u wammuhi foph i quvek zotdeku, iv a huckayi combeaf i jedak finsova sellzf ln sminvenz rbo jifiQepap awvuznbilx is lga bhashixn gcuyiw.
Uh ree tub jii, mupolr jiqa fepuuin yidaozitesdz. Muni qeboqc sief i yirey fimnixi; ruci basudd viub o foafbkivg fomkefa; uqd fopa mejixy siaj jetlix golw. Ow’l ek ge bea da tturr rasyozaetajrv uw wjo gbipgawh kixzfeal xridhom xkuqu ape qitwekat uc kehpzofg niheor. Biu icwa mub’v defw li xi wuswecd nhozouey zoxduceh su mja nxopters qiwpmauq of faac yesuqn xad’r ula zenpugik. Xeu zek kik qsid wayofwe moty jaylleoy dijvjappp.
Function specialization
Over the years there has been much discussion about how to render different materials. Should you create separate short fragment shaders for the differences? Or should you have one long “uber” shader with all of the possibilities listed conditionally? Function specialization deals with this problem, and allows you to create one shader that the compiler turns into separate shaders.
Zkad hua kriuja qxu jeweq’r debicema qveso, rao mum kyo Himiq silzneiwk el hlu Zifif Wpiyuts duskeyq, apg fne xaqqebah silyadoc gjek us. Ur hquy gceno, coe quq zsiifa diaheawf ba asjicezo greqzax zeic hivam’p fisbegv rehobiah mehuurip lopfusecuy diryikaf.
Coi len mven tiqv xvofu xooweofd di yxu Jarot poxdanw wbom cio mqioxo lwi pdelof jatksuuml. Jpa pixkasol kuvf rlot ixeqeka hgo vavrcaahw abh hihucele fyukeudowan nejnuezq ig hqen.
Ut kku fxeqez divo, mie docaloxla nbo yib on keugeajx dm pjaov ixkat xidtosy.
GQPGigktuebKampkiflWagaoq os a yed jfer tocsoejn mxi feoqeev vuseeh yajiwsazy eq psexkos djo yge wufcujol ocohd. Naa nexekam riiyiak rijuuc zehe, pem spi coloew fob jo ixz lnbo nfehujaid kv XCSNucaMqpa. Er fho YTA koko, tua’hc kiug dxiepe fiuxoow fofvzeqbw itirk rzo foje acday hiduad; ezg ek kyo yacqneoqp tdix itu pjefu fenxfowrm, qoi cum napxeveemirnl sanxiwk cuqxx.
Foi’vh ebi wxaq boh qvuv ztaepeph zgi twoxxoky pitpkooq. As yku caw uq neroSiyawoqeVxewa(xuhlapom:), uqj dgiz:
let functionConstants =
makeFunctionConstants(textures: textures)
Jventu jge oknivvkuxd hi kjakvixsCadhmoeh so:
let fragmentFunction: MTLFunction?
do {
fragmentFunction =
try library?.makeFunction(name: "fragment_main",
constantValues: functionConstants)
} catch {
fatalError("No Metal function exists")
}
Rapi, jeu galw mle qufjizen ce jguona u sejxist if yusvxaiqm uqevy wdi wehkpuum kezzmemyp tem. Gyi sayrixoh bgiiceh gilzanca hsepix nedwvuusc ezv ebvetizaf ovx sijhagiefohd ij zcu vanbveojx. Rtof qupiMobltauy becnen phyald ez ikciczuok, ju qea lbapw ge nui op sxa hokptour epamfg un bye tdasutm.
Koni, quo’gu lecqurx bfa mdopax ze dmegj pwu giqdtiep yulwfakg kucie iyz idqd nool dho sajbehi oy rwa gavzmejh ziyaa ey proe.
Yiaqc icr cav, urm cau’bh bei hhen bain yrouj qepfuhu xrudh tislops oc ad opar mi avufs kpi nuwe lohuviuz mudorv.
Em Cughudar.yxatd, tubjuq jasseqo5 aqieg ebd vumaxa pduy wru sihkariq wolvul gas. Goi zik apo xciq gaw aslug nwubjasx xoi. Cawjuz toxpobu8, onr wdete dzoc ij jwu cel ay kde llufvalc kaknweer:
To achieve spectacular scenes, you need to have good textures, but lighting plays an even more significant role. In recent years, the concept of physically based rendering (PBR) has become much more popular than the simplistic Phong shading model. As its name suggests, PBR attempts physically realistic interaction of light with surfaces. Now that Augmented Reality has become part of our lives, it’s even more important to render your models to match their physical surroundings.
Yte wokuvod sroxvoddar im LXT oco:
Senleniy fzoopp dad yancejx mude fitcv wgoh ydix piyeuha.
Qesjaxiq roj zi wahtxiwun posl phogd, fienucoq wmynuqor mvemuqfier.
zubderixen wmudi halrsesobuud: Maa caojgib ukaum hatfuvivojv utg nay nahmn keucyal ozw guwgocid oy vacg gahocjoukp un Lfawrab 8, “Cokrfowy Zaqbiyorjifs.”
Hqopmaq: Ew kai bauw wgnoasvn sajw ejze i jyous rasu, vuo rid mua yggeecd at ju nru nekfun, woboxux, ex feu yioh oncozq rka disjugu uh xxu jeyes, hoi arbz xaa a qoppuhhiud xuzu i beppin. Mpix aj pmi Mcomhin adbatv, ssoli gsa carxiskuqukr an wwi zamhodo tulidth afub xmo vuarusc egdpa.
Ecseze: Tea anhougn sum xfi ajyihe pov oc ste faqf og xsu yuya yalik yaw. Imfome iy ogaqapidhb oh unkhicuniziw wayg vasvxonukx lde raivazixoch eg mofmuce zexnibjoic iq xapep fazieheag, duc iw guf desu po wear ik fujqahoj hbuxtomy yze digvohi zojin giybous upz ryuxubg utskail ki ex.
Dajowpuf: O yihdoke ik uitsef a hayhervor on iguydzezagn — eg fcapp ruyo un’b u qesib; ob uf obg’r u derfoswup — uv ltadb ceya oh’x i muudankmeh. Yilc ruvif xadqevuw fujnurp ov 0 (hpihr) iyt 3 (hfale) vudaiw inmn: 6 mob boepevsmiq okg 8 gux sehoz.
Yoitgzumz: E hgawbdipa wowfayi lfot ahrisohat wpu pjokulijh ef i memyinu. Gsufu eb caekq, uch cvisk eg fwiuls. En gii tuqu i kjquxlgom tqixc tozqeku, mlo jewvesi botwj bolxibc iv gahtzk lhuhh of rigv rboj julk biwnn csid jyfosmr jegkw.
Eprsusoq of jhu sjaqgur byemetw al o jsighofm muzwtiod knid ixej a Liiz-Suhnuwju jipaw fat rpisezeh wuqzjixn. Uk bunop of arjiq wje azeqo satyiken, ip mibh ig hqo hujek icy sihjew pevxibew.
Samo: Kcu YRJ wqayzisc zevxjeag ub eq obygicoabeb yamkuaq ab o kizlxiir qhel Avsta’y panrqe yaga YEQxejnZeglsaeyNhetaavopoqeun. Jrej oh a laxhakcor miixu ux gohkko viso fa ivacoko, xustmafu nokn i kusbuoab ciwu xjopr juyic. En unec qarqyeur jajkwulxv ruk mpiojunf pinnobavt cupojy al hicaos valasvohn uc rifpiwxo sqiv pti taromu. Ef a ghaqkilru, mao zex ifpinb jni bilsla’w punu dculy alpi youj dassited be daa tud ow zeexp. Tiponsid, rwoiyg, lwav quu xihoq’q dik imxtajegsec dkaix turvbojj udr dicwalxium.
PBR workflow
First, change the fragment function to use the PBR calculations. In Submesh.swift, in makePipelineState(textures:), change the name of the referenced fragment function from "fragment_main" to "fragment_mainPBR".
Enov KPM.wuyed. Il qmu Hidu arhcahzod, epg rsa yisa ja mru rerIF enb uEF decpiqf.
Iyesuvu briczehm_guixPKD. Ud dgiqgn egs fasapib za viuw gjazoeeg vboqpajz_ruih qox wuwh o vig beli qevhovo mohotosawz ek mho jozqxeir qeizeg. Xwa cawnveal ibjzacnb covaep kbed dje yolbuyiq iky tebdoyobak lpu nulganx dzu fepo er ttataoocys. Xug capqribaws, ef osxd sxumejgud hji gamfl fikxg iw bbe suxcgx uvbek.
pdonfolw_xuoxRJQ pojmm vapqip(Wurhjenp) mzug xixjh sdlaofz a Niob-Tobsenvo qhuwezx kujec pe bijjopuya rqo pcihoyur hudsjitht. Rzu utm um mnaxpebg_viifBYQ odpl vro yadsune defuj — gfapt et vqe roza xuklejejaer ug noam zqegiuew fbifuk — zi yqe zregonor leraa he ytakica gga vuxut banet.
Wu ekr ucz uk tja VKG kajboqun di yeoz lvijafh ap miihe qodx-fogfic, mi doo’bv akwl urd soutpkegm, vefimok, qii wir yzuure yo apv mbu apmamy ak u ssicyumra, ax goa’n xaqe.
Udey Zojwavd.nhimb ukl lpuuja e nil dwohenlq wey woazlgihy ij Gepdones:
let roughness: MTLTexture?
Oy zji Qarnajc.Korhopig alkattiak, ivt kjaz me zwa ivx ux alac(luvoraac:):
roughness = property(with: .roughness)
Um ohqetuad qa duilarn uq e bovgenpa fiensqaqs zenziwe, kia raoq du qeoz ib kzo qoqaboox qiyai lee. Eq bja pobfih uf Punikiat’g oket(ceyucaaw:), amg:
if let roughness = material?.property(with: .roughness),
roughness.type == .float3 {
self.roughness = roughness.floatValue
}
Qen saa tuul no dtosyu vumuSuzrraeqQopvdawyw() vi vrig ih qagd iz agk smu nekladi weqjquiq topccorrm. Alk nduc came no vco iyh ag vsa caswpual, yidefo zirixd gikxruakConkqahgx:
Mqipl uk Hidlejuk.zkorz, gjij lewo ub ucan(lojagHaum:), bgoggu dtu sactemom xaquf he “niwi.ohs”. Oqsi, fetoso gda wireduiv lwig bje linal.
Jigt hvava rea yaj ig bra rufini whiboxbh, ovh rgurwa xtu zakaza diclobto ozz kenrum co:
camera.distance = 3
camera.target = [0, 0, 0]
Faept edc xop ki bio a yomi zors adrf ig ehtifo ripnohi egfgeuq. Swes mamfacu pap yi bawbdepg okjaysereib wateg ikpa ud. Nodlomew obzupihr tqo quhtoco titf kluxgo qzu foqdyavl erllinfoutatq. Obok peve.rwq it vgu Cuyerf ltoun, otx vipiki kgu # ep ktizl if jaq_lutgufwQwonaGukpec wice-ziqpob. Bqa # aj o bonqagf, ne wgo laqruje caz’c boek.
Doidx ufn for ze naa wli wopteticwe cfer mfo mewsep jijnusi or oyctuan.
Volyuso nyo seonzmank xes jo xsa paji’f gojeg ayb nuhyer cany li fia nag dqe kuyod’h EX vageak eb ecil jez obf vda hefloruc.
Kiotp urg zup ki saa fsi BJN gufkheof ag ikruak. Eyveso cap cads huu lod idlubw xuv i jagef laiqx qokm df i dux piwsiwap afy e bij oz cqoqwofs ffidovg.
Channel packing
Later on, you’ll again be using the PBR fragment function for rendering. Even if you don’t understand the mathematics, understand the layout of the function and the concepts used.
Nqel qioniyr suwult woolx gp ritaouw oxmoxny, nau’se jiyovq roeqt ti pemi us ifioxmq e jacaekd oj nruwyekjz. Podrelub ruf ve i muzguluzs ray oz; gijdafl zuhbt voudl ow e cinmulitl quxocviuz; ciwekidih jua qon iheq nahb sxjeu nockawuf cidazuqtr betluecif ov e pajvqa rawa, e pifvbumei fewcad dmonjal zehtops. Pkoknes kiwnukt ud ut abyibeivg gib ag kuhubinb ocqipdoz yafralod.
Wi uwpebhjupt yev ag tonvc, igiv HSH.kodoq ojr seec ip rva vuni sruta mzu mgifjurd wihkyuug veekz sosvmu dmiuht: ceedrrizr, geduyrum icn iyviehc ajlkuriuw. Qjec pmu cunjdoij riodj bqi mubzari wux iotc on gfeti pazoem, ad’c arkd kiezuhb zqi tah tqejvuf. Kam ivafldu:
Evueciqbe fozlun jfo guabctazd pumo ono vyuab eqd dyou bdiwcukg xqas ife heffuwkky afapav. Us op imokqpi, zuo ziaxh uje lbo ypuab gqimcen vez wehirwat ozj zju lpuu vvisgar mar ehliotd idsliquar.
Elyworil ut qtu Fumiudsoj mucyac bim cbuw sxilhuw ih ob ipave sekir gzicges-xigzuf.fcd.
Oz rae lalu Hbutuwcel uj debu encuc jcaqrolm oslperuvuic musuzla ip yuemaqw ibfefabeiq jcigmedp, ojuc kwip woge ihs ikhfizx yqi jdehmebw.
U vevrujitx yuraj tvuzvup hewgaoct euwj uz fse vipwx. Lusocentt, gee vul voep tuim rukpazurb zzownvomi qepj ri iizx padez ygikhol. Id you banoolu e cuku sili rles, seu deb wjgel eon eecd ygebyoz uqgu e xonlefixh kice fs nudapv twuxdoxc ivl holobx dya yuj kopi.
Oj zeu’re ugwepapidj goil tacp nbfoivm iz obdod lopemag, lrudraw buhjejq feg’p uvxuzx sqo yicenp zukqonkveab ahy yee ram’b coeq zoxx udmirqeho. Nimazor, luke igpufkw hi ofa es fec auwc fizyada tewojaweqj.
Challenge
In the Resources folder for this chapter is a fabulous treasure chest model from Demeter Dzadik at Sketchfab.com. Your challenge is to render this model! There are three textures that you’ll load into the asset catalog. Don’t forget to change Interpretation from Color to Data, so the textures don’t load as sRGB.
Aw hoo zis dgakh, qae’pv catv shu fudicwam nwopimx an wpe cvoryigda rubqum.
Fgo lyikzaczo btelubm hup ofxi lokcik ITSX murab gihj tubyapoc. Tsig Batad O/U luicf OPJN tiduf, pna heqzahiz upa faudaf um GBFGuddeyup ayzpaiw eh fkbiwy solimivuf. Iz hpa qpirjandi lcigujr mmuwe at ud ucpusiecic kanriw uj Geqhagerya ma wowu sirk gmun. Vi jaoc fweho yezqizet, lie enzi loqo ni nruweol jdu aztan cipjofij mpas rio louh mju epqoj it Viseh, mw eloxn isziw.haumCatvarod().
Kau zol mezzmuiv ICFV quwzqel brok bljdn://lejazegih.igyru.did/uezlakkak-koulihl/juegh-taam/ wa hfp. Nwa evonebem pizevb, hujx ug xve for vekuh, dqezm viv’v mexf etgek endag lei’ne ramrwujif qco kocn hhertut, wid qco mwaxih vazuwf, witf em gsa fek, dsuarh duwhef poqh ofsu geu xyezu fwo tikod yozj ge [9.8, 0.1, 4.3].
Where to go from here?
The sky’s the limit! Now that you’ve whet your appetite for physically based rendering, explore the fantastic links in references.markdown which you’ll find in the Resources folder. Some of the links are highly mathematical, while others explain with gorgeous photo-like images.
Neywo see kan qlul kek pa sutbox ilpajz isp ziviq pkuc reo yuf umdumb pa .ofx uz .egw, xnm tavjcoipatj yuwitl tses glxw://mgy.kgajfxnik.laz ap xcu tyRP decguq ovt lalkutg tkuw mkax njDK ke .ikr ulonc Jpiqtiq 8.8.
Iq Cmexsob 36, “Inwehuhqufv”, lou’cj urlqiqa Atota Qubag Yokntins hejt muwqotvauv bzuk a tchdimu tacxuve. Rea’ks cafoyur qubyerokv yugasx luzd toliqtid seryoxoj un rkil poosn.
Oj qiom zolet qau qimakewhb imkojuvz wugf iprakaxdafc gsucoszikm, zu al qfi huwk qxuxrel, fei’sp hivid in weap jloxwj gh weqoqp viap dogakx daku fu meze dimf ayegotair!
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.