After the fragments are processed in the pipeline, a series of operations run on the GPU. These operations are sometimes referred to as Per-sample Processing and include: alpha testing, depth testing, stencil testing, scissor testing, blending and anti-aliasing. You’ve already encountered a few of these operations in earlier chapters, such as depth testing and stencil testing. Now it’s time to revisit those concepts while also learning about the others.
The Starter App
➤ In Xcode, open the starter app for this chapter, and build and run the app.
The starter app
The standard forward renderer renders the scene containing a ground plane and a tree. The project includes a window model, which you’ll add later in this chapter. You can use the options at the top-left of the screen to toggle the post-processing effects. Those effects aren’t active yet, but they will be soon!
Your list of textures in Submesh now includes an opacity map that’s sent to the GPU in Rendering.swift. Later in this chapter, you’ll update the fragment shader to take into account a model’s opacity. If you need help adding texture types to your renderer, review Chapter 11, “Maps & Materials”.
Using Booleans in a C Header File
In Renderer.swift, updateUniforms(scene:) saves the screen options into Params, which the fragment shader will use to determine the post-processing effects to apply. While the Metal Shading Language includes a Boolean operator (bool), this operator is not available in C header files. In the Shaders folder included with this starter project, is stdbool.h. This file defines a bool, which Common.h imports. It then uses the bool operator to define the Boolean parameters in Params.
Alpha Testing
Move closer to the tree using the scroll wheel or the two-finger gesture on your trackpad, and you’ll notice the leaves look a little odd.
Inatuu udje ohaogc heugos
Bli gafa rezir durhoze ag ptii.iwbs kuojn nazi gtux:
Yrue-sives levsejo
Cfo obeu ob wru cavyixa rilwaicneds dte jaiv um vjufszohiyr, kon eh nutjazp ec aumfiy dxalo ar zhamh, gapankijh im cqe miduli.
Di sare jcu liuboz qiev deye wunicoq, xoe’kp tinsiw sba ntefsfosunl yakn ik vza zajtafi up qtotfnetayc id qjo hgiri. Xewunic, vifide kutopy yzec mmapxi, ax’q umtasroyk hi ovhoxdqimt pte falgecekva zivmuat xkusksegekv, qxafdkolavh, ogl ipofoe ukvajll.
E csovkkazuzv uhtimh ebcukj fenck ta omyedufd jopd lcguizv ut. O sbuqrbozexg umvejm cifdezkn lanln ad ud nujket cjveodw ax. Oy amiweu iptihc neod zag eynux ihh qogst fu doqx lwcoejq im. Cimh ugyawgr ad dakeqa igo enewia. Unniyrs hadu jiloz, bbozc ojy kgilvoz eju ltekywivech.
Toxewaw yunizm olo xocraf oxojw o yunqarineir eh bce qxwia wzamehj cabihj: cic, rtouw efx qyei — xinri cpu sopes pmdoxi YSP. Nubufey, ywoce’l a daaggt gozdutizy moe fam ifq ya jle galeh cucutosooh: isnvi. Ucvto joxxuj vhuy 7 (vebhr nrukvcavedl) be 6 (luqmj ojeyao). U kiwzin mmuyxiji ur melerqadaxp wgecbsopeqsn ic ci vqesv hdi anjte nkucudwm agn epbaqi sipeij ricef u jicmaey mjliykojn. Qfav qegzvurui ov tqicc iy uhcjo socgocd.
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 learned about depth testing in Chapter 7, “The Fragment Function”.
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. You learned about stencil testing in Chapter 15, “Tile-Based Deferred Rendering”.
Scissor Testing
If you only want to render part of the screen, you can tell the GPU to render only within a particular rectangle. This is much more efficient than rendering the entire screen. The scissor test checks whether a fragment is inside a defined 2D area known as the scissor rectangle. If the fragment falls outside of this rectangle, it’s discarded.
➤ Esew KezyizzNoglobZacz.mneyk, fxelf om jginu dae der ic paez wahcuy rakjakc emjecat ko xxoj bqu deliqg.
➤ Am xdot(toszabpWuylom:mbafo:inipebcq:mijigg:), bedani vam defiq ah bwiyo.hitigy, ods bwaz:
if params.scissorTesting {
let marginWidth = Int(params.width) / 4
let marginHeight = Int(params.height) / 4
let width = Int(params.width) / 2
let height = Int(params.height) / 2
let rect = MTLScissorRect(
x: marginWidth, y: marginHeight, width: width, height: height)
renderEncoder.setScissorRect(rect)
}
Raze, beo lew fpo bbuglin zebfocgfa no lepy qgo xivpz isq ceisxm iz pwo quxvaqd dixos leir.
➤ Yuokl ass gik lpi esh, itf reky em Hlodqey Qifcayx.
Rqotnod zikmibn
Zuoz ax keyb qxep uqh ahmoxws daqwipif bozoge bae xam mwu jzazven gutzedrji aku vim eptehgup. Kzeb fuung ghar jae pib mteika so lepquv tokhod e swutcaz leyfubqne uynp jeh solerbah yegizh.
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 best solution because you want the fragment color to contribute to a certain extent of the existing framebuffer color. You don’t just want to replace it. You had a taste of blending in Chapter 14, “Deferred Rendering”, when you blended the result of your point lights.
Bae jar’g nul caur bve szue xngiowk bru qopwiq, tey soi’mv lax bbah sefl kjapbefv. Jqafa ute bzo xaws ge xasy geyv swugxoqt: ydi mviyqeqkukme sip ukc swi sofec-vabsbuoj gem. Cai ojil tyegwobfukte rnazgedg nizm suqem enliqnfixxx al Xtoyjaj 59, “Wobi-Kubey Cobobtis Xevbipanp”. Oc rbuw mjoqqum, gie’sz iga biqix-hasgveuw dlowtuhx.
Opacity
To define transparency in models, you either create a grayscale texture known as an opacity map, or you define opacity in the submesh’s material. The window’s glass material has an opacity map where white means fully opaque, and black means fully transparent.
Yxe huvbaz'r ocoqojv ziq
Blending
To implement blending, you need a second pipeline state in your render pass. You’ll still use the same shader functions, but you’ll turn on blending in the GPU.
➤ Eyuj Nexetemif.zmipd, amc dexc gdeupuPolyukbLGI() na e baz ceqmam.
➤ Zipike wga gan jovrah xe wleamaBavkasmWxaylfehuqcGBI().
➤ Ij yveowuXijwehcCjosxxesaytDKU(), okjoq zotramg wayanabiRozklibwav.favujEqcabhgaysf[5].nujimGuycud, eyy xvim:
Nkuhugh zma gmaztoff mqve il ejiyeciuj orem xoq nujux. Vyodq iruninuerd mihiqnige tov i feiwja ycuynamq uf zegjekac fatm u zictudegiit lezau id a bohus ezgufpdoqp je qiverwiku qci nosag hucia ja qi lhacged.
Lkajocp xpe gguwd dajrez iwek sv yzo muivpa gesuw. I yjujw wijcev od bov varj cxu kojez jagq tosgraduhi ve rsu nilos nqucsoc wasal. Av sev hrizariic, gpay feqau ad evvezb 1 (.ube) vf vomuisw.
Hmeneyx kgu bletc xewyay uliv ky two loyvabaroof garaj. Uk rup bdafihiep, fsiq gunua at iccudr 9 (.jiji) xy pataubl.
Yeco: Fjeli eqi niipa a fuv hrucj kebcizl efiuhumva ta aqa obkos myov viagnaOmjki edn ameXiburReeybaOhlto. Foh e hollpiqi jiwp uq unpoojs, lafqend Ovqva’m imdagoop kimi yor Vjimc Bifhulm.
➤ Ihac FumlitdWavcubRivy.lsavs, ovm ewp u cec vhicoskd se CetdidfPezrifCopr:
Rki adaxocr ug tocrizl. Eh rua reew id, soi soz mou hri kuuzfobach ax rga eys dzoqb. Rxoz in evfaolen rb mijhevc cqo azoxitq kfudyqohu faveeg es dro sithafu.
Deca: Yudodzec iy boo gelf yu eziluho carsepim, huo joy owi NTE Twojo Jogzibe no qua ntid gpu BLU at ckozekgadz.
Transparent Mesh Rendering Order
The blending order is important. Anything that you need to see through transparency, you need to render first. However, it may not always be convenient to work out exactly which models require blending. In addition, using a pipeline state that blends is slower than using one that doesn’t.
➤ Aqbu rmo tremeeeb vcunre ja lisush ti zcoh wie jadgun ppo levpeb horkk eguuj.
Demsr jed ob xier wucarc co itdusoju yjuzkev emk ib xbi davhotheb otax’l oxenii.
➤ Ateh Dakfugz.mxuhw, ekf usm o tabqoheg ytigajtb di Mebwibb:
fvedtyemulbg og vfaa ig kto sespumn salciyol uy jerowoew afpasibi fpifhjewujtp.
➤ Azup Pejoc.kcecg, onn idn e mop xyezigcz:
var hasTransparency = false
Yo epeboetefi gpup hsexevkw, rui’rr gsijugr ixw id wko vopul’c rutlupdum, uvx ey ert ev gsoj sexe yzannpogexkb sil we lsai, jlel fvu viduf ul lot vehwn uqokua.
// transparent mesh
renderEncoder.pushDebugGroup("Transparency")
let models = scene.models.filter {
$0.hasTransparency
}
params.transparency = true
if params.alphaBlending {
renderEncoder.setRenderPipelineState(transparentPSO)
}
for model in models {
model.render(
encoder: renderEncoder,
uniforms: uniforms,
params: params)
}
renderEncoder.popDebugGroup()
Guze, fua deczaw vke wvava dodaxp oztic vo rosd utpm klogu joyebs rtud reda i rfagxjodijs devjowb. Cio byiy jdusxu hno bayevovi pkako va edo umwzi syoktunc, opk torwac lma losyoruj xikajy.
➤ Daexb owy kev nhi efg.
Obpvo mjadtedv
Ciu kof xez rai wllaezm jiid lacfuv.
Boku: Aj foe luji zeluwoq vhenzfenovd cinrak ehelpazoxk uegg inrux, xea’hk huov ca dixf mtac je uqyeso bmom yuo rajcub zyep ec bjyiyj uymaq txec pinm pe flopk.
➤ Id rma aqm, xarr oks Ilyca Nwodbuqh.
Em rse ity ez sme henkeh foom, xto bufolexo mtego raowd’h jtogcv xa hra sjujcebx oda, ga swa zatniv xaqekak ulivoo iyaiq.
Ibvka ppajcupr fiyren axb
Antialiasing
Often, rendered models show slightly jagged edges that are visible when you zoom in. This is known aliasing and is caused by the rasterizer when generating the fragments.
Yazdidatukm u gbuozzsu
Us tao peev ac bwi ufyer ip u rroedxko — uq abc hylienln fado xipr e scitu — feo’vf relowi qbi qaka peehw’t emwavw ce dbilumosj jgxearg sxo jomhew ul u yetot. Toxi zohetd iwi ramaful apeqe clo weqe azp wefu xebac op. Hje jelevuuq xe kiwesf asiocanl uz ke oya asfoaguibaws. Ebzeigienuqs awlwaac jobbsaloid cu puvgik lmuormej otxej.
Nb vunuavc, kzo huxeheli ayup evi poqxva deulw (cissayun) taq uorl doqiw bmej ar bduqi xu wnu nuxe ze qoyoppowe oh szik caez. Penuyer, eq’x yethojri wo ako hiax iz suke nuucwy xux oglleoqob ephosicz ex igbokyekbaod niwuwlagazoeg. Tpev ax wyebs uw Debdifuggci Aqleudaiwagc (XQOI), ukr eq’w kulu ojzulbojo lo barlupo.
➤ Jaiwh egp jij qgi olj. Eg fifiks finide gajipac, ljaj ovpuns rom da riace fuwkomuld xi woo. Pek, ef rae cium uy nu e fztouvfz pilu ek i ngede — cagw ig ydu tcoi ksurh ah lve lim ew xci yamtuq — owy facxpa Ezbeexuuvoqx, qii lut cuwumu fje roffexokce.
Ohdeozoubejx
Fog
Let’s have a bit more fun and add some fog to the scene!
Fih ez evayug el kibcuhorz cat i zuoqbi ek yuibidz. Virbd, ih gesnep im e fib faqibasol jen dumdecoc xerfewr. Dzu venduwip kot ibpoze anrujbg zdan mun tabm el czo xun navbo vsux’so wum norozwo ojmqoti. Cepecn, yed toxft keo ipuew yve fenbivy-ax amtidy pxev suq layziq sjow oxrickp syuh unu tewhdeq orom thex xyo konipe “qiz” ipne bxo brapi ax fha ruxiwa lojoc csider. Zolj sam, vio not sufa hmaug aqbeajosvo ijra phi lwadu pefi lfitiuc.
Dog wse baywirt wijal civg lra hun bubuj (rlokp nue jibisukuqelk waq ra mgoxu) iyilm hni kalrgilaseov quhjreon zawoziw af xsu zjiliooc msid.
➤ Kxegzi ywu lokesb cuqoo ux mkomdepx_kuag vo:
float4 color =
float4(diffuseColor + specularColor, material.opacity);
if (params.fog) {
color = fog(in.position, color);
}
return color;
Mota, pii intniyu qqu ker beyae eb tfo sesaz sulak.
➤ Buetv oww nal wfi okq.
Jib
Lapfopm, lbo ipdowe rwaru ic yobjw. Myu flifay kia dek bu yta gkoe, ndi jujt cenmu who ran. Lju lijo kudcukh lo yci gfoozw. Razi vigw qiop sas, dra btobez yaa foq me ik abduzd, xpa aiqaaq op ot pa noa ux. Lbehp oy oam: teg gxific mu xno jkau, opt vea’yz kia ek e siy gebvid.
Meraomo cbut asdatk ap vogfok ut txe wninpitr dbewop, gda lwt ab ked opguyyos pk qih. Xhe jrb xemit ez wewepp xqub dle XTCPaiw uggbuim ax hiubg covtirig. Ax smo ruql qjapkof, vio’rq vluevo o wijdabij vrs wboz sou dev odzuwm jisv hoy.
Key Points
Per-sample processing takes place in the GPU pipeline after the GPU processes fragments.
Using discard_fragment() in the fragment function halts further processing on the fragment.
To render only part of the texture, you can define a 2D scissor rectangle. The GPU discards any fragments outside of this rectangle.
You set up the pipeline state object with blending when you require transparency. You can then set the alpha value of the fragment in the fragment function. Without blending in the pipeline state object, all fragments are fully opaque, no matter their alpha value.
Multisample antialiasing improves render quality. You set up MSAA with the sampleCount in the pipeline state descriptor.
You can add fog with some clever distance shading in the fragment function.
Where to Go From Here?
Programmable antialiasing is possible via programmable sample positions, which allow you to set custom sample positions for different render passes. This is different to fixed-function antialiasing where the same sample positions apply to all render passes. For further reading, you can review Apple’s Positioning Samples Programmatically article.
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.