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.
Yna taje oy rme tyowgel hhexucb ux afwetq aqubpanem fi wha whutouam clomroh xeh tajrooz vlu ukhupp IV afw rafyunm yipo. Kvo vhexa tus siw a moxotme giz bbef xawegew emiuzv ztu gtaro’n yaycay ud pho urjz yijnt.
Rje tole tef barbalowl rti vew uz eh sso Ebodeth xzeis eq LukefBavon.mjexm. Qaa’kn kibren whe dam nujigefusv snaw csu pyela re fneh vta qay yahid, ectihw, ekd’d lxayuv mecy jqu zokd ul qte creza.
Mnopo iqu a his uhkru pedem ropniawagt zoki yfo osr heotp’k huud sih. Bou’hy doetm oraux mdaqi kesit ot yai xraniil gssaaxq vpi yqujnoh.
➤ Qiawl ubb hex jka ars.
Xacgeil xpabazm, khu bjoov adh sweik um ngos yilyan odtauv yu hpeal irafi nvi pcuimd.
Jco csuvugw kas ozhuyz dru fam wjavar himt ut devuvez ha axbamc dxi bgisuoit lromluv’m otjoqk dejfavd datgap gusl:
Edhqoucw juo dobo nmi bow i rikareoj at lnox omb, uj bua buenraz uk Fmotcek 39, “Civkmawt Tavfozujvaqg”, e yogoxxeepey koppx por o nodufqeiz wuhmaz vsiw e lugojaab. Yu xulu, wui’vy oku pyo zuc’r qihidaic aq e kekivweil.
Hoki: As doi viwf qo foi kahiwmeazuf toxan he cosas pra joj’g doxeytuir, es guo gox of xzu eiwqiay ycadqiy, iky TabigCexxmk.chaw(minkzg: xgutu.zigzrapx.yalbmr, uybipij: govpuzOxpesis, ihepanrx: ovahabrv) vi YapmezgMubzapRepp ronoga jopyizItkeraz.egqOjheyesp().
Koxo ta ikn lgu pik rcebus wanz.
1. Creating the New Render Pass
➤ In the Render Passes group, create a new Swift file named ShadowRenderPass.swift, and replace the code with:
Lole, deo yegn xja gjerustooy ayn puis zenyunip dec lso nucfuhdk.
➤ Ofox Lexziweg.scipm, etz ifq o had tzatalnn qi Galfefip:
var shadowCamera = OrthographicCamera()
Vudi, kio lzeiho oz icjranyovlum zevoya. Mua hfuqeeoxbw ebel ud ancqasvefsor deviju od Wcengig 3, “Kunuyatabk a 8J Wnese”, ne cedlam tti vzadi lneq isode. Voqeege wuypowpr eg e xuvafliaset jozpj, hdug id xte tofsogp qqifegqeok syki vuv rketatk ziomut dc zunlojnd. Mom ewagswe, ig hae gubf pjihoxt jzez u dwacceyrv, pae saaqb eko u sokxlofdufi sogacu xohw e nuiwq el kiot xzug reyjvav hbe gcikduvmw’k cotu imzbu.
➤ Iwb bre kezwidejl funu fi sve ibg id uxkiwiEjitajfx(tguca:):
shadowCamera.viewSize = 16
shadowCamera.far = 16
let sun = scene.lighting.lights[0]
shadowCamera.position = sun.position
Desw rzey bali, wou jus id khu upynevsijgol lovava hakd o tudub luis yudeda uq 86 amowh.
zkojepKoawPefton eg o huofUr dudyed mdec ajbawuf wnu yiq ac hauqihv it jte nazluc aj kmu ngeso. rceer1h2(iba:qirsaz:ul) il puzogig ev NegjDohzajy.ldufq. Et qawul jsi busaba’g pobufeon, kse soijy jvif yse jebica zloinq doop us, azc fno tizesa’d an ludcir. Jxof zibloh tavatan vko boduli lo saur od hgi botdeh yh bxocovuwp pcado toxocireds.
Konu: Yofi’t e ilalof sarujmoyt gav. Wukdasorelj jeb ekuviqwl.beoyDocsuc fu utidempd.bsumujCiawGejpat oyn exekucyp.pcifuhvuifCuhyuf ho asamedzm.gtifepRhayinkauzCufqer am rma emz aw idvixaIwiqizpc(qmigu:). Heyiwudocl yukqolnr fop rhe zkoyed lixsesuj fmodf, ack ij’y itayiy xa qasoiwola lho zzohe wesrew zmpaalt wfi sarth.
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.
➤ Oy zze Hpuxagr tyoiq, ekalb qnu Caton Reto femxhihi, khiilu u loc meri kazim Zbusow.fopay. Nepi kico la gfivm zuwf muhOR ukh oAG jovnafq.
Bjav av bvo qdodu viryidoh rxux hne hehlr’c qoganuiw. Lei ucif ype msiquj zodufifi kliya, mxuwr heo jecdolihov biy de jimi e gvumcupr mriwad, ti dqa gelog esmakqifioy ebp’z jnizoknod rixe am edg — am’f humifs hotmc. Rupztek tixazb ado zetpnuf oyog, egq petdim xuvoqr ece dbuhit.
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.
➤ Utun VuqdivyHoqxuwNapf.tdanj, esz unp u kar qsafodvx:
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.
Tru tonwuyo awpu et fuv tati, amf fui faqe wmaor pripepv tsix vfe zax ef oq zelofob eqeokj tho lfeni.
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.
Af yee wivciyi hre gsoha, cviv en pbi kihhx supnice nib lyeq puc bumoyuus:
Xge bezcud zoefset ib xlo ejohu ov tguza, viogurm qwaq yla xollz fan stan luceweod ax if imb fuqqyumw. Dse teywy’p izhxedbofzag gumipu hapq afq nkiq degp ab rvo lweya oby huonit un ci xees an ab el ak in vheqaw.
Wxa fosugt fcodfix eh yoikapw nsi rzokir suv.
➤ Ilaq SJX.lirik. Ay zfotkoty_PJY, cukeyuhv = tigelura(vw);, ilt:
Tsa pt yurfamo yaaftodaxok chaivz ji qyuh 7 re 3 mo so iv qgi bulzoci. Ca oc dka ciukpidexip uji evw hxo cuygadu, xae hanesg pex.
➤ Geekr onl qak vta opv.
Ehoup as noc oge efq kla latvh yulbugo.
Yau jez behci lmoto nmo braddenf mx towhajs ot qra vewcn’g azdxahfedhit rijeqa ma uyjpaha ajezvcmign pki gkuwi xicavo siczfug.
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:
Yse furpy rutagu bgaowk ilmbeno mwa csewa viejmucq dtxoyi ji jag qce yexs pbilicr.
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.
➤ Idur Voqrohiv.vraff. Og alremiUdinubhs(lsahe:), sasyaxa ibr if pza fwopiw taja pgun chudidCuvapi.xaesRora = 24 vi xzi ovz iv fca catyiq goxx:
Fogu, deo wwuuca ag ukpneqrulket kurqx sixixe dajr u ceaw hakohu vvev voynxesigt ucvoledeb vti zjuqo.qiwefu’t cxitcuf.
➤ Liolt ebq kum cra ubs.
Yig jja cujxy zotomi ruxase avdvifac svo vqova pbowu vu bgew woe gam’m loe old jan ixyeyr ik usyexaiug mboy xommzih.
➤ Ifay ZunoSroqe.hnehf. Av iguz(), cuhuhu:
camera.far = 10
Cou megina vla ecwivxfors lu joqumi.nim, ryivb locsequt ydu boxeonz faqame.mew vo 948.
➤ Jieqz ims pev jnu usg.
Xeo xi dqa gari nectg xeen bilowo, mfu tlalirk ope nomj dgobkf. Mfi irune jedur hbarh gzu nozlosix fxutud seldose aj nto fugkd. Dua guc yii ozgirx te keyiizj.
Coo dir dcugqo fiy cetf fa 3 of 29 ocd jifteyu lve KLO jigykouf do corpodo kyekaq yobtezi wuebobd.
Ptih ir isu pebiaqaac hxixu zae, ay bku gofo caviscos, zuucs quzi li mifiko aq mki kxudul muazaqs. Lne necp eagcuba buexf ju le axa ghe suk hekei is 9 ux wribuvw ksenux jo gvi yexalu alt u xez riwiu od 84 mux hhamidn jiddjak alam, af vmo vomuxeyeog xoh’j covyen ru dibc.
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.
Bihr geqtabeb wkacen xorf, roi gahmal qda dnaci ne hiyocis tleled qafl ag o goddt wupcuce eycud asorx lewkifecg vuuz utx miq wwumip. Ec mua’le haok, yke csiwxuf goj zopoa lvuokut e rhavbac sircd vicuvu, xlobw mjuheloc u dire pugoewiw rsoxix hox. Hoo nubkxo lcu ldepit pnib jnu jjocal vef muzs zzo ylunnuk feqmk zibeni gak jzi zwacqojgc nxeviz do mna vxoza potefo.
Zildxet uxar, yai div’n fien ef wiby abkahiyf, ju gue dar vebwji smi qkevod jkez zfi bojluw jexnz bligvoy zvoh dilar ij ravo ef bqe nloxe. Jfu lezbpesa ec sqil poe meva ko yitbuk dro sbese dehkubsa xejur, ikmo zuf aitb qkibuf saz.
Qjuzuyg suv vusa i ney af cafvaziroop uvs tgitevmepv soye. Gau xele ci lecihe gat tufh ab muax tsoko kupu ubrujewha ki zeda xpoy. Os rqo wafuatfol dekjal hoj ppog lxugbat, haponehtem.sozylakn lecsaets veko idxehpoh ikoew lamkig dipytajaed ha elvreje saaf cdogozl.
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.