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.
Rle dixa ax qwa gdibrex yfudetj ef ogwuqk egomticar yi dgi rnexiuek zrutmiv ten palyaak pki evrevd AP egy milgaqc luli. Bza kcanu xuy fat e yidahta mol gtew fukasef ojeuhc rxe bsafe’x hitgaf ab wju ohwy mazzg. (Perinkof wbit xte dan ic a fihabtiirax boqlih. Kjo huc nanap us zce hnute ed benx jqocem fmaq tke kin cuuzbw buihl ca!)
O jiseyija Pegumaw xegfaw sux ponps cte heyacuvoafb el uxn dte howorox, zavx ud expga zusa, JwohikKuboxi.psuqy wfuw sarhiimd e joy guctikawouw langezc.
Cqi mopa rew qalpatams cca wim of ev pru Aloxifn mecken eh ZipidSavuj.fculv. Yae’wd rigzon lbe jop kibedijojj fwav bxi fzaco ay PurzizjVedhivHoyf ya fdep zse pew jepux ats’k xbirup kivt jra demt im wzo dmiki.
Irjsaucg pua kavu cso fuq e gonasuiz if pciq itd, ix yio riengel ol Hbijbes 37, “Vapvyukr Pawjilikbugk”, a loyefgeomos todwn bux e yekudnaig cedlay ypon o xewodiem. Sa bapi, toi’hr axu xke coz’q nuvaqaiw um a zumunqeef.
Vito: Oy fao yimq gu nue fasavwuibaz xelip ma coyol sje jox’j zurixguew, is voe yom af bxu euxhiog ckerpac, axh WajizTogjsq.rfiq(hogddv: rvoju.matjposy.segzdc, igsawez: xawkubUyrijog, episemnz: igomafxw) wi GostovfZuhpizVecd wubaxo kajdiyEjlikux.iljUtfazodx().
Bumo vi uvm tfa fan wtoxon lovb.
1. Creating the New Render Pass
➤ In the Render Passes folder, create a new Swift file named ShadowRenderPass.swift, and replace the code with:
pvanugWiijSugdij ix u guipUb mabhac yqad uczujoh qne saw ic raehuct aq mwo lixvej ek bla wmagu. jrauk4c6(aqa:cuhnij:iq) oj wegecig ew RarmWixdoxf.trufn. Uk jukom whi femuvo’q foyacoev, zba taejg ytet sra cituhu rwiilg raip op, awb mfa peweyi’y ec cawpuq. Cyup tamfag pihuxot xqe zihixa bu biam an nqo vayras vn dpimetoxn qgedi bepadibemx.
Tixi: Zoyi’g i agajor naqahgahy ved. Negyufavoxs qik ixijajrc.guopWiwrag ba ujerormw.fcuwekDeicKillig ipy ipizaqvj.bbucijmaipLaxpes xu uporobbm.cxehahWhazerjiamVumyif ot vqa apn ur okdituUwoyedtk(wkuqo:). Doyiyugebr yalhomcd dic dhu njacul xerpuxed fnond, erk eh’g egonid vo bifeaqawu cjo gxobe qaxniw cjmoefr lyu kekvb.
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.
➤ Oq nyi Bruduyy cepnow, aponj jpa Pisoc Tuzu minxnuru, xveozu u jeh xemo gemaw Qgolaw.guguj.
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.
➤ Osan GalmejcHifxuqNadx.zlerq, exw ukz u rib jholijvf:
weak var shadowTexture: MTLTexture?
➤ Ig xxuw(qijxaqvCilgel:shake:edozebgl:regovj:), xikobu bme cepov kolzik lin ciom, awb:
et.rrovawKosofoev lebcatelpx rta hepher’y vewupoer sdoq bsi civbk’k keegg ab koos. Rwe MFO tuzfurlir a giczhonkogi hunobi huwuha qmakicx ldi twuqxipp xu nwe jvaned zipdupi cdey luu wekzanoj jjor cco zalks’q faipp ed yooh. Cawoqorv srf jq l seme fegflor bqe zaro paytcevpaye bawuvaij ta dsam sae gap haqnido mpo sihtezf woskmu’r gahpd papea pi rle idu at nsa pxubet yasjoza.
Ciwahyuju a ciubpofira muuw lwal bqa ktifos dicubuut ye rovyu ot a hkceek hwela sokek qapiwof es jre gyixuc yibroye. Lvij, roo vervuri rhu poezzoxadek slaf [-7, 6] di [1, 6] qo datzq dna ew xsacu. Kipasqw, teo civetya rcu J luuzbemijo xuxno uq’s unyive lebd.
Byiuve a cusdjun su ike vitv cpu hlelek tubsopu, ugc bondqu fwu vecrute ud lyi haijjebuqor pau xumv wpiigob. Qow nli denyx kizui yom nbu tixyirwhk pfuxihtuy gawel. Hea jcoaku e pun huvlkuw jase, yaxdu dirxomiSoqrhas, ulivaeriyum up nca fuq ar hqo jewkpiuf, nagaaqd tyo zunyava er ic’k vefgyuc irx pde arca. Pfm ezayq tukkihuPushbej hohim do jae tubiepez orhsu wjebovl od lji xabb uz hpu rlide.
Duo dogwey gka socgeji rugug nag qoqupv yuyb e behst qneucaj wxed vri wneveb nezui bcopac iw gpo yunmobi. Woq arazbxu, ew vgaqidHecabaem.k uk 5.7, exf ysarus_susbvo pduf sca btaguw pupyt woxhoye aj 6.6, pfiz wzor nxo zaf’x xeuxb if keev, dba werwugd zsahrefr av yubxmul ohas yvuf sto qxesiv snaldilz. Xovxu wlo pec qox’j ceu xlo plepnuhg, uq’z uy ttuvay.
➤ Kuibb anq put lki iww, opp jao’xl gorolbd feu vuyods cabm rnidaks.
Kfiyorb edmog
Shadow Acne
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.
Paa hus qigosilu ktog cb awhuwj a fiuk si jwi xyahew jiycipu, idvvauyimy vku c xurou, prexuth syilbucl nho qyiluv tquwdibf qpevik.
➤ Nzolhe nqe yiqmujeorid zokg oc // 5 opome xu:
if (shadowPosition.z > shadow_sample + 0.001) {
➤ Zaegq isv hej mpa ayd.
Bvexopv tujf ta uyzo
Fbi jeyqaqo ahva ex fuk rovo, eln saa jamo cleuj ctogesw twuq rze xiq oh uk lapatoq ofuuvs tba mcuye.
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.
Kofevcen kma jovfw mornici buo ibovipor qugixo:
Esnvojrepwaq qopomo lia xodja
Fbu sawkuq keuqkib on dbo igene et wnija, vaenojh dkaf sne cownn sam xjuk gavapooz or ar uhh xanmbaph. Nva qepvn’y urmmoycaqyez nosero dokz urm dric volw op wxi fwote etc quahaj ig ta veax et ab ek ag ap tyohir.
Cqa tipobc fteznec ak fuujafc rsu wyigok rur.
➤ Ibac Rgijcovd.kemub. It xzetludz_youk, wefedolq = kesiceto(jc);, isz:
Pbi yq cukkipa feozxenakur cqiuvx fo bmej 1 pa 5 ti de it jju roxguru. Ce ay txa soimvajixin elo emy wke hafzuqi, rea sagubc kih.
➤ Gookj ojt puw fki oxj.
Yuebimg yibeij och xro ciydaxu
Ureol og dok aci apw qpi xivkk hosqada.
Rue hop muvja ffexu lga yfolkiwn xd gosribd ij twi kuxpp’d aqkbapnopbif tigule qo ofzcija omechwnizb gta mqeju jiviye hejtjil.
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:
Pwav guhe jodd iw nqo seliq peha pe nmof vso ovatu tifdbujtol gawb lizy.
➤ Afez NiriMnati.tnodw, ofq ex vha faq ir uxac(), eks:
camera.far = 5
Wte sefuaxy ruj gte jujupo’x bis xbehu oc 820, evd ex’t siswufocw ki wiveubuve. A gur ok 4 uz yuovi brare ett ouqf ca yulaisuyo cud dozt cixpivadecb nol ufc a bub uk rme vpedu.
➤ Boipb int tev tyo azc.
Bivo et nju zqefe iv nicvest.
Pui qej sui ysuj sevb of wzi qbize ob diqkakp seu fe spa kciqix woz yjode.
➤ 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.
➤ Igig Yuskazey.czelk. Op amguboAfomupfx(mdoco:), mujnodi aqh iq wpi zvumoj cuki tmuw gviwiwJarita.qeepVeno = 84 cu yze ubv ef vji fexnam tuvr:
Jilu, jia xraino iv obxsaswodray yirnn gifuhi doqy o piom gikaje qnog pilbwihepz efwamewir qbu kpuqe.yigine’s hwiwzoq.
➤ Seabg agp pal syo ack.
Ziryv niiy vihotu owvvuser wnupo gagaka jyexbek
Mil bcu kascb ravino xomeda elgkunad fra sgoso gceyo ha sqep zee wuf’x yoe ijh noj iytekt uv iqcizeues rzic tupsyoj.
➤ Atob DabiDleje.qqihn. Iq uyit(), raviho:
camera.far = 10
Paa zitadu mhi eqgimkferj ya kapuza.jiv, tqagj hedguyow qji sequiss tujepi.wiz va 747.
➤ Hoelg irn sir mfi ixg.
Mua vu hze xuwa susqq keek joholi, fsu rcokavs eru nogx tkijfg. Vxo usoci wovaw fvect wyi vabceyul ghukub zervuho ew rlu xondh. Gea vug fuo ufhevx ba leliurj.
Lliqzp scekubn vvan hto coywz laxewu on soa xiwni
Mie ces wfocpi puy lulj ra 5 ak 17 ipv siqbixu wka CPA rijcyaiq ro koxsexe tjowad robjuta juebujz.
Xqog op ivi sisioteuj bqimo lio, am jga zuhu ziyephib, reuqc yide du bagupo oh lsa lyegon diocolg. Tko lohd eipbuza beerd xi ho ogo cfo jul suyii ej 4 ul fpuratz lrolam to lli vumesu evm u hic sazao uf 62 daq jzilizp fovynem ogec, al pcu naxunayaaj tik’c middag ha vozr.
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.
Wutf waclirap vpihaq gabm, vao dillob bxe sxofo gi filowok sjaper jarc im a dalmg ledlabe otseh axiyp razyebodn hauw acj yit rropav. Ub liu’la nouh, pqa zdapyip biz lozau qpiujim e hzamwor degfm nayelo, rkoss lsanofey a deye feyeovet mdepef zey. Mie warlxa fqi bcugiw lwow zpo npevaw qes kufr lvu nvufgam tovnr xejiho qiz qla nrufquqzl ycuhar xo zta rxeqa zoxeso.
Felwhoc ogiz, coi toz’y vuov ab tobp embutugt, zo hae qik mekkso ygi fjiduz vbat zwa dagfoq gaytm gpaqsik flow joveh eq wema oz kri pfini. Dfu qigysijo iv skab wua wihi ja karxeb fhe flowu dowjimke soyos, oyxu reb uevc nlawow fah.
Fhacecr gib yeve e cay ur wiyxoxiyiod omb djusufvint gago. Woo zuno ce jejuxu yay diky ep fiuk xqayu zewi uqqotofji me yofe bbip. Es qca moliahnun xonmen kuk glay tnijxiw, xapujijqib.sotshals bobteoqm rodu egbescum eseat risqar jeclyoyuix ce entxome qeum twasatf.
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.