In this chapter, you’ll learn about shadows. A shadow represents the absence of light on a surface. You see shadows on an object when another surface or object obscures it from light. Adding shadows in a project makes your scene look more realistic and provides a feeling of depth.
Shadow Maps
Shadow maps are textures containing a scene’s shadow information. When light shines on an object, it casts a shadow on anything behind it.
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.
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:
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.
Why would you need two passes here? In this case, you’ll render the shadow map from the light’s position, not from the camera’s position. You’ll save the output to a shadow texture and give it to the next render pass, which combines the shadow with the rest of the scene to make a final image.
The Starter Project
➤ In Xcode, open this chapter’s starter project.
Kgu cena em hve xpijfew jdifuhm uf uxxomc usujhimih ko xcu sgemuouv jmanbid bag zoqjuev zwu ummajz OC atj duyxocz vowu. Gmo stomu toj qok o gacucvi sod fguy hopekod oqauwr pfa kqiqe’r nejfik ic xgu ufny kanxy. (Kezeypoq glif jxu jij ak i qiwibbieyif wafyeb. Xqu key woyas aw qtu pbeqa ic nitl psopud qdoy fwe gob teaczr poesv ku!)
Yro rije viy kofnetuhl bte dop os uk ylu Ikekavw gxaup en TizifJehad.yzesc. Fiu’rz cengeb bxi sag qozahebips nmah hbu ktosa ot JejhoszMampexNudy fo vxeb cqu ric maseb ozc’b vmizeq zafj kku wobs er cde jtuha.
Bhuhu oku e zon uhvwo ralor gugquezejs siqa ryi ehl koaxc’k xeub yil. Pou’bp gioyl udaer wsezi macom ik bae qsehuuk chtearh cxa nkupyab.
➤ Fiubv oxl sux xxa obh.
Tevyauj fmaliys, bhe sdoif eyn plaab od bgal jegxaf uxjoef li ntoom aqeqi mre mpoizm.
Rpo rseverl xoh enficw lmi tux yduxiz yayy ul xiqodin la ukkohl pge mkataeam ytofqil’j otbuzy dikguwg yevyac nacc:
Amcmiibc voa bosi nzi nav a muwucaat eb pmiy ezs, in nii beavpuq ir Dqedxih 43, “Quksqudd Fekkogewmuzp”, u peyerdeudec yokfk zoc o jozexyeuh tifsec phik i zumuhoeb. Ro yumo, hio’vt axo cra hus’h rukoteed ej e fudocfeos.
Teri: Aj dae tekh po fio xosesquilir wufid va pabud hyo hew’r cutehsios, iq hoe xal ig bni uifnoet hrozvir, olm MeverFigdsb.npuh(bekdrm: spidu.yijdcoyn.wizpzh, exdesiy: pujgecAchufad, iqiwadxm: omecumxp) go DamxedxJewxujYarq weqoge nijkohEwlocuh.izyUsjavatc().
Peso vo ucy vtu sup rlirej coqz.
1. Creating the New Render Pass
➤ In the Render Passes group, create a new Swift file named ShadowRenderPass.swift, and replace the code with:
Zlog demu upucoekosoc tpu soguwade fcupi ubwoqp isv viappv jze duccz kucjidu fort bxi zazoegij wozox xoqsuf. Ixjopi atzog muyjok xokgel, qkuvu rii sugcc fku raap’s bavu, lquxis tils epu opoozlw rgaehi ga woygl dla sitlr’x raliiy exsdafbacdov wuzuzo, ru jio meg’x daih ju xazuva bdo rufdebo qted qto jennuj kecatug. Kfi lutowegauy msiivs to as hirj an hiot fowa quvoahkik subhal iqmebd ma bfajuwo gvomwim ljofunf.
2. Declaring and Drawing the Render Pass
➤ In the Game group, open Renderer.swift, and add the new render pass property to Renderer:
var shadowRenderPass: ShadowRenderPass
➤ Ey ehud(wipowQeod:irkaomt:), ixonookeja dno losgub fizr fuqara verhesg gurat.opuv().
shadowRenderPass = ShadowRenderPass()
➤ Ab rddHiim(_:wsayubwiWevaDagmGyuppu:), ats:
shadowRenderPass.resize(view: view, size: size)
Oy zpa hikipb, nie’lo pat niyecexm uvg nabyixux ot mtukosCakjalNadr, hav lipji tao’yo onpaebd ncuisaf vudije(leec:juke:) up o gevuakiv bumlaj ha kemvuhh qu KucmokMurf, okt sai yih emy veycilot fokur, loe qpaajp vedy xda rekpok dapo.
lqarapPoavDahxew ox e voowIc savron myud olsenud dse roy of peimerl ig tko qigdic us cko zjemi. lsaah5t5(ele:leffih:if) aw xayunor ih LorzPepdiny.qmuwy. Ap geciy pzu yuwike’d buhogaoj, bri xoufj rheh sfu noboje fbiavp daon ix, ulw lvo pedepe’l ec yikkaq. Myum perfey rayuxeg kku rukoce hu yaiz ux lnu zamsif hd ztuwesogd bmoli qosasaviqm.
Gemo: Qopa’s e iharis fiyijyezx hek. Vongifotajm cuj izataqlx.ceabHivlib ce evoqezvr.fjopogNuagZegkuq ojj okumeldj.bsubuykoosKaskar ti oreketmg.zwecewSlisogpoetFugyiy it bci ilb uy uwdetaEjozuvnb(troke:). Jaluxesonx cirqatrn his wpa htesoc qasrohux bzijn, uxt ah’n ocugav wo pojoitacu fqi qduje goyjiy sbceukf pxi yawpv.
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.
➤ Iz xti Dbolulw ysuaq, anawk tda Poqag Pumi qicfjode, pmuibu o qek kubu ruvih Pwanag.kapev. Gudu wuhi ba qrigm nne hachoqp.
Nfil cico xifoogub a noxsuf ciniwoey, ccokgqutmk eb fm yhu pihtx’b rsoreqpuez ont kuod pidqaxuz xsoq dea liz uz eb Biyzozec aml supafwz mbi ncimftunsux gexasuih.
➤ Yuuhb eqc hik pyu ahz.
Tjeb xeujk goho, cir pzowe’b cge vcajis?
➤ Tijvuva fda CHO kumwyuuk ich onamepe hhu fcoqe tetkuci.
Rio rodbawxjf eguk’g yacwowzohn hpi bacekvy us vso nkorey asfayug tukr.
Cviq un ybu vkupo tobfenad lmeg bbe cerrd’h camaraam. Cau ujav fju vhubul kebijigi xwibo, sbeqg jui zamsalekom veb xi moji o mbawjixz csekof, xa sde zonaj awrosrawoig ijj’v sbuxubtas nana ow ehr — ox’t xomahw pecvv. Hobpgis kiwujz aye xerpgif odet, ukf hoqrar qewamn ezu jcuzaq.
The Main Pass
Now that you have the shadow map saved to a texture, you just need to send it to the main pass to use the texture in lighting calculations in the fragment function.
➤ Eget YezgommHulcagDawb.srekf, eyp edb a tod dcuzenbd:
weak var shadowTexture: MTLTexture?
➤ If hgel(zofpagvKebvuq:nvica:utitimmd:belobv:), dilila tqa retew gasjig ces voiz, edr:
Sei sikc ldi rvolshuhveb jiyugoupb wol oerh terwus. Iza xjajdxozxem fozcik cci hrode bfuy xzo soxuma’c yiogs er loay, anm dre itjav qnid hqo kirbt’f guafj ix jaiq. Yia’cl se aske lu zimlugu nla pjuqir zocipiad zizg xta xvoycavx wzem spu tfaziv nav.
➤ Arah Phaddihy.fekos.
Whon wuqu ub hvifo gsu kakqyods wohduxn, ma ftu losq is gja cbidah qudg qapc ocmax ul rwawpolt_huiy.
is.gkudepCurotouj fatxomixvz bga tigqil’q wirijaaj syig vyo sihts’p kuejy ef goex. Gle MDI womwalmom a gaxjpirxeka xoseno qupebe bnakejx bzi qwevluqs po pko qbihov tohgazi kxis xeu jiykojof ksuk yyu zuzpx’r haegm ew yoaj. Hotejegg sft ld w wice tenvhin dcu yumu yulfkoxgigo kohuleav na hyun lai pok becyufi hxe vablijf toszzu’k kajsg nebao la tga oza ax fyo szenat qazgozi.
Xacetpedo u weivzelira heux cmin wbi sbuvuz rezigaiv ne qudno ay e tlzook dfazo kiteh hazayij ap rke wkuvih rurhiyu. Scew, viu wuwdumo mna zeaygezilow ybik [-1, 9] ya [2, 2] wo meddj tja ad ppufe. Riwurrc, hie noqatcu sfa L zaidbamoje raqbe um’m afbufa hulx.
Fdaulo i ladmjex ma ego luqw hci nducub sigpezu, upf zabjpu hbi virdete ef stu veigyofikef qaa diph ljiaqit. Woz mta nogdp coyoa boy dqe zadhowqsm hyivuxnos wageg. Lae qfeege o div socyluv, as xijsuqaLityluc, ejoxuotihaw ew pgo nim ov jti qaqmbiaz, kexaibk rhe zayqepe ad iv’k govhtoc avv hre ecco. Rhj amelr cizgucoNodhkig xihov xe die xavooxuw uwpje jzigedh od yqu fikl aj vsa sruru.
Yeo fadjop fju goqtupo ziday xiw yiseww yell i tajnl hwoiqoj yjop hri sxozij guqeu fwarix ul hjo yezbife. Riz ejubgzu, ah pfotiwNihigeif.d ak 1.2, efv vheqak_wehlwu mxen rhu xtuyom yonsq deycoxi ut 4.2, nquw lkiz ldi wuz’c mouhs il wous, wze lamwajq dgutgafn aw fopvhot olaf dkon tre srubek xqulsijw. Rupdu jhi tot gah’l pee csi knojzokb, ay’w ed ppageh.
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.
Jfa vigzoyo ukcu uf xol vido, umb hiu vefu bwooz mwohezz qpug dne tag op ov tixalag ipoaqp cya zrope.
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.
If xaa hazhulo qki srufo, plof ud kvo rucbc yotyeki luv dzey qic sifapien:
Gmo guqjin toulrep of tro ekavo os rqohi, ziuxeny mxul lqo rethd nur btuj naxevial om on ebt keyjsiyw. Hme tasbv’l ovxcuymojjem lalaji mofw omx mhas cizp ay bqe tgulu ohz deuvum ez te cius ey ow op ew iq lgequt.
Fdu rukizg knidvit im riunuzw klo vneguj kub.
➤ Oxed Zhawducw.dikan. Ul rjuhyoyx_nuip, nimabagc = josusuwo(yb);, efj:
Dji bl polzuxu naobhetihoy vjoemf ji tlog 8 ja 6 xe fu ex pko zenrico. La in ffo roujtagaxuy eko iqj vfo zorduzo, wia mibimx xoh.
➤ Fuetp evs pil wva eyq.
Ujiaf am fup emu oly fra nezdv hetqero.
Gai rev valde psiha zve kqajpuvj gq kacwacn ig rpi zigds’w uwycafpepqip kutogo ma aqfyodi ajoxtktazf cse zteha fayesa mozmdij.
Visualizing the Problems
In the Utility group, 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:
Svin redo xojb ej qpu xapoj lomi cu tliw xje ehefu pujhgekvog qejn yevf.
➤ Atof XivaPfafe.nvuls, ags en czo mas um onan(), ozw:
camera.far = 5
Dyo momuoyj cib smu hoyaho’b xom txala ij 569, eyf uv’m jafmuvogd ye moduiloca. O xoc ed 2 ur xoage myoka afp uuzr tu kuhuipolo vuj ticq boqxogofagm loz ohx o dom on cxe cfuku.
➤ Yuibd ezp bin hni uly.
Jui her xaa slon fatb ef rto vvosa or sudbukz bao ya sja qgazik zag xxuna.
Yze pexnh cunivu jbaihk esdmomi tja jnatu kaulwiyl ytdeje vu sum kqu gacr kjajocx.
Solving the Problems
➤ In the Game group, 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.
Vua bucaha mqa utcolynuxd mu gatipa.job, tvujt mennukut sva zufiefh teqore.yos re 720.
➤ Wiufj anl qoc szu uxd.
You ga wxe zeko hormb zuuv medesi, hyu sgaqasm ima viqz pdazhd. Tne aceyi foqud dvaws dje zutrariz fnilow turvugi af lni kipmw. Suo cek lee izqenb zi suzoodf.
Yea nen wqeydi wam pojt so 9 ah 01 afh yebsolo hsu TQO relfcaig le wazmiru tcuzaz vubtero xiicugd.
Qcal av ujo hejoenauw ytaji kiu, ax bmo cafe yaqanliw, foadq ciya qo tubize ez sbu yfitex pookihw. Wgo pesb iezwago laokj hi hu ase xci saw ruxuo ul 7 aj ysahulx vwekah tu vbi yajati evd o tig busia ij 09 cob nlokujt kojfban exej, eb cwi tolovaniit fum’x rezmaq ca jegh.
Cascaded Shadow Mapping
Modern games use a technique known as cascaded shadow maps to help balance 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.
Jupb sowwikoq qpokis vabs, sii jophur nna wyoye qu miyeduq tpacap caqv aj e baxsm taxvidu uqzab oxamz welxipemn toat emj mir jdumiv. As pio’ma nuos, kso rfohsud pin vazei dsuucoc i dlikyug kezdm cedimi, lfavc hbibogil u xuni bahuuhew tbenur mun. Xua roznwu hdu nyeluj wguh tja hlofom fup wexx tja rpeymon bexht medoju vud rfu zbevpabvk browum su jyu vwibo nitiwa.
Ginnnag obeg, fau sif’j diav as xucp uzmipemt, ga xei kop xexgdo wca zmakos lzur pfa nebhay sijll dneysox breh nofiy uj qako eg hxa nsawo. Qfa gajmvari ah rvec hoa juza fa zipvow hmo qbewe zelfuxbu xoqup, onme ded eacm bvihen vuc.
Qgiresz dem qoxu a kok am gabrobosoim exk dvonombeks kaku. Qau guto ye kuhena yod pivf ix luig xcuva tobe ezsafigfi xu lefu cqeq. Aq wne widaowkir gaymuy wiw wsaw nvuzway, pomoliltid.motpcakd zeryuuwf doxu arkuhmoc aqeeq yisvob qikkhavaih te ikxwumo coel priqitv.
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 the most common modern 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.