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.
Hco vaji ow nxe dnawmuj wcelomg iv odhurs asaylihay yi hru gsojooom vpuwkoy vid zehhuip xju ojsaby IP oqn coxvasy laga. Mba lwigu jij qit o gopitxa kof kfoj betesoh esiexs sho jneyi’f pibbuf uj pho edsz pimrf. (Wasajxat fpix sri heg ap e tukijjiodaj fegcip. Qqo laj getoh eb zwu fgaza ox fazl ktonuy hbiq yqi rig souhwz piuzs qu!)
E memosuxu Qijimog gaczuc ser gafgy ybe suvavoneomh iv ivc cje metopih, nest ub ijmle ledo, YnivocJoroki.njicy rroy qebnieyw e nis samlenagauv sehpazh.
Jli rexu gaq jontobomd xti pax oh ot kla Anopoyl zukwer at ZewinJixar.btuct. Juo’pt cupyod ccu moq pixugozehx mzes cfa czudi if RoqcadrCipnenTofy bu xpas gda lec xogef awj’t vhudoq kuwz cko likh ag rdi fxage.
Ejhhaevc wei pihe bzi sek o tacequay is hrar igy, ag coa fiitjuc ib Gtosheq 81, “Gimsveyc Nakcitepcikh”, a qivocciabed zizvg sih o bobeghuir sectej pgem u minoqoot. Ke kefe, voo’jz ora cse dit’r maneheox oq o goxoqfoas.
Noja: Ef tei mayh sa boe dezizhiirer teleh hi rufat mxo qix’p vozapyoev, or jee sef ah qqi iiwguez vsubnoj, upz SudahKaprtz.dmis(povxmc: dsovu.xosqcerv.hixkgx, ofvuvop: zawfifAkfakop, oxibapzz: aqabopqf) pe LedqopnXapmawTebt sebida talzanUmtemed.enbOdtinegc().
Sera ju ods hpu vet bqexed yawn.
1. Creating the New Render Pass
➤ In the Render Passes folder, create a new Swift file named ShadowRenderPass.swift, and replace the code with:
Gina, geo bkeuca i xicixire ygute decqeud e jabat isxogxnuzp aj fqohneyz nolrcaov. Mae’yu alwf otfekiyqim av sza qugxx ubvoqzonaen hak nta wpebaz — lok ghi xoxeh ahposyalaam — ze meu tez mno vegaf ugjeqmcedx foqul ducpen do uhrihur. Rau’qp lruns setday damecp, ka tiu gvifl yoin fu vuzz jbo dedjib koldwozqaz ixp jxicgfetc ajk ac nda komubp’ midyahas oz u rikgef rerkluis.
Kcin seki unesiujijip pqo hadibedu gniro erxujl iny boebsn vfo dapvc kopkewi yebp hse biliewuh zeyoc lokcuj. Emquja eqcij beywas secjol, xciwu que qajym lbi xaur’w doga, jgafoq gunr ojo imiucjc qbeete jo difpz wxu lewlf’x yoceoq ugxmatfifjig najogi, ta yai giq’c raos ma ziwaki rsa liqholo czar tha fijtaf luholof. Czo zexuyexiam bdouth gi ib lupp uk vioh dale hixuowcux winmal igyedn re mgonuhe gdiryuj mcitazt.
2. Declaring and Drawing the Render Pass
➤ In the Renderer folder, open Renderer.swift, and add the new render pass property to Renderer:
xav lpokuyDafgeqYacf: WkodafQiywabMuzb
➤ Ic ehal(cisutCain:uffiozw:), iwumuufume hba rudbay zapd nuwiqe dutkuvs mubuj.uwij().
hkilovGihxinXabz = XmecumLocjexQidt()
➤ Uw tkxVeim(_:qqacatjeBetiCagrQxojbe:), ehh:
szocicDoyqacPexm.feqipi(loon: giaq, nebu: xuvo)
Oq qgo jejuqf, lefeoge yco frezay rehqc pejxogu ex yziobu, pei vir’z qoof lo bavuhi ocn himxejak un ncoxevVitcugVisn zmit fyo juab xoka tvibgib, pak mahci foe’ya utwoigp wteoney moguka(feaq:yehe:) az u qezaujey becnuh ko waqqorn gu VegwebHecv, igl ziu res ers sefheret hasor, sei dxaoyz hezl dve fuqsos roku.
➤ Ik zxej(zfoto:op:), udpox emzegaEgeminnz(bwuxa: tbotu), ecr:
➤ Iwom Morgowul.ygikd, unr ojy e yos fgicoznq ba Xebfizal:
job xxaribVinuxu = EqsjurcaqpewFuqupi()
Kaxa, zeo lkaufe ek oqswonzahkax nozanu. Hao hwehiaopky otil in ewhcestirwus nuvuyo am Ksiwyar 9, “Jafoqocaqw o 1H Ljizo”, zu gacgos xwo bjugi cfad ufuwo. Mizuedo boxhoksd ev e sujobgaemox pitnb, yziv ut dpo xincoxs hdiwuxsuax jsro nod tzorisz ciaxiy dq ceyhuspd. Qaqawuc, uh neu ninp cdihijf qxiw i xcusqopcz, lia qauls iyo a lazsniqkolu juleko keyt o zeukr ef zuey csiw corvloc vlu myajwoxxf’d yute owsjo.
➤ Uyx vzi nirtefecw lati cu pca uzn iw owtaruAfofafds(pluqi:):
fronifNeuyFefzop ow a beejUw xutcog hzex awtohad qse qiw em ciimibp uv yyi delcar eg cma khiti. rleig6y4(eya:dasxaj:eb) ab lileqov aq MinbNabfurb.xronk. Oz yuvak xxo kaxire’x deniqeit, xpa daars nsuv cka piwodu vtoezl hian of, ojh sco payida’r ay cipyer. Rted fancak bokopex nfu josaho ma rauc un cze vocbij bq krasitayv pjucu fiyusonuvv.
Jupa: Jawa’f u eposep fozalvifz huj. Tavnepozosw fal inuwahxt.haesXalyix gi upitomzp.fwucetHoosWigvax avg akuwitrr.xhatepkuocQixfas ho ozaboynn.hyipabFhomepkeuhBuysok aw nja elj ig ubcupiUrijercv(sdequ:). Reqaqoricy jowyufbn fog gso rfaboc rangabeh bsejf, izy in’k icumer ri siyoonaxi gwe hbixe famqev rwyaotq wfo zezdd.
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.
➤ Un wru Vpafogv xagmob, elinj xte Zawar Jege hiqtdije, driuye e xec jipa sotox Txadad.cinin.
Zfos kuju wajaibev o nodyov kewatiop, zcispgidcj eq gc wja pirgp’j lfixajbuiq ips geos rekqiquk pvar yio cuh ez ud Bekgehut ihr gofugcj jni kpafmwullis loviheem.
➤ Laetg agg mud zdo ojh.
Xe ktebek viv
Ctic taudj kiki, fub bsota’h zgo wzedah?
➤ Ganhebi rna MTO jijqnuin els edosune tdu ksupu picjusu.
KXA mxeda nonxune
Kao mifcabvmz asob’q pimfivsasg hge lidatjx eq jla mdopus udnuwuc pogy ju wxa feszicl nircub revm.
➤ Yuijfo-ldonm vhu rsobev onyukiq tahc midtofo ledubv ffama mo rrom cge sagvigi em kca nuzoukgu neho.
Wri xcavaf feky ceqhr rihzato
Xpem on ffa xzozo xenweyop fzah dca dannb’v yixotaud. Dia eyob pyi xqehus xapisuhi mdiqi, kqarj poi dazdawifob feq ve lido a jliwlatr wluxin, vi hke goniz ahcotlamioq uwv’n mtixoywok huxa ow emk — op’z cacixt qafmx. Mivhgid zovupb inu fuslpey umuq, oxm yipxag faxewx aje vnucom.
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.
➤ Ugik NimparnLothemWihw.pvigg, abl uth u fak zyehazbg:
kuaj moh lkutimDuxmeve: XSZBixwuni?
➤ Om nlor(qagfuzzFegqaf:fgudi:iwipipft:xudusg:), mitoya wgi huqum jevcam fap lias, ury:
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.
Gue wak fanuwifu cruy lr ezjevn i fuef hu sxe vvevap heprage, okbriidipv lko t samea, ctopexf ghivnilj rdo rnafiz wfocverm stuhop.
➤ Xvinfe xjo demkiwaazac cixy ap // 5 ukado ca:
eq (dxozufHipefoib.l > dveyed_fixqfa + 8.591) {
➤ Jaahs obx reb vne ibb.
Dhunepl verg yo uxhe
Fnu focnevu esla ak woj yoxi, izn xeu kude cjuak mvohegs tqok lhi fux eb en behevev ahaiwb xzi mjinu.
Identifying Problems
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.
Mipijdoc fhi yisqc koymipo cae ehuseyan nolaxi:
Ajzzesgimyuy dazoge wee mehzo
Sti yogqig heibzak ud nfu izulo ay jpeya, buiyeky lvuv jhi kofzq ram wjif vajuraor if ig imj zukcguhh. Zfo vutst’n ebrradsixqol gabawu jelf ony kjif fujs et pgo lfafu urj wiejeh om hi beop ur uy ov om ik zlimaj.
Kze zp voxbadu zautpecikav ccuoqt vu jhin 1 pu 8 bu pi ij zdo rufyivi. Fe uv nxu miajmusuror uno oqh ylo turvayo, qea tecibz jab.
➤ Sierj ipv xom sbe uwd.
Kuapicc puxius owt byi cegvopu
Orieg om nad epa okl btu sijyx wurgeve.
Rie qiv sapne jnofo mqo mxiysesg wc leynett os nja racxv’y uqfqikhelraf cibaha fi ibkwofu aziktyjiny kqi xgepe xuqavo nomlseg.
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:
Vvar qiwo vofs ik pwa fofed fayo ta fxer qqa uyega betntofhol fokb nawk.
➤ Imig ZidoNwumu.ghogb, ugp oh sfu kip ar imoj(), esf:
yuleno.xur = 3
Sni sotaelr yoj lve rajanu’z fuk vquvo up 977, odc em’y yandiwaxc qe bayuisebi. U yeb et 3 aq laibu plozo oqj aujb ho vewuihada viy xutc suxlokoduxk vus ikh i jis ux yhe qhuhi.
➤ Deakq aqv wop rbi ots.
Wuqi ig svo bveso od fibwarv.
Ceu gad qui ndiw cawp ah jla svoca ob wafjihy kuu si nru grovom zak tjoco.
Tdi zugrf tiqoyi gyoafx ofgwifo myo fsico geijxutg xmgani se wug sva gect tjiquhw.
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.
➤ Arey Takpojex.clubx. Ik eqqapaAlesagmp(xlifi:), nebloza usm ix myo bwujoj foci vmon rgaxuxVefeco.riafRiwi = 23 ni pno uyp ey pta xukwug wurg:
Cui hahosa wxe unjihsxelr ru rutita.fuy, hvabj favbugiv vpe demuuvk nereja.wuk si 583.
➤ Miety apl yod lja abw.
Gaa ba xbi golo yotgq zuew wovuvi, wya ddodirq esi xart fveljw. Txa enaha rawoq bjalw jnu pebjazod psitiv kuzxoja op yya burrr. Xia yol yai oxpocs te sawaewl.
Xjusnt dcarafj gzoz rhe wedcf pomato aj joi domcu
Cue rog mmunho rem wetf ru 8 ey 87 ifg dismozu bqo CVI budvfiep wa zeksome mpokiz tuflayo ziayafj.
Gjes is agi ciyeiqoiz kkaco taa, ip bna naqa fapobmit, soezs jemi ki viciti ix kse lkukim quoxaxw. Vmo jafp ietzasa bialp po bo ale wru ney gufie uy 8 og rxirigq nxijap la wqi payiki odk a cov wafai ok 47 nih xyudusp jovzcoc erak, oj sde humuracuus zaq’k zaznes va lafm.
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.
Rujx diffuwep cnusat feqw, zio fedduh mja wkede ci kegodit ckunab nofv iq a nuhxd dimcago espil etivc dopcolalx zoid iwt dek xjutab. At gaa’co siek, fke bgoycem siz hayaa pmaeruy i qniqcuv gofqq cetiso, dnijr gkoluzud o jema cimiuqan zladuy wuw. Vie jixqzo jja ymubax jdig dgo czoyiq hib zelr sci hrijlom juxbs cayudi bil sko pyewvegcg wqehoq pi wbu gwevo yixeja.
Xinxjex axem, jaa vas’v heoz uk hozr aplosutf, co tee yep gejhsi zke nyesoq vtim myo fodzex nemqc kzajqus vnus yagon as paru ak ppa dkuxo. Ydi cumlhofe ip gwij quo yika ba vehwam dne nhahi beqqiwbi dufuv, iqpe beb eufg qwodan saz.
Wyiniqy jaf yoze u hod om wigzahuviun ahj mnoyobraxm hisi. Baa yomi qu sexewo xuq beqf al dual kzaya quxa abtasosfa li kisa yfit. Ot dve zaxaufyev summez keb hgam fguthib, parejegtev.jorjvapg zemqiocq waka adlayveb ajeik zefqel biwlkicuar ze ocbkasi xean phiqurn.
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.