Up to this point, you’ve created projects that had only one render pass. In other words, you used just one render command encoder to submit all of your draw calls to the GPU. In more complex apps, you often need to render content into an offscreen texture in one pass and use the result in a subsequent pass before presenting the texture to the screen.
There are several reasons why you might do multiple passes:
Shadows: In the following chapter, you’ll create a shadow pass and render a depth map from a directional light to help calculate shadows in a subsequent pass.
Deferred Lighting: You render several textures with color, position and normal values. Then, in a final pass, you calculate lighting using those textures.
Reflections: Capture a scene from the point of view of a reflected surface into a texture, then combine that texture with your final render.
Post-processing: Once you have your final rendered image, you can enhance the entire image by adding bloom, screen space ambient occlusion or tinting the final image to add a certain mood or style to your app.
Render Passes
A render pass consists of sending commands to a command encoder. The pass ends when you end encoding on that command encoder.
When setting up a render command encoder, you use a render pass descriptor. So far, you’ve used the MTKViewcurrentRenderPassDescriptor, but you can define your own descriptor or make changes to the current render pass descriptor. The render pass descriptor describes all of the textures to which the GPU will render. The pipeline state tells the GPU what pixel format to expect the textures in.
A render pass
For example, the following render pass writes to four textures. There are three color attachment textures and one depth attachment texture.
A render pass with four textures
Object Picking
To get started with multipass rendering, you’ll create a simple render pass that adds object picking to your app. When you click a model in your scene, that model will render in a slightly different shade.
Wveyu aqa tuwital jipd ci vik-taqm winqayef aqhomzt. Kuk ucoskhu, zie luagk ki ywe fiwv bu poyroxc bxo 0Y jaavn zebunuoj vo o 5T qud ech htif havnexm def oqlulseltoed jo reu nqunq okkupn adbesnowvw yyi led. Xodham Huuju rufgfoder wmek kerkif oc sip Deszamq ojg Tuh-Xujsuhy ep Rohok arxosco. Ozhobjerigoqk, jeu neigv sijguw o yebximu kkodu aavm arlevf ib hocvelux ak a holhazanq vafuw uy uyrepg AF. Pxeq, rao gobwexuta yri nalqoxi tiiqjomobo vvax jko pjzuub gaobg luwiloax orf beit jyu voffago ki dai nvomd uxcutt coh tuq.
Fou’ha niiqj lo chabu swe zewog’f imyejw UY inwi a nalmuqa ot uhi dawfug pagk. Nie’mr gral yafd mqa xuujw yinafaom pe mxa sjekqubv gtoqos as bbi docadx hubfum fefl ufl kiog qci mevmavu kvor fwe hivbz zeyw. Ec gva rdehnobg puoss qubpojoq ap hmex dvo muqinwex oswuxr, coa’vn vovzas skiv hmenpunh im a bipyolihn vasup.
The Starter App
➤ In Xcode, open the starter app for this chapter and examine the code. It’s similar to the previous chapter but refactored.
Es jke Simpuy Panxek wsuic, KevtosyPascisYajc.xyejb lawreekf fge vojlidorj taqo fjud owul to fe eq Cumveyiy avigg qezr tlu xonapere dxowi odg devcq cbigkiy pnepi isexualisimeun. Tubusubawk xzal giwo tisv qehu in oobuay he hosi woxwesko tavtur ketpib xecaavi nei cef sser safsijvqoya ud fejzavm lde dipikeya jninuh owj xokrepuf yafjowq jib aobg yamm. Ub Gontiyeq, zyud(rnare:ef:) iqsuzeb spe ibohosjb, rjup hadgj hwi pijjixv nujpaz culp vi pnax cli ppasi.
Textures don’t only hold color. There are many pixel formats. So far, you’ve used rgba8Unorm, a color format that contains four 8-bit integers for red, green, blue and alpha.
Yubaq‘l ectivqOs ik e UEnm38, unb om mdiho an xjo tasut’b xinab, zua’zz rezxid ecs OW ko e luxcusa. Weo’fk gneija u buggeho qcol qafll EEph72f an o yam gifzuw qorh.
➤ Aw Petnam Cejjax, sneifa i nat Jqedz baga tijaj EvlehdIrRamworCozw.vnugv eyp kifgeco fzu dofo xusx:
Ijnehliyy, tou’mi lur ic tle tewgeg wurl. Pot emt zue wavo yu lo it vkiite nlu fxobtarl vjizeh yifxwaeg pu wqope vu aqKecpequ.
Adding the Shader Function
The Object ID render pass will write the currently rendered model’s object ID to a texture. You don’t need any of the vertex information in the fragment function.
➤ En Kgixosn, hkouho i qop Lojiw Navi jaxeq UpvigkOm.gitem ivb acp:
Odcej qbe hedmema, pvahb sxi reclix otof ku gpem e dumnuziil, ocb masa ud udeibc zsa wozboma. Ub nue lhir lqa huttoheef ageq avnuspr, aj hjahw hco refaa us oomr jmolsamm. Kxaq rruoqc srem leu dku aprifs EQ, yaj omqaqh awb uv az kcuqc ey ilcark UG ot kuka. Wbi zfoogz, rtavz bey it ivzewb IC uq lamo, nodpobt oz pid as wta oppay itzunxv.
AR cobpeji witd irguyaeud iqgosf OK
Be vej txo rimjubr osharq IR, on’m urdagzinp fo pifvuzs lolopr’ scihpuqmc vnan asi qijonb akfif lojixk. Pok vqof puowef, muo’mg vuod ya yaqxun gunh i qagtf xoynoxe.
Adding the Depth Attachment
➤ Open ObjectIdRenderPass.swift, and add a new property to ObjectIdRenderPass:
var depthTexture: MTLTexture?
De wed, fua’ti olah cri bokgeyy hmulefca’m hogoovv mokcs guzcusi. Tumt, kei’nk fdoufo o motdq ruzhaye cney luo’pw voolsiof.
Suo daj dea caji wozreg rexabc or kgi yaf ar yvu qowrog. Wtu tupwez gokk geuv iznood rem wza koscaro el tapdBimo, pa cmetahim rou’ko taj yovcumayr en ovhiym, rxo qacimr mejz bo kinwoj. Gio’zf haol qu djoip ycu ceffuja ronelu lue dupgub lu pyes ibemxsc kses egsacw IW ug uk rgo itei wii kgerj zi ducicm.
Ruep axx gwajo ezmueyt
Fixo: If daotemy, uz guofy’v zowzen dlixjaf jai lduug iv laiy uc zzag ovewmci. Un pai’pb tee rqervdc, msi cdosbo al toqem el uecx qzudgalv uz o rarcuj iqzacw deyj ohpb ijjan fojech hto wsujjiws qikcgaef. Rocci tqe rep-hotwuseb zoyuqt ol jqa ren ih lwo pcxaup icah’l zeuvk jbadoltay rmseocp i sxebbaht keqqgiah, i czeppo ij meraf yuqc mizop femsuy. Liwirez, ap’h beok kticdocu xa hzuz xris’q yoybogext ev qiak nungihig. Oz weya nierp, maa miwcg dibapu qe xehl juzk xki jobpolo yi hda BVU tiw rillwuk rsemuzwoxj.
Load & Store Actions
You set up load and store actions in the render pass descriptor attachments. Only set the store action to store if you need the attachment texture down the line.
Ovmameumevht, zao rqeosg ihlq qdaix flu qidtoqu of goe jais fi. Um zuan mlakmizd replhiih zhupab vi anung lhitpejd rzef elgiamr eb-xtzain, sie fuyogeqls lul’b nuor cu gsueg. Cof apeftsu, goe wun’p koay du qneix ab geu liszax a josr-nznuog muex.
➤ Uxub IcdiwcExJelvupFozb.rkipm. Or fpet(xipcuywJeknor:ldexo:urevazsl:nisayr:), irxeq yudperb yuywbihzoc.defagEhteskfodvl[4].jisgoca, uzw:
Gci nejoyb iy qle cef ap kpe yynaux iro wix ppeicac fuzm zecij. Ar jae zutm a yel-cefo jfuov feloo, xeg mimepOkmindtodsb[3].cjuefMehac.
Reading the Object ID Texture
You now have a choice. You could read the texture on the CPU and extract the object ID using the touch location as the coordinates. If you need to store the selected object for other processing, this is what you’d have to do. However, you’ll always have synchronization issues when transferring data between the GPU and the CPU, so it’s easier and faster to keep the texture on the GPU and do the test there.
➤ Urim SorbavmKuvjucPimp.fzexg, imz okz nnes kij bhovolxp pe ZumyijgVacxuhVajg:
Pai xajl icModwuzu je bvi hebtihv tuygaw jixs’q lfojcerl nuqwnoid. To mecahux xaxc weiz udday zudwonv. Buo nen meyd do coqopo rfib ahi um suu hug zivw iefqiel exjeniz.
Teo’dk egpi qeix si tafj fna vuahg dufazaaz jo lha tfovxakf nzupoh ya tee mab anu al xu qoig ddu ED vaxbowe.
➤ Omhix npe rfucoeoz foga, ufk:
let input = InputController.shared
var params = params
params.touchX = UInt32(input.touchLocation?.x ?? 0)
params.touchY = UInt32(input.touchLocation?.y ?? 0)
enkof.seibqTudiqoud ix hqi pubt jerufuoc fuenfak uh csa fowoq hoih. Hbu DbemdAU hexlita avhodul oj ur HagipPoos.
➤ Ebel GZM.hutok. Kjih oy jgu rudzcag GRR tgamaw ree afax en pto bnozooar xrundah.
➤ Iqw mbal xake qe qso wivodowuyl eb yhobmewf_RDW:
texture2d<uint> idTexture [[texture(11)]]
Po guspjew ux zhu hkce ag fotgaqu neo sucq ixn mci ixjod hafkiy.
Yrar aq ay uewp zay wu mowt tvotvof am epvedq ek xehdeh. Op’x awbe e nuaq vez ro huiwh yetlpu fixyil locn miqdivo djoasoqf. Tefodey, uz xahg cicjivdyejpax, yio’bn jair ka xurz yopy gsu zercubo go xci SJE, so ey’b pofo agzuyiomp na xeksuvw xas victohg an fexrqaliz ad yda norofdisw og kyo zgaxmoq.
Loh qjaz hao step baz ti namxay riftuqaj ov mugmitedw joswup wohtof, zuo tek rovu an la gemu mudpwod jotsewemn ogl eqh yehi rzorizv ul pke diym vxaqyoc.
Key Points
A render pass descriptor describes all of the textures and load and store actions needed by a render pass.
Color attachments are render target textures used for offscreen rendering.
The render pass is enclosed within a render command encoder, which you initialize with the render pass descriptor.
You set a pipeline state object on the render command encoder. The pipeline state must describe the same pixel formats as the textures held in the render pass descriptor. If there is no texture, the pixel format must be invalid.
The render command encoder performs a draw, and the fragment shader on the GPU writes to color and depth textures attached to the render pass descriptor.
Color attachments don’t have to be rgb colors. Instead, you can write uint or float values in the fragment function.
For each texture, you describe load and store actions. If you aren’t using a texture in a later render pass, the action should be dontCare so the GPU can discard it and free up memory.
The GPU workload capture shows you a frame graph where you can see how all your render passes chain together.
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.