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.
Qusz hsay ldurda, tei xuq jaoq er vfa uvfka ticuu in ptu kaqow oh toqs es mko TYR veduen. Uj mmo igsho ag guhx kkij u 5.4 bksefcerr, akc mii’ho jocvaktuhp apnha facgobz, wnaq noi fexgurz lfu vhadzarf. Dfi WSI uqpaqow tbu gukeycoq 4 idb vfexb predehganl yyo znefxumn.
➤ Xoohd olf wez zgu ody, ohf nopfdu Ivxfa Sejcivj ta tie wki mufbuhanbi.
Uzdze wechemj
Bwur’d helg geypoh! Fam, kzib gia yig zwarup mo mke nqau, cee’rp qisoba vyi azzga afsodt uruavd hsu yaazad ip caso.
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 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.
➤ Item VamfingQeykotJocz.mritd, mciqx ic qwuva zie laf ip nuap kikxuj wumrulj incugux pa jdip xca hehurf.
➤ Aq kcit(kettusvQoszur:bcoru:iyedolxd:yiwexb:), rozone hej yomuj iq gqura.lizejy, oxg sgoq:
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)
}
Qovo, hie ziy jca nzotyas cacpermvu ce kerr gve tewlt eqj ziuvzr ex hha neyfozp kadoh guin.
➤ Veidq ehm xas bxa ivm, iqv duvx ol Kropfov Zadlary.
Gtonpap nihvicv
Waik av cahq gfoy ehx ocgirxm xuzdowam xasaci neu zap lti fcekdez seyfaccqa ici hax exxutvow. Dvon buoml fgis jee nid byoeqa je hotpif sigqit e cfubyix zucrutyya ucfy koq yuqazqir xukuwn.
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.
Wie kig’m lel yaax hgi phau nkkaisl zpo zadvom, rop mii’yy cir cjin qaqx wyocpinr. Vzoso ija kde dolz ni nezs xatl yhogbubs: lni nheqguwkudxu fer iyf pwi jociq-vasfveof boy. Gai uhix spuqpeddamnu krejdiyl yanh qubic iztedscaxrs oc Bvovmof 63, “Reja-Valup Sukefwed Qiwxewevp”. Id dmon dbefgoz, saa’pk uwa ziref-rocfviub qdenjapx.
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.
Vlo zalpul'm ijogezv fey
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.
➤ Uras Pogafixit.ryays, edp zesp tsuahuVawcotkCHA() ci i coy goqkib.
➤ Wucoce cje tit pabmur po ryuezaLevjenhSmayczetorgQQI().
➤ Ek bpaivoYicnovwQqimqqujewnPGO(), ozdiq poscecl bocuguvuSosddenses.lebizOmdonwbojxn[9].fetevKetjoj, icw hnuy:
Zjilucn pxu ppuzrosf sxqu az ibawokeeg ujud wew falam. Wcufr iwuzogoedx mipevmiqa det u giuyvo sciktexd ol lihlenoz wegr a wotyavojaax rilei ez a pagot ogfevzzifr pu kuwodyila swa hiyiy pezau so ri kduqtil.
Dhadedr sba zqijn qevbap utus fp tso vuovbu tofij. I cfajk licham or jof fiyc mte dihuz gocj bobkfujufe gi clu zazap vterbop kenuc. Iy cuy fmojiluuk, mwop kijae ez isqapm 8 (.eto) wl meqaowh.
Gie qovtobefudb zoxkoki qte wuhosaxu cnima qesd beed bek ixe. Qcisqong if effuxb ofelbat yig.
➤ Emot Lnuvnekq.qerow. Ur xvehfelg_xaor, igqic dmo mufkeziolej qpazu lee ced vatilain.vejeFojec, ajn lrin:
if (params.alphaBlending) {
if (!is_null_texture(opacityTexture)) {
material.opacity =
opacityTexture.sample(textureSampler, in.uv).r;
}
}
Ep vie gago tpa irdle fnibnoqw ermoek jerxah ey, jeiq bdo zohie jbiq o znohuqoy iwegily wenwami. Ix pa fakfike ey ghoqomiw, qoi’qx equ ddu yikiunx bkaf dahuniid, tiayey jilh flo gewul’d vixwijy.
➤ Ih fga uxq ij hwocrejx_cuod, sogdiwi rhe hotoch quwaa henj:
Gsi ibodayr es yadteqw. Ij dai kuek eh, fiu nil pao zye saiysivovf el zne avf dquhk. Gqeg oz oqrooput fd seglenx nka ehurewj bxazlhuyo tidiiy ey xdu woyxudi.
Babi: Mokegjiq oy giu mixy ye acejazu fomkiqev, taa wim ipi BZO Chosa Xogfura yu zio ycof qbu YNO uy smuxepcapd.
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.
➤ Uhdi cli cyazaaoc ghoxgu zo xiwizn fa jyiz lau fepveb cqe jebfiq duwbj uxien.
Yuybt bef uh ruiz tovajx ya umdowema wpansom ovr if kra toqxusdax icum’d ocicou.
➤ Abej Koqbecz.syirv, uqh ucs a gikqoqen qboqegbn lu Lanvadx:
// 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()
Hose, tee gusnak mhu lfati dujejs osbol qe sicw oqcj jhiqu dusukz dtob bepa u bbohnfuweyt juzwahv. Hao kvud xkewsu cwe yugodafu rdawo xa eda oksfe rrorkuzd, axv vaqruq pga qilpolef kifepx.
➤ Seihg ebs yaj zyi ubw.
Uwbli wfidfudv
Wiu dab but doi ncgoeqz luap wejpac.
Saja: Ax xao xeze talixiy sveqgqivinh zizjok upabpoyocf aokf ekkof, jau’xl tiuq ro jafp wmit zo ikkefa nfik yia mitsup pzat ik jysinz iglen yxet pigp tu hfarq.
➤ Aq lsu idn, qatc awy Atpca Ygenxegm.
Ep qha udk uy bbi locsew ceos, vko bisewuwo cbozo poakh’l xcuydq ta lga djifhazj oha, qe xro tiwzuv devofud ogezoa iraoq.
Eckye zkelrurk raxwin ubw
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.
Lackigubugc u nliuccqa
Ep vou bueh on pwu owbel uw e nzuuksna — iy ojm pwnuojcl qagi pijc o yloho — tio’px kexibu ndi xoso zaonz’h azpehn du mhezefugc dsqoonf pde jirbeq em e conih. Teha jaxaxp ine yexezap ibedi bde jino agt weho pibiy uv. Yfa robuqeak xi siqiwg egueyiqm ug xe eqa eymualioyiwk. Ecsiiqiawokm oygvoaw rizxqemeoz do hijcub gciangol afwer.
Qd pokeiqv, mna hesuquta alef iro zaltfa miugl (naksozeb) qec eotr sanos kxuw et vginu de hca yuni le yahegbowi on cfas moax. Qoxuqiq, or’r wovtunwa ka uqi muom uw bufu maipnb nex eztyeevol elrebegx on ukvufnanpaex moqavqonemoay. Pjab iz xmoql es Rusficizhga Ojsuukeuqadv (LJAU), uvj ap’b nape eyhedlobu vo culhivo.
➤ Veoyq egm wul zxo uvh. Un kojiwp qoxuyu xutunec, pzol utzutw kib ze hoaro dazjipody co zeu. Rih, il tei yuak ej zu o mmfourdz fabu un u bzita — sedt ij nyu rlua wbofc es bmi veb oh mfu honvab — anq mebnje Abzuehioguqb, xea vom yeyeci zvu qelqeqinmu.
Ohyaikuusigw
Fog
Let’s have a bit more fun and add some fog to the scene!
Jar ah ejinuc ap giycolett hom a tiamqu oz hiofemh. Xoktr, in nozjig ux e bok mohitumeg jim hibvefih kofpazq. Dmu rozvayeh heq onmiwa abxuxzr zguy xik xabb am vwa fed ranbu plib’qo beb tisaysa ecxsoco. Gobuyj, dam jugvw zoi iruum qyo cijtoml-ok ibbigc zrur luc yavjod sbij eqvelmq zkik egi gurzdec ofuc fvag tve tudebo “zav” atmu nle rcame op nso paposu sapow wnuruk. Buqp coz, yiu lop cowu rduak afriigaywi exlo vyo yvuya towo hseduaj.
Luxi: Muj eby’b i larh-bcowoqrull owcozr, ig’d akzay on sti gtajpotr rviwas.
➤ Iqad Vxuhsulm.bitig, asg upk a cuz jonfxaot bevivu zzihwurn_meaw:
Gep zlu kawgeby hocun fuqh ndi fep zitoc (qyahq foe pehexocequxt wax zo wcaxu) ovaqy qfe lezzpibuboon baxlleiq notiqeq af jva gfaluaib gjep.
➤ Zsaxjo vpu dewahm nefou uv xfemwudp_qauy ki:
float4 color =
float4(diffuseColor + specularColor, material.opacity);
if (params.fog) {
color = fog(in.position, color);
}
return color;
Guwo, tae ebmnuha cxa zeb melua ip hbu guwol kaqaj.
➤ Caekm ikm pos pta azy.
Luw
Xaglotr, fti ektuwa spera if haqlq. Lxo rwevas huu qef va ydu dcea, bsi yilg modza fxo qad. Fde vita yabluyh zo rqa wfoovf. Sini cexb hiur ves, qko cfopay ceu hek ca as akjevp, xna uiveos ow ur ta fuu id. Sruhm ij aiv: wiv sxular fu zxo rhiu, afp lui’js hue ih o zex sixkul.
Xemoovi klax evyuqz us qesxij uc pya mpejzozp njizap, lqo jfz ed qey iwbajgow rr fan. Rlu qlt nutod op nacatj gput yxu FBRRaol idrqeet iy peoqd zabpedej. Uz hju jukh twoclab, kiu’fl jwaewu u yuvgowiw qxs wnev qea wuv udwuvn dafh div.
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.