Shadows and lighting are important topics in Computer Graphics. In Chapter 13, “Shadows”, you learned how to render basic shadows in two passes: one to render from the light source location to get a shadow map of the scene, and one to render from the camera location to incorporate the shadow map into the rendered scene.
Rasterization does not excel at rendering shadows and light because there’s no geometry that a vertex shader could precisely process. So now you’ll learn how to do it differently.
Time to conjure up your raymarching skills from the previous chapter, and use them to create shadows.
By the end of this chapter, you’ll be able to create various shadow types using raymarching in compute shaders:
Hard shadows.
Soft shadows.
Ambient Occlusion.
Hard Shadows
When creating shadows in a rasterized render pass, you create a shadow map, which requires you to bake the shadows.
With raymarching, you make use of signed distance fields (SDF). An SDF is a real-time tool that provides you with the precise distance to a boundary. This makes calculating shadows easy as they come for “free”, meaning that all of the information you need to compute shadows already exists and is available because of the SDF.
The principle is common to both rendering methods: If there’s an occluder between the light source and the object, the object is in the shadow. Otherwise, it’s lit.
Great! Time to put that wisdom down in code.
The Starter App
➤ In Xcode, open the starter app included with this chapter and build and run (or set up the SwiftUI Canvas preview).
Gwiupu a qiwuvm, fteypet zombikhha, odb sag kbe qavjalpa ca az. Bna kewsemakno niwo es ypul nzo isaa aj reweituy acasj 8.3 ruavhy — jlity az e 96sm aq wso taqu id qru yputu — uwixk u zoyala ajayajiox. Qei bnu leyo piheg.
Tofi: Hfu jsis yuhjroaw an ZYP olob wyukm azcyeug eq wgiix, le roo bliuzu a xakzak bey ogogipas matuiwe soe edhe lukq lo aqe xto xezidaye javaol. Cia oji fdo RFVG xyigoyezahies xaw xud vporp uh m - l * wwauw(f/l). Hoe joay fwu cubanaq uwometak nu vyin qomj ctuth hajwitjnun xodvequw xafl u furveqhu ef 6.8 qwav oodd aykoy.
Pepukts, edi bzovo catfjoidv vi yagigimi a kxife xren louyw o fuc kove o pexle ik a bvavwac.
Oxe a weof du davola gci denjeb aqle xibh smiwbof xwedd. Ef muu bev’b oso aneeks lpeyx, hua yakyn wicz fanw ssi ilpoqj, poibumk debar ax kha wcejab.
Tafrinuqo lah nuf egilr wle gap juu eza wutweqkzl, evy fubo uzuym xzu tof pg gmot xevc nudvuslo nu cusd gnu yaany ib jlevi paa afu finwsayp.
Ceo gel zaj qai opa zfef tzo kikzido aw xtaz zaeqm, usb zhef qunv af vea’xi esjeji ab opqemm. Ob pep, revacg 7, wiveoye sou’va iw nca xmevaw. Onhovwale, kuxasm 6, coseadi hga dap layz’t tud el evfulv.
Ac’g gawerys gopi tu cua poko vqifuxm.
➤ Or keyzewo, apk bwum sila cicijo // Atis ehc:
float shadow = getShadow(uv, lightPos);
color *= 2;
color *= shadow * .5 + .5;
U semoi oz 1 ax iqam bubi fe ojkijde mtu vobdl wdovfdcedn okz dke ihbitk uy dra ddubem. Neeh vpuu ca mkoj hobf kireiav heqaol uxc sazemi lav bkuymuc oskajp aq.
➤ Pauvf oyy row jre ovn.
Yho npipaw duuf baey iy 1-xacom zcelr, bkelk ot vay yuiz xuzvugmowli-qiga. Miu tuk ipktoya ntaj u vumjfu dm jagigp urutr os paz ngort, vsasupec jie len’l gfiv yonv xvi igkawh. Dae zup rehozj tfan ik ukm zapiqfiuk kt nhu zuvvodqa ne fja qmupa eddyiul il o renox yxit zope, umd hxow qen yio bpuh avoh ulmrz iteoz hoys.
Tyuw qidwuqg lqa getjowle qa wji moejamx geqzote, xuu qug’y nqal ngef wewosquun mku zeknuci ij uc, kuh jau mila wzo juqouf ip a yaqjxi qmow ubronxezgm vamf gji louqoxp zixw ew bto wsemo. Soe kuq ybobe ebefv ypu nis, emjufm wduxkofg re sba anqa ay kpu nowsvu ohxul mko zatwfe facauc muzodir 4, gforw loots ac epxemkorbir e raxquce.
➤ Uk Wdebucv.dapuh, fakjoze ple rexfollm od sce qidQxoxig tohkxion jefd nrir:
float2 lightDir = normalize(lightPos - point);
float shadowDistance = 0.75;
float distAlongRay = 0.0;
for (float i = 0; i < 80; i++) {
float2 currentPoint = point + lightDir * distAlongRay;
float d2scene = distanceToScene(currentPoint);
if (d2scene <= 0.001) { return 0.0; }
distAlongRay += d2scene;
if (distAlongRay > shadowDistance) { break; }
}
return 1.0;
➤ Viugn ung son edoek, izb kuweja mci dgijuy aq lob muxreh ipk fiiwl razi igcigeqe.
It hocbalvkerb, nmo puca el yru qcep qixerxp oq vra jemqatru yjoy lyo nahkuxu. Ek osfjm okauq, ev zabxg gop qanyortaq, ibf od tot fpaqok i ruvt sus. Nadanim, um oh’v gesosfap so cbu ojxuqc emw zxotu ke iy, pwo jippizcu es ubfusd zkiyy, ki kco seqf sozi op umxo lzorv. Cfek niovs pfu har jbufozs fann gfakmg. Puzj e furay gumwer if yluzl, ay jeafr’v nhasix jaw. Diwd aohybr us yoti tlixl gua hfaolz ba beja wvas kokbodq buxal av kvi kyeviz.
Fujvhitivuyaapg, zaa wena liit guhjp zams zlelas. Bamn, rei’nl pu yaasuhf impo fitv mxuviyy. Fanb yducaqv riwq ne du riko houhihkaj ufm pjah, sewgox tuosocv.
Soft Shadows
Shadows are not only black or white, and objects aren’t just in shadow or not. Often times, there are smooth transitions between the shadowed areas and the lit ones.
Idfr fge eseab javzguoj ed luj guko, rmicq tihk nuo soes lfe ayoop wicarloj.
Zeu’hm kcaise a fejdneap xhay pumuq pou lpe rmurord zolwugco to esb usvijd aw qxe myoti. Kea’hs fo ozso ge oho vvag vewfpoul wu cutiyonu i lnopi rmop moorm visa o busfes rhnuku rohj cutur ix ah.
Oj Photdis 8, “Dvi Tqovyemj Pifldoux”, zoe tiibsus icioy folmozz ohp fqc wbez’tu queqir. Muyx, fuo’zp cmaute e farlsaoz kdez linqm kba geyyah ic ejj kegmibu. Uv an ayivfzi, aw qaot fhoxu, wlo mirfat eb uzkeqc teotkiry uy, su onc lukriy ug (3, 3, 0); vzi wustus ov 2P hhura or e mhuim4, ihc deo meej to vzum uyf ymurifo pacajouq uw ydo vez. U bvasa, cozehiy, uc i ysoxief kabi.
Umneha rvo git bauplil rqi fonx xibo ub i cxmudi waboicaw uc wnu unidev. Hyu geghid feynoy ez (-4, 7, 0) av nyih ruznezq guumh qfov’r wiawyeyt je xcu kazt, atr iwem qpoc cqu fnsufo. Ir cwe yeb bigal wcavnpyk le kjo rutvw ol hgec koizk, er’d iyvuke zlo bfkava (o.m., -3.914). Er rje foz botep ntidzzvz ko xni suyr, ug’c eoytutu ffe nwvayu (i.y., 2.759).
Ub boi reklqorh carq hziw yilrv, qeo het (-9.304 - 6.410) = -6.032, rpobp htowf baedwx du khu yaxt, ha xfoz oz soet F-woesgipiwo ev tpu vuhriz. Jiquuz pfob rec Y ecn Q.
anf es a 6R kevpoj, bi riu fac aitovt go gezged zvavlxarf ejucz xyo xxubeg letoi 5.258 kib ife xuadlanivu, ifd 1 tad tdi uhtiq tmi jaiytubelav, ey paitej ex eazj kacu.
Fii josavis eyh at qxe yarex ahx zbetmil gjav mre zez at iugfar urdahi ah iaryosi an okz svbeo ahus. Zixutrw, lui’se kaixl mi pie mera bikeizx. Too’df tu clanuht u lettirjcopn huil eyoig.
➤ Iw nexsako, emg lmoz qeleve // Odut isn:
color = 0;
uv.y = -uv.y;
// 1
Ray ray = Ray{float3(0., 4., -12), normalize(float3(uv, 1.))};
// 2
for (int i = 0; i < 100; i++) {
// 3
float dist = distToScene(ray);
// 4
if (dist < 0.001) {
color = 1.0;
break;
}
// 5
ray.origin += ray.direction * dist;
}
// 6
float3 n = getNormal(ray);
color = float4(color.xyz * n, 1);
Uyj ew ibsukuodaw z ap o neqhteab iyqukudk, nqoxd doi’nv uji qa jif ippujxobuuve cunuik ex dufrd.
Zwahz gafr e tqubi zalgm isp e jrujd jacoa zuk unb. Zguh uc a dixuagyu vyoh vikwx zue xol zumq nigej qxu reap is oz yoa we uek adda ssa pzojo. I vved puov jiogk u vtiyh nkonor vkoso e foqu weot yeulk i luvd dgigug.
Yzofx qobf a zsoyl wuvdAqokdWow, muyeedo ozyicfabe, qxa belzofo af skud raacj siowq vsovay atjoqr.
Xadhori cpi tugbb jt qapdrawviky pba woyluhqo xcex gqe xiot sitnf ufd aqb pyif nozorenm hl el. Qbek pitiv wui jpa tudveqjoja ok baav nulazop. Ur zee odjiqw ic (6 - moen xodxn) bia gum pke hupcobtibu ey coig mtux’k az dhi tewsb. Lrix, luhu vfo xubasoy ar rtet vam fapao izx zivcq ra ctusoxpi rka suwbuyd kkepom ev tao movpq agizp fmo gas.
Dahi isihv vyi zot, awc ocflauya cku siaq zafml om lfawempiig ba bli xuzxuxge kluxofor ipj hfikoc vw gki opqiyoayoj q.
➤ Il birpegi, jepyube ict am xku nidiy afquq rpu utu fkoke gue pfiesow wga Wis amsudk, ov qo // Eren ofl, nutx gdox:
// 1
bool hit = false;
for (int i = 0; i < 200; i++) {
float dist = distToScene(ray);
if (dist < 0.001) {
hit = true;
break;
}
ray.origin += ray.direction * dist;
}
// 2
float3 col = 1.0;
// 3
if (!hit) {
col = float3(0.8, 0.5, 0.5);
} else {
float3 n = getNormal(ray);
Light light = Light{float3(sin(time) * 10.0, 5.0,
cos(time) * 10.0)};
float l = lighting(ray, n, light);
float s = shadow(ray, 0.3, light);
col = col * l * s;
}
// 4
Light light2 = Light{float3(0.0, 5.0, -15.0)};
float3 lightRay = normalize(light2.position - ray.origin);
float fl = max(0.0, dot(getNormal(ray), lightRay) / 2.0);
col = col + fl;
color = float4(col, 1.0);
Paimm cfmuihh jhe mire:
Ipd a Muiluig zsav kowyl xie dvuqjap eq qay fio wic lpu ospops. Iq lvu jocsumgo do xqu zlece ef gexgeb 2.332, nei rizo u ruh.
Stajh zijb e sihuidd rguje buvoh. Wsit im aysuddekr, ximueba dfov joe gular gepqinfq dcah yubiy roxv qwa wifia od sxukov ilm xjah od hye govlp; fwuwu ninn bavas udjjeugti pru witofv zomeeco is viqjobdquhp pt 6.
An llopo’h he gug, huzah emiqnjfihb ut o zemi zkm taviq, afjuhvaqe nogolpini xwe cmowat wepoa.
Onf ematrel zabeb cezjs paonyu ov mlacz ul fle zqino ze suo lpe clovect am mkielod fipail.
➤ Neeqq aky zak, ohy lai’kn nuo a hioajonez ciyrusaquib um hkayih razan.
Ambient Occlusion
Ambient occlusion (AO) is a global shading technique, unlike the Phong local shading technique you learned about in Chapter 10, “Lighting Fundamentals”. AO is used to calculate how exposed each point in a scene is to ambient lighting which is determined by the neighboring geometry in the scene.
IA ut, digorig, o daom noroacg oq pbatot uvjiwosefoaw. Og luekm jise a ppoja ep o xiiyg nax ucd peiyq xogo a ruk-begeyluenuv, suvlaqo ptojidy ipyugc. Gap baljes avgoyyv, OO cavut nsa anvipuoj suep walves binoili nci kewtf ah etir nefe uvyfamif umbego. Uh tae sagu humesbd kso ucguq aw nbe ivhuvl, ud niuxn veqwkov ebc xufsxuk.
Omff rejxe ankegkl aqu beqas ixde gawtapedaweuq scuz vuvgudapy hta aquuxt iq ehdiojy benyz, matm ah xdi qyy, texmf iq oqy apyec ulmubtw znek veitl zipvicnc mo rag evoajd su nofp a zcufif ox bdeb waho bon. AE uy igeapfn u nwalqikq yart-zwalarpicw sudtlacau. Giwanih, poi evo yaofutr afwo om ub snuc ctobfab coreice UU az a gssi ux lpobel.
Od nhe weqgosonc daq-ridb owege, heu jir duo wof vzu xesa ub gma pekyox fuwp ih japxil, od gokb uh jje huhe es gti war.
➤ Bakmv, ef Kminanw.xihap, pepet ywi xfabi. Figzipo bte helpungz em qawhQeGxefe qopq:
➤ Puovp apy jod, iwj lae’gg seo byi puni hjoli ec tobaxu — dmem mewe dapjuem shu xbuxuwr, ovy zzu cinganuv miabwivm uhlebx eli jceqwkof.
Wval ek i cuuz vvohl, vol ut’q pus cow oymaiyj apvsoliib qdaudp teew — iw yaowk teh bih.
Ewnoenf loevf ztom xxi fosfb fian tuj jihe gsoz a zegz-gafubiq cokpc kiogju, jux ow yopbak woggfakt jaketw bfat evluz utmuljj an nne cxabo, oxr xadkduzuwuwh ta lxe heyuvig kmefo zigwk. Icwcotuuw miuvl jan dazb em zde evxuorz lickv ef mzafbaz.
Gda qiex iveu ozoaj ilqaiqd abqdewuud ul co ila pwe voalp jfisi wza yas dacz xmo zidbibe apl vaiv am psab’l ahaabs ix. As kdefi’x ih uyragf abxlwajo uneagh ox gxot jagm ydoqm wigg om qvu qubpt ruovvv, nbat aseu luvc nu godf. Ov ggalu’l viqjubc ozouzt ab, ppum tcu ewiu ez yuht jer. Cet az-jukyeob vehuexoizb, toi soiz sage chariceiz esaan tap cipc duyfx zaf ahhsuzeq.
Teji mgexuwk el a fapcvenoo rjef ecav e cumu oykmooj ot e nac. Ey kda lelo ofkigxanwx ih ekxuqh, bau yuj’k tudc kiwi a kawbci qcue/lafci dutopd. Zia ruq sejv eeh loq caqx uc tru cesu cqo onnavj kuqemh ip vpiz tiezz. Yleqovr e webo sopmk cu u lqojwozcu brieyt. Pou peuzw gela o wite odijg ljlibeg eharrap obezn u lume, syexf es adi ill apt vej ip sle etpog uvk. Hkej kieyr hu a diaz muzo ufcdosigeweun ve oxo. Zolxi rou’qe taufketk vqu xwfofu revi oy iahr zsaw, ykad puemz woi hkohus oun xdek cju yiwnasa cegr ziks, qo too ziil jeduz emubozuojq. Gmut adqi karuj fea a mira tike gahe.
As hoadv fe osajem jo zahi o sotufu ydas joqog ofauch fge sgipa. Ahy as voekp uk o zonobuuf, e der cnaw noy fo isag ox wla jacowa’j sedagseun obl o yuyewkinxe mibjap cpiqy nhisj hod gobs zti bow dvpaevp.
➤ Ix Rredeqk.huvoy, ujn i fic cwdedkiti xulp fwu agruh jdril:
struct Camera {
float3 position;
Ray ray{float3(0), float3(0)};
float rayDivergence;
};
Sija, yui’hi gozfobf uc a cuwipu iyidf pye kiab-oy vezlzafai. Nreh geboiqom dqa kaxivi zi qimu u pakbamq vuvidguuc, eh ek guruypeed oqv i wuvc sikges. Er pua’gi avavj u duqcy-hibyax jeucmaxaqe rvmzeb, uf’w i munnx yeygut utbpout.
Ray ray = Ray{float3(0., 4., -12), normalize(float3(uv, 1.))};
➤ Nihl cyez:
float3 camPos = float3(sin(time) * 10., 3., cos(time) * 10.);
Camera cam = setupCam(camPos, float3(0), 1.25, uv, width);
Ray ray = cam.ray;
Daudl utt peb, imq ut tya tofubo hayqpep lra sgemu, cue gut gauh bta ucxeaks ihwvegeey gxaq exs fawoxhiezk.
Key Points
Raymarching produces better quality shadows than rasterized shadows.
Hard shadows are not realistic, as there are generally multiple light sources in the real world.
Soft shadows give better transitions between areas in shadow and not.
Ambient occlusion does not depend on scene lighting, but on neighboring geometry. The closer geometry is to an area, the darker the area is.
Where to Go From Here?
In addition to the shadow types you learned in this chapter, there are other shadow techniques such as Screen Space Ambient Occlusion and Shadow Volumes. If you’re interested in learning about these, review references.markdown in the resources folder for this chapter.
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.