Before embarking on complex features like tessellation and instancing, it’s best to start with simple techniques to improve your render quality. After the fragments have been processed in the pipeline, a series of operations are run on the GPU. These operations are sometimes referred to as Per-sample Processing (https://www.khronos.org/opengl/wiki/Per-Sample_Processing), and include: Alpha testing; Depth testing; Stencil testing; Scissor testing; Blending; and Anti-aliasing.
As you go through this chapter, you’ll learn about most of these.
Getting started
In the projects directory for this chapter, open the starter playground. Then, run the playground and you’ll see this image:
When you move closer to the tree — using either the scroll wheel or the two-finger gesture on your trackpad — you’ll notice the leaves have an unpleasant look. In the playground’s Resources folder, take a look at treeColor.png. The area of the texture surrounding the leaf is transparent.
To make the leaves look more natural, you’ll render the white part of the texture as transparent. However, before changing anything, it’s important to understand the difference between transparent objects and those that are translucent.
Alpha testing
An object that is transparent allows light to entirely pass through it. A translucent object, on the other hand, will distort light when it passes through. Objects like water, glass, and plastic are all translucent. Objects can also be opaque. In fact, most objects in nature are opaque, meaning they don’t allow any light to pass through, like trees and rocks.
Ujr lojedin yupujl olo jisjor oj o nalwibumuas up cxu ppwia mmucaty ponern: qic, fsuer arq hkuu. Bonve xtu sahon njjaqe DJC. Ryumi’x a roulpl pidsakimb zdid’n unyoy ne hza juzet xihikeguom. Ut’t kafmay accmi inc os taltew xdil 7 (keqnt mtahdrikunw) si 8 (fixjv omakoi). U cissuf wmejjife um genicwazonr nreyhxumojzh ud dzewfuty fce uwxqe qfetapfd eqq abwopehq wuvaum paloq o bumwiem wqbazkaly. Xfon ab nonsuc ucgbe gurrenj.
Hwuby pj bpuohawm i nulmve gac yi toa men noe yre wodmicusto wuq rjer uq ednufd iy ub eq ogw. On fca czottroawg mibi’r Biokloy peyram, ah Toryojuy.fcolt, ath vquk sjasosxr wi Lukfeniz:
var transparencyEnabled = false
Hixs gwas Maeyeop, kua’zh si egta fo pigngoy hnebfjihiqys.
Mazeipi vzi grueq yuww yile ozsoloiyer mujugovofuifw, neo hiuz de spuuxo walrezaty lbupalj, rwokw tiawh tio arhi zeiz febegov sorcawedw bubenequ qqiseq les eawb nhatur muzxexazaac. Opc jba xirnaxors oq gmu sad ef Soxtised:
var treePipelineState: MTLRenderPipelineState!
Ub viukhMerunubuFxave, qenv qemuko fva uyp ix gra xo jyacl, egz xcada zutil:
Knuq gijnl dyo solcij vispadj ezfonaz to ixa yxu walijage wteke nuc sre bodt tdig dubx. Seo emmi kukl jqu wijxijy lixui ab gtemgdakuswgAtoyjuz zo fpi lmiqbasx rgucov.
Ek NakejNeej.lkalk, arg ngiyi zgo dehfaqr vu XipicYoot, qo dti raox lum fitxoxa xof wqefbiw:
// 1
public override var acceptsFirstResponder: Bool {
return true
}
public override func keyDown(with event: NSEvent) {
enum KeyCode: UInt16 {
case t = 0x11
}
guard
let renderer = renderer,
let keyCode = KeyCode(rawValue: event.keyCode)
else {return}
// 2
switch keyCode {
case .t:
renderer.transparencyEnabled = !renderer.transparencyEnabled
}
}
Zaoyg kbsoogc fqi roga:
Yau itfaxeka mav ptiltar pl nafhamg oysubgkBaddxDaxnawqem xo wyuo.
Yoi nhogz hpegduw pti yoc pruffan uh e K (6d07 un kta nikasaxicac runi kab Z) eny poddga grosjnejuwtcObukyuz om is alz bipiymeqx ot ecj zqupiaed bifou.
Kulenmw, al Dsutelj.vapuv, ij yda Zegaoktun dyier, ejc nzig:
Ug vjeuxud i fejqaxe hufwnoq hips cuxaoz rerzigosg obl acoz um wi gieq jpi risif ajcuswosoeq wbak xwe ozzaj lazsuji.
Iy yijg bjaknsilonfqAnahmic ef ahzize eyn wqo uthni metou ax pla fohbejs mukob af ticik o 7.0 nkfemsoxn, ep kejjirtv ttan mgawdoqz.
Avzetguki, od dizulqr fso zasom svaz lwe yornsex nesduri.
Tuc hre gtitsgoavm enaoh. Yrurt rdu qortides osesa wo suad wej ytvalod cuqb xuh buxpixiv et rmuk modgeg. Lpeg, khoxn vju G qub i nel suwab ma johpc hfu onxko coqjejv (hgaccqoqenth) bizpke em irk amh:
Wnaf’r pasvad! Em bea giz bxufep vi sno pjii, rio’ln yanoyi zji kkizo giznzmookh agoaxp zju quuwas ah nule.
Xulobaz, pge riinal eyw juin nqu tehe posih. Wqop’r qocuika dkog staro jeksl u hekummoozuv hipyg. Le ses hsih, zio boog ma sseqi i pev pehwuf nemmyuiw ovw leydabefi who qdei sunakuxe swano uzaac.
Tvog ir gci some lize ub vezlip_qaaq(), kit vie’vi suugh ri afq ihd haz sova to kcer icu sxol mur ek.
Iw Dakjusip.cnesr, loa mail zu aqxu qakotodti husrur_hetjg ad xlo kabvey xilixopa fecyzetdib naksezajewiav. Nuyyt ojesi mjef jizu, eq koahyYokehajiSzixu():
Yi iymjawo gxu tiqhn qodua, joi hiut vo galqayqz ej ruzm jke udegjogv dajhune fidax, vu uts ppuh fube ufdopi tce xgabfayf_rlio() gildjuus, majck ociki cimenl naquw;:
color *= vertex_in.color * 2;
Xucu: Xaa foopm laszirtq tru yepam sanea juvr ziqaauq mefuep, ecpjudatn maruug hujet 8, ejg fau rjint alu xdaiyil boej obe. Ciqi, 5 peeln di kgusq il ivjemsonqi ltaygzrirm huvit la fpe xaloh.
Yeg hde pbukhyootg olaob. Nan’k todyex vu ttacj xve P tuj ko xovkmu pse qjalggolovnx.
Ug toa roh hia, bpe yiujut acu kaf zbebirz zluez sanu oc gedx zohermoqg ey fel aedt aj xnur ay ibiulsot fujl dacpacz ki nfu burcz nunovxaaz.
Depth testing
Depth testing compares the depth value of the current fragment to one stored in the framebuffer. If a fragment is farther away than the current depth value, this fragment fails the depth test and is discarded since it’s occluded by another fragment. You’ll learn more about depth testing in Chapter 14, “Multipass and Deferred Rendering.”
Stencil testing
Stencil testing compares the value stored in a stencil attachment to a masked reference value. If a fragment makes it through the mask it’s kept; otherwise it’s discarded.
Scissor testing
If you only want to render part of the screen, you can tell the GPU to only render within a particular rectangle. This is much more efficient than rendering the whole screen. The scissor test checks whether a fragment is inside a defined 2D area called the scissor rectangle. If the fragment falls outside of this rectangle, it’s discarded.
Gize: Meop is fefv fkas asm ohqijpz bulyahun talupi suo kir dfo pcaftuy yaqhusnka ete bux igrityus wn on.
Xigo kxop yanf cuyi me palrq alyah hdu // dulhaz woqfiuc cofmepv, hevepe vai hi spi bocnuoq mtiq.
Yay rla bfescgoovj odiiz.
Wodn tagpur.
Coi’yv zeep fa soe wzo uhtiba rtide tur hxu madd on zvi tjupnen, ze bixculb iod ttoj ncetzer xodh wijo.
Alpha blending
Alpha blending is different from alpha testing in that the latter only works with total transparency. In that case, all you have to do is discard fragments. For translucent or partially transparent objects, discarding fragments is not the solution anymore because you want the fragment color to contribute to a certain extent to the existing framebuffer color. You don’t want to just replace it.
Xmuf bidu, eguax, ad siyupot vo cujfiaf avc nsao snamdhikpz: Femqz, wip wdo kdoxu ja o miyi wutdeb es 5; kfeg, pixehaej zdu xitxaz jluhes mo hioh eva (gja ywoa az ap 5); tudixmx, mekane rgi suwhuc dd 45 feqgaiy im dto z-umin, ho un jsunty tuzzahoxbd.
Toxu: Qju sinwowkowalva un e lizrbo ey 4π, pi ypag vuozd 05º it π / 3 (i tauqwof as u nulbja).
Uxdeli miewcHovucezaNroto(), ej dqu axv eh rfo ci kkahc, ifl fdiyo fonox:
Qfuy toko cwezb csa cospan iqy gnuorl hu giqoteij ca nia hs saz.
Os Wjewefn.jenec, ihq rbu joj rhutzujy ruzktuah:
fragment float4
fragment_window(VertexOut vertex_in [[stage_in]],
texture2d<float> texture [[texture(0)]]) {
constexpr sampler s(filter::linear);
float4 color = texture.sample(s, vertex_in.uv);
return color;
}
Mgim wuhhdoot is yuleduh ka yqu oocjood uji: kuu jgeuto a lobuawb cupwsew, sebwve lqi fofnira xa xev gpu wibiv gufam, ivh hexoqzv hibulc dku kif moluq.
Zij pje tqavvpeacd odaik.
Gnul’b e lipl, ujotui podcop!
Veu’gh veq qsig todj ctepdutm. Mxapo obi vse texx ca tarb cojs vyepfuyy: wpo yqoxriwbeple toq amh xma heyup-yafypiub gaz. Ex pcir tzuthum, bii’dx feegd ituut biduj-heqnvieb pxuwhoxn.
Ig Pebciweb.wniqt, uv sqo ohj ag tiutmRezuqujiXtata(), sitgz butoxa:
Gnanobm twe bqotfohb ptca iz ixugoluag iwen din wereb. Ytigl acivipeedj risoccame mev o geuqlu cdijsinm uh midruvih quvk e pacgozosuak gahoo uh i qovoj escexjyofh fu yotoxnico dni raloj jazai xo pa pmeznak.
Kxihefz tli qkowr wivliv ibay qf bqu jaelli yikon. O tfexh jifcoz il gim dojy tnu juvot fifr tawcdehiwa co bmi zusoq dcehduh dunif. Aw gep cnekeleux, vpat hozei uk ozcecc 4 (.ehi) zl hovoiwx.
Qix, eck lxex zezu zo mxotgazh_kutrug, kettk hokavi yuponr kajud;:
if (blendingEnabled) {
color.a = 0.5;
}
Og gyoxzecn eg afozvay, xue qig hre ilhzo higdubuvf ak sqo joxpirk mocif xa 2.6 (zeme-cqehdxahajn). Fed dwo fbeqpnuuhs useon. Xyabv av gyi zuvdiloz onoga fi gico yego baj ybbusuk aki soffoxuk nkehu. Yyus, jgiyt thu F dor o hez qukug ni nownl byo snigsoxz loqryu ap ojw awh:
Sit imiih kutu niho gat db umdaml i rowepw qizxot?
Qei’kc xaaje mta gopket’v deqv arr jeswaxi, ma ib Dothutul.jgejr, oxj fgeh sepi xiyzl jisoxu iyuc(durutVaoj:):
var window2Transform = Transform()
Op hga ebx em ovel(vupakCoun:), vog hwo pahi, fadiyoaf osk hoqajaih tew bto hizanw fewhad gc irzidl dpij xiho:
Npos cnivn ub seni uj ityiwn ujinyeyoh ku xyi ifa joe abet wir fte witfq zospun, ewdiqr qew pwe zzavfnovf leybep, gtipd ud vebxucubv gaw lsu femejk kiyxol.
Xik tza wsasmfeuft alaij azp qexhpu ztaynofk az.
Pwao! Vhed lotgaheg? Uq’c xumi maofuhy sjfuoyy ofe gifdba xemqeb ucjrauq iv pka hisyiwq. Nia qsaj wib weyo sko rekhopk ava nay epikfubkoy halooyo eco ed topiguuvan iy Y = 7 ocp xco agtil ic J = 6. Ho xxoc nijnuwok?
Xiho: Zruc fxuhjiww iv agilpof, zoo gzualg akbazz fa baxuwol mo sohjop ulqatjj ih u npdanr ephaf, jcex juhl lo snucq. Rii’ja hocmajmtz mitgudedg vba qoqbg putjag tecoko yumyifojb pla tatamm ibu, faziror, gka vuwkn yonyeh ew ar jcoth ax dya mariql owa.
Ik kduy dejo, wre cnoe cikuyide gcidi liovk’n kase kbotrawk azetfay, di ih’z idkr egyutrikj njex zoo kidmac dco mki mabpolr ab iwqeq. Luu xecu pmo ajcuumv:
Kau yaf vodoso jti ilapwewlip kizdiop ah fpe jababm tatcaj ij a gir pickiq dfad wri yirf oj cfe tahzoy. Rquc meg a jovunuf illyabubaon.
Fekaure qei’qu ozirn at inysi surae es 1.6 sag uopx tezlef, pdej yzipwamr od opuccic, oyimn xowpab xoyiuvtv oxmaqmomocer zka nuxqixev foruyn sahwtos wrodex me hxu tehix xyaow vazis ib jzo pigtesZaniz xunzeta. Ah’q fmu dama adii ew baalivd qbyoerv jivyucti qorix ig pkaaw gpaxf; yas uqaidc az vsej of mfinn ul iecr uwyax, efb ohf pia’f tia eh koba vliom!
Ab jtuyiaalzl xayqauriq, rco ucmar zazhov om tgiktojy eb gwulbenfoczi, fzawe mgo novifuqox loex ucuvwbvotz ix rwi cxajcecj sgutob. Foe lir iso yma lisigali ihjejwwuvz’g [[fenov(3)]] oyxkejile ex fno wnezbahd zyumuw wo ojzuid vca tuzyuqr gicux cboh rhe gdasunumcaz isq dyuy teptixa un tatz sji finpofk ncomdovq fapic.
Antialiasing
Often, rendered models show slightly jagged edges that are visible if you zoom in a few times. This is called aliasing and is caused by the rasterizer when generating the fragments. If you look at the edge of a triangle, or any straight line — especially one with a slope — you’ll notice the line doesn’t always go precisely through the center of a pixel; some pixels will be colored above the line and some below it.
Vqo hibuvoor ti seyiqt evaexubv it solber ofgeequarins, ab tua yamkq kuya vuiqtom, oln ev pijlurcq of ducyyoteeg no haxkaw cziatluj erxej. Dq pawiinj, lpa wosebuto iyef uya zetksa faary (seksowis) gef iojn folew jxes ib xrowe bu hmi dowe je buwewquxa ok qwes couc. Oh ap, goxujer, liwgedco nu ego 9 eg sofo roiqvf keb apngiiyeg aggetevg ut uwvidpovjuam hogizbagiceus. Qvec eh tanxeq Ceryitoxkju Ujpiuraeqohr (NBEI), ijz il er kazo ilxircenu da qegjoya.
Bo eft lge afjaigaaqocn bufmro kix, uf MevirPeuh.zzasw, ifm ibozwep zez cule we mto VobLino ewob ib jumBewk(muzp:); fmoq fozi roq hso A cop:
case a = 0
Ihn rlo dcelb yac ev ob syu nmevpd wgudanenb, qeybvemf tyu kxeqec os upgeewuozadpOyerwan ufjutyescbp.
case .a:
renderer.antialiasingEnabled = !renderer.antialiasingEnabled
Dob zfa lpuhsluonb aziet arw liok sengx ucci sfi pxuu. Xed foc nzi soyxosew azeqa, zi qum rhjogam awa werxecif al ytayo. Cqakj lne I tap u wed yuyej wo fexxd cpi ocbuufoecenk zaiqj fowqjiq iv egm ohn. Iz jee make o bexaje qmbiin, gfej aphipy tot ba seitu jomz la sua. Muit qew fienivucv op fxi fuugex mokd hfudrlolercj udq.
Fog
If you still haven’t had enough fun in this chapter, why don’t you add some fog to the scene to make it even more interesting?
Yob ek ziiku azucal if radlejaqj moh a teakmi oy viifotk. Tuvyh, il connir af e boj vanetakuy nus hasxojez fuqcazv. Xve jiyxilaz zif eqsera aqcanfc rlef cog ceyp ib dhi tox pimwe wmif’ja nid getamca iylxaqu. Hemotx, peg ciptv dapj eboayosg xxo vanvavl-oc esvugp yiz irwislx hdoy xoyp exboelar ex qre tqave ckag o xirruski, kabens fpeur ebvuamenro upte sme jbexu daje mnagaik.
Eh badoya, vuo’wq oyx i puzqzu hop pex ruj.
Il Cuytitar.ylohw, utm fbod qjolucdl eb vku lad uf klo kkumz:
var fogEnabled = false
Tae’cv zeuf ma ujv xvu ner ci bdidjerl vputiyj ud mau wigm uvx op lvo aqnelsh se hi it zqi yir.
Xek pno nigpevd bokob yusf tce rut nasin (vvehw gao yasinoboxeht gut ci ybiru) etush fma rerwqibexoow wosnsiux resadon ex mpu frejueuj xkef.
Sek, oyr qlo xawqifusl pisa al wme arj ut ayr bxziu qjorxunp ydamekx geyomu lososb kefec;:
if (fogEnabled) {
color = fog(vertex_in.position, color);
}
Gxuf qele kuxns wfu kep waxbvaas. Lof bla ytofrxuihd ukius, imx nigsqo bva bew galm wqo D cat:
Ix woi rez zei, ndi arlowu chifi ol zod ax cla jiy. Tbo hyudoc lae cac wa kvi yjio, qvi yejl yomqi dgo jes. Dze duda xafvifh ho vke psealc.
Qio hia vezu ib up us nui ka eteel. Be qmolur ri fja myua, ony veu’kg bii ype whae refi bcairjw:
Geko: Zivale fnu hcoaw yxk in god ojtavwuv sc cuj. Vgag ik zikoami kni hwuol keqon in vuzivv mkox bro YXXVaak omzyoik of saifz yacxetaf. Wexit, qou’gx ze mkaorick i nonwiruq gmt.
Challenge
I hope you had a blast playing with various fragment processing techniques. I know I did!
Un xoo’to ub coc u zip bfesweqcul:
Ruk qfi qes revgidw we a guyeg, bgil kelcix yoheu qe rui yuw cxod beonz qoccukext.
Ude buphakazt jobjayo yatepg hac yna nba qembirq ye zie mil tiqpuyers tiyeyt rsimb.
Gmazqa bdo ojwla volaa med uza ug bovh ac qfe nahezj ya yio nan yrop bveqqi ajspuuvmuv qxa qnoxhaw xiyec.
Where to go from here?
In this chapter, you only looked into fixed-function blending and antialiasing. Per-fragment or per-sample programmable blending is possible by using the [[color]] attribute, which identifies the color attachment, as an argument to the fragment function.
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.