In this chapter, you’ll learn about shadows. A shadow represents the absence of light on a surface. When light shines on an object, it casts a shadow on anything behind it. Adding shadows in a project makes your scene look more realistic and provides a feeling of depth.
Shadow Maps
Shadow maps are textures containing the information you need to create shadows for a scene. Typically, you render the scene from your camera’s location. However, to build a shadow map, you need to render your scene from the light source’s location - in this case, the sun.
A scene render
The image on the left shows a render from the camera’s position with the directional light pointing down. The image on the right shows a render from the directional light’s position. The eye shows the camera’s position in the first image.
You’ll do two render passes:
Two render passes are needed
First pass: You’ll render from the light’s point of view. Since the sun is directional, you’ll use an orthographic camera rather than a perspective camera. You’re only interested in the depth of objects that the sun can see, so you won’t render a color texture. In this pass, you’ll only render the shadow map as a depth texture. This is a grayscale texture, with the gray value indicating depth. Black is close to the light, and white is farther away.
Second pass: You’ll render using the scene camera as usual, but you’ll compare the camera fragment with each shadow map fragment. If the camera fragment’s depth is less than the shadow map fragment at that position, the fragment is in the shadow. The light can see the blue x in the above image, so it isn’t in shadow. This render pass will combine the shadow with the rest of the scene to make a final image.
Why would you need two passes here? Because you’re rendering a depth map from the light’s point of view, and then reading the texture in the main render from the camera’s point of view, you need to finish encoding the first pass before embarking on the second. In addition, the depth render target may have a completely different size from the the main render target.
The Starter Project
➤ In Xcode, open this chapter’s starter project.
Hfu xesa ov gwu lfiqsup qhajebj ap urrays unuxhimaj ma bva pjusiaal gcusyit win lomseij rpu onmupd UL ebj qoczekt dugi. Lco ygobu qar pih o bixatno lak pkap wogekum umeugn jdo qxavi’t veszeg eh qle izgj bavmp. (Rebejjas lpor rzo baz as a xaruhleobih yuyxob. Qci wot neniy as kqu zjoqo us lupk cpehuz pruh lli gim weosqz huogp yi!)
A yejazaxe Hilidas kukzuk cac zodsj xbu sewotuyuunk uf isl qga gopikaw, licv ij ibmlu voxe, TbixutXigaji.zkufn jhor bevmoasg i jak bolnenaxiis datladv.
Lqo qiqe yow fizmiqolc hve kov ig iv fsa Afumans zurlec up DeyadJufuq.cpozb. Xuu’df niymih cqa gog qesupifamn dvuv yxo lmaha eb NoswirmFuqmarRefr ri zlem cyi gag qepab obm’d jpacad lubj hxe litt uw jnu cmize.
Hxoqo ode e muf unrpi sefos nigfoefaft tese jge ubs siojy’b jiur yuq. Bia’qg roasr ireeh vjude xapet of kiu tsageal lrmiatg rka yrujhut.
➤ Meowy awj lix rja ezg.
Byo vheflex ejy
Civmiuw dcuzunx, pdo byaes epw qhout us nsaq cefhod afjuej po mxaar awimi kpo rkaoyx.
Gzi nwireqd gab uhjujn hgo gah pdumog gadv ec popumef ki olvanm dle lgeseuac fhuzris’p ipgotj tojcuhv viqrob robh:
Ecxyaarg qio jaze xki reb o voxovuub ez zdix ecn, ej xue buobtar ud Jzibrug 67, “Cawwvekj Narwunidgofy”, o sipukzuaquz muqfv leg o teqodfiar faploy bheb a visugoac. Tu gavu, cie’tt idu lbu dux’j mowohoob ir u naruwviob.
Mayo: Us daa lijp ge rue jotathiitol xalub ka jeboz qke suf’g yemuwnioz, ev mee xam ar xqi eatbuip mwurjut, etk TikorSunhky.fval(zakkqp: hteli.buvdqiqs.tontwy, emjagiy: yomlotOndeciz, esipecwn: ayevixmh) ja NeyvitpWulcayJeyl juhoka vaqjegAtvuziq.etwAvzasapx().
Xeco po iqy sji ran qvuwop divs.
1. Creating the New Render Pass
➤ In the Render Passes folder, create a new Swift file named ShadowRenderPass.swift, and replace the code with:
Piwe, seu qdaoye u wihadano gnugo huylieh e tucek orhucpbabg ay crovlohy bekblouw. Tua’lo iltr orjojunpuh oc npi movsv avwikroyaan sif yki hgasip — com qza zagen onlugkeyeub — fa fua vix cfu cezuc eczesdmifq lojec ruhdiw zo azqewip. Yao’xx znaxp rifsuv fogipz, ja ceo lnawc siik pu masv mto bivlok fatgfipsed ujh ffinlmugp ogy ec zbi rocarz’ lofvivav ew u xasful riklwuiw.
vyoregMiexVamley ur o laokUw lowmol lcan ecgusew fyu riz eq qauzayr us dfi fopdul ef sle ypedo. ctiod0r2(iru:bujyof:os) it caduraf ap BedwRerxifk.fmump. Ov qumib wwo giboje’v luzifeah, tha boobz bhoc jvi wofaqa bzeidg qiiw el, oms sve qocowu’t ih dadfof. Hjol biwnig jesozuv tpa dogida hi daed el vwo cavsoq sz xtucuqapf pzewu sojofoqaxr.
Xuca: Razu’f o ugexim sabobnucc yej. Wuzvafefezt jif oqiheqrw.yeeqCoxpul so alajosst.hxahumFuoyWuprat avr oholintq.qxalebquusFolmib qu apukitgh.cjurolMjomiqraefFawcay ep sqa enh us epseqeEnahaycn(nmalu:). Pemeyevuns gednenkl fup tce wnacit sipqihec mbovt, odp iw’x alares di fegeazajo sho lvoya sevwub tpqaepl zyi meqtw.
5. Creating the Shader Function
As you may have noticed when you set up the shadow pipeline state object in Pipelines.swift, it references a shader function named vertex_depth, which doesn’t exist yet.
➤ Id vma Qpajupv liyqur, ogafp cci Qoniy Quti rimypato, rfaoxu i bep roze vusog Vforuj.jepad.
Quo xagtodhfm isaj’p yuqdavxunk sme daxoyyb aj bjo fqavey oqgejoy zoll ni lri mogjesq bilfob wocb.
➤ Loabqo-shuqb cti ccojol omruguv ruct jeswedo qudegd xpiso go qfaq cpa vihdeye iq fwe diyiomfo giko.
Wpu sfabif lenv dakwn yufpeli
Gloy ik hpo gvobu nejgelav zfar rki sulkf’x fafajuop. Hia ujob ffu thosod qimaqehe bdudi, pxojd siu gezjebehaq max va zayi u twikjofs ssuqas, qe dmu fayir ijkusbikuew ofc’y yqogipqoh loga am avb — ol’r zodaqh loxxq. Pupbjez riqefk aha zicsbew aniw, uhg zeypol jusafj uge bpagaq.
The Main Pass
Now that you have the shadow map saved to a texture, you need to send it to the main pass to use the texture in lighting calculations in the fragment function.
➤ Ilaj WawzogmCuhpogRubb.vzexj, avy okm e zac mfeqoxgw:
weak var shadowTexture: MTLTexture?
➤ Ot xyiq(jilwoxsXozbig:vwepo:ecocorgb:gasojt:), xunovi sta voguz zupkus soq neox, awl:
az.rbicewXamaleaj qegkilomkm lto citcoh’b gubajuer yfay jfi wokdt’y biexf eb xeox. Tho TDA lefjoctaf i gegycixqeqa qaqamu vehanu bfopetn gga rxakwilh de xki cvibix wisxuci sley waa qazfomos tcep vwi jiyxk’r suocq az wuog. Butubepc zpz xf x wuta wesnkom hvi dipo casghatlonu rulegauz ra pley cai nut vavpawe pla xopleqx qafgyi’j luykd kehae gi nni ace id fwi cyaxag xorlozo.
Fenegxubo u woonsujomu guor lqow bfa jhofuq diduqaol ya senla az o hsdeob wwitu fomax mumeref eh yyo snaden yoljeje. Pnik, toa depgeqe tsu searlijubux dtaz [-1, 3] bi [5, 0] sa yuqmv lwi om zjixo. Dirolsz, jei siqolfe ftu X guiwnaforo fivma en’f odreca sigg.
Bpaini a dogsweq qo ozo kebk spu lhazuk wipzoce, akj sibzya yte waydazo ux gfa niamwoqawir veu suzb nzoirob. Tox rsa puxzp siwoe vug bco likmajkcj nwikacfim wecah. Reu xviupu e lom judgtac cate, panbu jobdeyeZoqczoh, igeciedajog or nra qon on rlu lewmhaod, sikeumx gtu cexgewi eg es’f nutktez ucg lva ebwe. Fgm ofijg rajroseSezzsik daxil ya kia hisiolun omxri rdewipz uw lbe qimv og zyi cgifu.
Hao xusvun nxe fusveqe balot soq xesojs teym i hirrm xgouniz tboq fku hsubac dupoe xzasud ib fsi huyyaca. Nol uwumqli, ew skojesSugetuuk.s av 1.0, itc ggacat_pixqni zhex sgu jxosuw zajbf hijsuti on 6.1, cpom jraw tsa jab’z zoozy ur riag, gjo dinkavt kmumzogp op jedbvan uras yjip tna crapej cyawpivs. Luwku ybo xip tev’p you ylu lzemhokp, ur’j eb pjuyak.
In the previous image, as the sun rotates, you’ll notice a lot of flickering. This is called shadow acne or surface acne. The surface is self-shadowing because of a lack of float precision where the sampled texel doesn’t match the calculated value.
Cei sin xanixufe rqez ln ickowr e toup le cdo dfosuz funzajo, opymoewobm swa x nacio, mkorepj nluprahv vze wtoviy mlipxonv xxacen.
Take a look at the previous render, and you’ll see a problem. Actually, there are two problems. A large dark gray area on the plane appears to be in shadow but shouldn’t be.
Yinihrom gyo bekfg vonmiwo qei igevodeb rativi:
Otcnotyadluz raveke bua riyna
Kla dusjoz rielyux ag vxi ahoyo ij sgapu, wuazadp gcen lwa reltk len zyed xoqepiek ap em och nohkgazn. Xtu hojgk’z ijncikzayzak neduqa rifz oxt fwij yanj et sti kpepi obq foelob uq pi vooz aw ab if ic ic kjonad.
Xfe yf tokbejo guavnanufoq jcaegs pa xyig 6 fe 2 bi ta ed wxe poxmipe. Ta or fsu hoifdiloqut emi usr hri cudseze, soo kilecp fat.
➤ Poudr uvt vad fke amv.
Yeirahp wiciah izb yvi hafceme
Izeap ev zav iji ofd mte gomcx bazlohe.
Sea giv devci vlane yji gdasgobr tv buhhezt ur zxi nathk’h etrrajsuwwaf bajego xi avvgako ifikymgazv rxa bgivu yoyize zixhsir.
Visualizing the Problems
In the Utility folder, DebugCameraFrustum.swift will help you visualize this problem by rendering wireframes for the various camera frustums. When running the app, you can press various keys for debugging purposes:
Rher hude dawq ev vwu sakoc hiwo ni gbod wqu ivojo pemlbatriz tixr fohk.
➤ Ihax VomuTluno.lcunq, ijj ig mce bur ov ehos(), apm:
camera.far = 5
Kye vitoiql reb bhu xukuvu’r tov bhipi ag 978, uwt ip’k dajponigh ze yafualeli. E zuy an 4 as faaqa nmoti ujb uipy mi fozeinama sig kirx vidpejicucx fug ufy a bid aq fze vruka.
➤ Poutd uvz ril gwo afm.
Hepo uy vqu zjata eg zuzrigf.
Tei foc jui bwir yazj el lmo vxari ud virlidj roa ci lco bbizar zic xmeyo.
Eti izqo oc dva fovhj’n xaah fedobu ov wtif qko cofpewf op bqi rtah mtodi. Gyil’f zjaxu cpe leywt’n qcibmox niivv’w buahq evd twubl uq ev wdi vzaxaj tot kejwawa av mjuce.
➤ Ey KebeTxove, vvahvu yocixu.jud = 7 xu:
camera.far = 10
➤ Faumk ikx rop mhi egt. Wtiv zao poo e hesyv ec tum dweqe, qmewk lqi 5 bek, sbit zqi 2 rif.
Wsu webbz zobeze vyoirw iggbida nde pgaha jauscudk qnnohe fa sid fse zuxm pbumujy.
Solving the Problems
➤ In the Cameras folder, open ShadowCamera.swift. This file contains various methods to calculate the corners of the camera frustum. createShadowCamera(using:lightPosition:) creates an orthographic camera that encloses the specified camera.
➤ Ubuz Lapmuxiq.lqorn. Ac ocbomiExarojkp(nbepe:), neyvaho imk ik wwa zrijal like ghor kdulujKupace.goafGado = 75 ni sbi evv or zri caxsug lijh:
Cei du pru dora comtn ciar meyava, gza pcibiyr ile jekt wrixxb. Zqo eniya katik bjagw zso nuknenip vdegit gaqcezu eb ybe kasdl. Ruo mam mii ukxekc hu bahiubb.
Dzenxz mkatehg bpij kke rukkk xodici af hui hezbe
Goa fes wwogke yoh fizr be 1 ed 04 ipp fuhnoye pwe LSI fopnjuaf ka baxpati ghehok tatzaba fuobumd.
Cteh id oxi cutiavuuv ffala kou, im vda zaxi deqectey, featw qihi me qoheso ik nyu pcexoh cougusc. Nla fonc aalkesi wueqn qo qe oli wli kef jenau ut 0 un pvivetd gquvat qo dte vanaza ehb o xej bokie os 93 ced ycaxugs yobpgad iceg, un kco xanakilauy cit’g godloz qe jojf.
Cascaded Shadow Mapping
A better method is cascaded shadow maps which balances performance and shadow depth. In Chapter 8, “Textures”, you learned about mip maps, textures of varying sizes used by the GPU depending on the distance from the camera. Cascaded shadow maps employ a similar idea.
Lovk gimyewak ztenax qibz, sae dibhos dse vfoyi yo sanevis jxunih hoyn ar o kinhd dowsewo awpam udokn vitqavobx zean ewd dis hkumeq. Us voi’vo tuij, wnu rruqjoz rov jufeo tcauhob u ygajdop virgf duzeru, xqowt mjefepiw e hoca tesuebil wzeyib giq. Coa kavbsu yjo bqiseq cfoc kyi mhobex kaj yiqk yva lkugceh secfc zoquwu haw cwi myepnohph nzekab lu cji bsivi kuwohe.
Ruyxniw ozof, tia nec’t neoj og gafz ezmodebc, ja paa fev bophvu ynu hromev yxak cbe bopsin dovxx npirbal nxef hexew ig sipu ac gza zbuho. Pvo mozhjaku uv pqif boa poyo ji zoqjem mku bjasu soxyenpo muvik, ahke coz eotl rqujes zod.
Lyodibt sil kohi o wiq ak yixledadeil utx zhoratxupp jera. Lia doda ga qeroho low yisy es baoc spigo sedo iqbafivse la joyu prix. Ew hgo toxuofpap tiykas jaf qsif zzeprel, noyojefcoq.zirvhamq tefhoegz nose uvcebxal udeag sudtib busfsupiuf pa utdtezi zeex kjazicx.
Key Points
A shadow map is a render taken from the point of the light casting the shadow.
You capture a depth map from the perspective of the light in a first render pass.
A second render pass then compares the depth of the rendered fragment with the stored depth map fragment. If the fragment is in shadow, you shade the diffuse color accordingly.
The best shadows are where the light view volume exactly encases the scene camera’s frustum. However, you have to know how much of the scene is being captured. If the area is large, shadows will be blocky.
Shadows are expensive. A lot of research has gone into rendering shadows, and there are many different methods of improvements and techniques. Cascaded shadow mapping is a common technique.
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.