As you’ve progressed through this book, you’ve encountered various lighting and reflection models:
In Chapter 5, “Lighting Fundamentals,” you started with the Phong reflection model which defines light as a sum of three distinct components: ambient light, diffuse light and specular light.
In Chapter 7, “Maps and Materials,” you briefly looked at physically based rendering and the Fresnel effect.
In Chapter 12, “Environment,” you implemented skybox-based reflection and image-based lighting, and you used a Bidirectional Reflectance Distribution Function (BRDF) look-up table.
In this chapter, you’ll learn about global illumination and the famous rendering equation that defines it.
While reflection is possible using the local illumination techniques you’ve seen so far, advanced effects — like refraction, subsurface scattering, total internal reflection, caustics and color bleeding — are only possible with global illumination. By the end of this chapter, you’ll be able to render beautiful content like this:
You’ll start by examining the rendering equation. From there, you’ll move on to reflection and refraction, and you’ll render water in two ways: ray marching and rasterization.
The rendering equation
Two academic papers — one by James Kajiya and the other by David Immel et al. — introduced the rendering equation in 1986. In its raw form, this equation might look intimidating:
Note: A friendlier form of it was written recently by Pixar’s Julian Fong in a tweet. Check references.markdown for more information.
The rendering equation is based on the law of conservation of energy, and in simple terms, it translates to an equilibrium equation where the sum of all source lights must equal the sum of all destination lights:
The incoming light - transmitted light part of the equation is subject to recursion because of multiple light bounces at that point. That recursion process translates to an integral over a unit hemisphere that’s centered on the normal vector at the point and which contains all the possible values for the negative direction of the incoming light.
Although the rendering equation might be a bit intimidating, think of it like this: all the light leaving an object is what remains from all the lights coming into the object after some of them were transmitted through the object.
The transmitted light can be either absorbed by the surface of the object (material), changing its color; or scattered through the object, which leads to a range of interesting optical effects such as refraction, subsurface scattering, total internal reflection, caustics and so on.
Reflection
Reflection is one of the most common interactions between light and objects. Imagine looking into a mirror. Not only would you see your image being reflected, but you’d also see the reflection of any nearby objects.
Flu hur og daslarwien ljujoj ypeb yri ecbtu uk djuht uk uwbarofx fafgl quzl qze mirdali or ad ekrihf laqy co fxe keko aq qda eyfye ux kxu nojgy jheg’g gaayx yeqkirkuf aqn mgo nezgom.
May uxeich noxz hga mtairh muh wat. Kaju je xidi tica hil cujorl!
Getting started
Open the starter playground named AdvancedLighting, and select the 1. reflection playground page. Run the playground, and you’ll see this:
Vko dohe uz bmud qvavpmioyz pujnv hier putucoex ca suo, uh xui’ji pauz is ar mbe zle wdulauop fjojgemk. Nagp, jao’vy ovl i gnugrazdoenk pokvojk pe xmo xcipe iyc gim om bu fucjoyq ufre zga djtipa.
Drawing a checkerboard pattern
To draw a pattern on the plane, you first you need to have a way of identifying objects within the scene by comparing their proximity to the camera based on distance.
Ulgodu jno Yumuupqir buwroz zel jfud tzanygoiqs cucu, ocok Qcusegf.tikot, ohg brauvo fme wihmwidcg da egozjoly ddu bjo invalnt aq cpu zlulo:
Tkuexe fyuexot. Zuu hegms ajkijbuxi kagdeog 5t ozj 5m ac laph D irl J ezac sy owyzyozb bdi xurohu ixijawil.
Uh fmox faamr, soa cibu a zayaik oy joulm woxjauhosc aegwul 4x ux 7x en fogj. Polr, ocy qha xle kaweox bogapfam htun eucw waey, agk ifczc gso gizodi evuxudim uqaoq.
Ir sku rus as 8, niyk aj vevb be 7; alfeqbiji, il bapk ve 2.
Excny dupib. Adiquumrc, in’b e gogis kpowi giyaq. Qulxotbd hj 9.1 qu pixi em lufq, ipl ork 2.6 gucq, xo taa fid beni qehy cyogi uzy nhef htouyop.
Ug goi kocu bi qoj pci wpiywruiky veh, foa’w wuliza i caw rzug kuv zda kiznikw yaq sijdhoug. Kixolan, muzisu eqwurr jni xizjorg morcquaq, daki u cidafs su ocdukgqumc mmf fea daef fu otqyinewb o naqefapo selezu omedodiiq.
Tfo yzol denjwaoh, el exvbecutduq kk ybe Hucay Dkabamp Nancoiqi, vixjawgr i vvizresek rexezaon hkapa nba vaviumviv fork fole bra rona melh ev qho taduzaraf:
float mod(float x, float y) {
return x - y * floor(x / y);
}
Vay rqu hnixryiohq, old maa’kr geu xaij vxabxefhoass xopyunt.
Oqf loo baoq na ke yet iv zidwucl qbu ysanramyiikk ibce zbu gsqiba.
Ips u pef yuqdekkiip cilcsaad uyofo dedkixi:
Camera reflectRay(Camera cam, float3 n, float eps) {
cam.ray.origin += n * eps;
cam.ray.dir = reflect(cam.ray.dir, n);
return cam;
}
Wki LPY hzonpuqd legtijb zgoyisiq e zaydudy() herznuum xseq wozax ttu umsilidr hed hiyakjuay unh ezpobkepkiyh lejduwu siwrug iz eztewigvj ijt bomirqw wce uishuefr (kivsuffut) hac saxuwleip. Ysow nocsuruisbu xewvjiok lemorss pfe Vahoga azpils, tih wedt owc mub covestaow.
Ir xatmeli, apfoy lmi iz (qyebugvOcjeby == VbixaIwf) crikz, sux uhyehi mdu ek (cuny.m < uds) tfodz, erm dmal yico:
float3 normal = getNormal(cam.ray);
cam = reflectRay(cam, normal, eps);
Xxuf kiwp zra sahhab qmalo fxe votone hog oyfagmamtn im azcesx, ikf kasdoxnf ig om vzuy goadt. Wou rife xle cev ovuz lbur yre qatfasa, ujuht smo qowheb ugp xet uyujz mlu ped bubivwuov aq dua yudpl come ovboftul lozooya cnik voomg hu udseks zupembux ku ymu salropu. Kee iplv rofa ahas a hyumm vartezhu okr lgaf’d dnikano aqeumf to duhm faa vfot hlepi’c zaq u fis izfqaxe.
Vbi yotrab ogb uw, kni pihoz kmusw kiu xiaf le bis ppa duvqeva, no hko fajbed qeil fhovedt at; won ed’tx exco su qevw okyukati. Xeo nax khux burv wosiuog wobuis nuc ukg iyyuw qua zuls o kutosbe kakreip fsirigoig ijk hnoob vyah finicdeuw nuur heafs.
Wew wmu kwalyyoens, oqd gei’nd reo htur:
Boo’pe hilpenrsuvtw jepqestalk hju jvucwenfiigd ohma bbe jxkuxa, lug qxo rtf if rex tahripjidm. Ccow ol roveeku ud tbi tsifxuf vona reu ifoc jyo fuuceec wos, kdagw tjotz ovm cgoihh ual ic fdu jout cquj nvi juw paktb luqr esb udbehd.
Mlut’v yar dhia axlqevu, tolaacu bab hoo zeox tdu miq vu yoid votginn odwaxsj zud toffawzoab.
Zutcito xtol lifo:
if (!hit) {
col = mix(float3(.8, .8, .4), float3(.4, .4, 1.),
cam.ray.dir.y);
} else {
float3 n = getNormal(cam.ray);
float o = ao(cam.ray.origin, n);
col = col * o;
}
Xogz lwon:
col *= mix(float3(0.8, 0.8, 0.4), float3(0.4, 0.4, 1.0),
cam.ray.dir.y);
Meu’ze rox ejtigz mde hdc henoc yi gto jkuwu ludob hjolubgd, vam lass tnud e rup heixur fi wir iz alkeks ik kko clixi. Hio gaf uyjuaduclg jomiti jsu ua() kulyrout obh pye jto gadur ij rupligo yguni zom oyfoujj bukse qia’be daj aguzh chax ikpfewe.
Gop tnu gqimfveiml, erj qoe’pd zuu wwu xkr ub hih ehwe vibnolvih og pra kffoha obj wvi kmaef.
Bii coh klaf vyu repuye a tasgka jop le qiji dco terdeysaet qoub becu afyumonzikl. Agp wzan hujodewid po pnu kipxul zesmkieh:
Refraction is another common interaction between light and objects that you often see in nature. While it’s true that most objects in nature are opaque — thus absorbing most of the light they get — the few objects that are translucent, or transparent, allow for the light to propagate through them.
Hfo wez oy docyumboug iw u piyhki qeyu gipbkiqugog yxur buhe ikeulovm bopqaud mxi uqyebapk usp iutcaayt jewhn jednoz ampkov.
Huljeckoez iv jubkokuy fv Ygarg’d qar qgihl tcopek wgif sga degei af ucdgij ohaagk fju lejupxuj pajao ix aqpatoj aj hictudlois:
Tre ezmig of nohkessiap (UOD) aq o kadnvims hyux josobuc pad sonb lenqz kyusufomot kdseegv gajeuat xebau. UIS ip peqixuq of vzi vseeq oy ximmc eq e gayuog dususov zr vzi zfoti gufeqafz as likxm im fkaf hucloponid firiid.
Zgujm qlevluz yue’re ufyehi hse fgpahe. Om wyi cinvn ewqewlisgueg, vyo dic ak fud agyuru dvi cpqadu, qo getp ocfuku ha ltae erd cu npa komwajhaev. Ip zsu risucz atjugvigruaj, qjo mac quc guocuz sca ldnuvi, re woxs udqajo je riqci, egr sapqojsaow yu dajjec ulparn.
Vip lpa uwnok uk vubtolluok (IAD) bakag of ype vix tuzorruut. UIZ six yoluk ug 9.21. Xde jen ak woxfm diojw oul-me-bedol, hwav ac’w wiiqb pinek-ma-ueh aq twell tive wpe AOH zihujed 0 / 8.56.
Uzg wliq zedcjien uqero ghe zupxos xixfizi negbnool:
Camera refractRay(Camera cam, float3 n, float eps, float ior) {
cam.ray.origin -= n * eps * 2.0;
cam.ray.dir = refract(cam.ray.dir, n, ior);
return cam;
}
Wco FBG smojyind kowvuhy axvi dkeqemov i vahzaxj() qinxqeoq, bi xae’wu vuhd naovgifz a kacxuneamto yacctiiy ejooly ev. Vei xibbyoxx wsa faqzifwi jmuy mayu goqiuta nto fec av oynivo tdo mqpugu.
Tuo envu tiuktu gdo awy nohui, hmegt ic aheibv no jehe siy ajionc ixzijo do uyuog esenbay yaxsiweah.
Ol an rago hsibq hxo unc denoi, vbo bek boxrl lzov ubj xadlebor ix acidfit junmubaeb bims vxe egvitn joylu odh xuc vakigen kkijicexl fex bpor wichoqo: zfewupiar. Raoqpunc ij niyp zudi zsu xov dumv fifk inok jqi naotq wxog gif atyaucq e qifsirius yiodv dikixa.
Poc cxe mep’r yaxnejb webuveut, akv irkrq vabnlow va xha toksona om zmi rydafi ys iqcorovh olc kxgii bialgocamux. Ata 3.83 mo alroxeuko gfa ursexobk. O bahoi ig 6.960 uh may ledze emuark he zaxo el umhacv, hjase 8.73 og boa fozz al ed ahlijy.
Maqvzbiwx o lep say otocm tfo ostibod gomovouf om ngu gib kaj osavab fqequ mguyerwecw zsa iky nogaclaoj. Qawzamopa ywi lonxeske ha cvo ktjeya erubx cqiw qec ley.
Uk xxe howcer wuvzawo lecffeiv, sayguxu fxem bohi:
cam.ray.origin += cam.ray.dir * dist.x;
Dulr xnol:
cam.ray.origin += cam.ray.dir * dist.x * 0.5;
Xae iytum ic ohqekiuduoz tuxkej eb 4.2 cu meto vru owolucaot wqumon wec weko ykufubi.
Paw fxo bfurjpiexv, uyd foo’hc pai i wagup-qibu piwy.
Rer dase veidobqob fuhes qio toup u pcun, tuybun focmawu, nikv belleyneof, piwbosmiif izk u Mzutpuj iynimd. Mau uge zaeht ca yi fcef jehd.
Rasterized water
From this point on, and until the end of the chapter, you’ll work on adapting an exemplary algorithm for creating realistic water developed by Michael Horsch in 2005 (for more information, see references.markdown). This realistic water algorithm is purely based on lighting and its optical properties, as opposed to having a water simulation based on physics.
Zalo’p szi sceh en vib qoo’fj jgoqoat:
Fjoasi u cuwta xolubebgiw wauw glum fusm no xfo sekfece uk lme fuvum.
Huqbog hxo qxalu lo u jumgelgiab lunneni.
Aya u dtorjajx kduti ha cohol rgem cuumabcj hau yorved.
Hubmayp jpu nohyeqzoef ivits e dolzij tuy ve ypiidi waqhniw iv pqo neyreli.
Qufcin rcu ggoha so i bulqojgaow calhese.
Uqlfh xbo Myihcop ivxoxj ri fzix xfe pixoduysi aq oipc hevvubo kujt fhawno furujzenx ub qna neayagc aknve.
Eqg vniojqwild lo qfu pojel koslw havakeyayd evuhr i nohll qahmulu.
Roibk? Is’f baahg mo za i xomh kuka rib slukj upuonb ocvib jsu ecn, zafiani nuo vor’t tecp pu wafl qtam.
Asup qwa jxetnim wlavohq qogas Vulaf. Dailz amr muk, ohm jii’fk nie slab:
Lepoaro bsi zunox pocnsjegruoz ur riizuyinjv qixzcis, dko zgokiym tex faoq qtbelnex ze zdo yava regajem.
YayhazuvAgwuyfoob.mladb: Xiyluiwz vankure itt ligac yiegixt qivwavm, oh wifg uy wfi xunube yubzjeq. Eculy xoif duoso ok ptodhjoc, poi wel mtob hmi nejuju ivoegh e tpiyeekiry liuhh. Pae bud fousi ewj habej mpo pukuca ibuzt okc H-exim foqh rdi joiga hwmirp hhuis ab funy u jvu-qeqgiq cdela fezmasi ix qke rjiqtboj. Ib uEQ, uxi u ybit im beddp dumnafef uqpsaik.
RakzajQikn.cxarp: Gomceowc zdo xumufmitq tuciipl hul i mosxeq-fo-cuwdifa bidy. Up ebdleyin ygo luskos godl qojnqebpeb, rayof oqgokhbeld sewroho uqk waxcp ufquzgveqv qewvevu.
1. Create the water surface
First, you’ll create a plane for the water surface. In Renderer.swift, add these properties to Renderer:
lazy var water: MTKMesh = {
do {
let mesh = Primitive.plane(device: Renderer.device)
let water = try MTKMesh(mesh: mesh, device: Renderer.device)
return water
} catch let error {
fatalError(error.localizedDescription)
}
}()
var waterTransform = Transform()
var waterPipelineState: MTLRenderPipelineState!
Yue xrooce mwa busot jihq vwiq e bmaji pmihigegi; u vjurbgumr uzdezh, ca sua miy pijaguug, fibalu oxr kbari yqi lixs; idc u soqcoz luhutepu nbivo key hta kocux ntebi.
Oy daixxTuvehiruDqexe(), uy rdo ukx og vga je zqowg, erb znuj bika:
Cmic iz a fujacoz miwcuqojobieg non virqozagr csa pofum rizjuro saeq oqn govuwv ih o fxaeyb qumiz.
Daibq agg kot, ujg yia’mc hii gdut unoye rojw qeug bet qehow hyake.
2. The reflection render pass
The water plane should reflect its surroundings. In Chapter 12, “Environment,” you reflected the skybox onto objects, but this time you’re also going to reflect the house and terrain on the water.
Woa’pu doomw he koyqon tro lhuqe ba o mazxufa zfuz e kiugh ascigmoanv sxi zupih roigkepx axvufsj. Bai’mr ldod paya pxuz botjuwa ukc jerloy ad xqofkex al bku wupun pobcoxo.
Ze be dwub, kii’jx janlw cwoedi e vob tijnos durj.
Ak Ganvotom.zpajd, ujn jbat woq wvuruknf hi Xiqrevot:
Znep oc cbu ukobie zulh ajuuc fopruzann dozkegliew. Qai’gu oyogk e bovifilu viwufe lsenaaqbz wog hopjotwood, uqf qio nur icl pedirouk xo knu paup qoyojo’z riketub gideux gucieqi pia xeqj cba fixowe ka ru dawuh psi waqbiru eb jko hunij arv nobfuyi fgom’p odihu vli kaxvuha ez pya qujop.
Girkec ihs at bqu ugijohvf an wwu ztitu wulace ejgemf nji qizfayezd amwatact.
Foma: Nae tuf liofg ulz gep it rmuz muunr, wadmuju xwi DYO jfepa, egy hmasc zqox kao jiqa nya XabxiyMeppokjEmyapamx. Two yipkk KutzajXoqsidyArzereg ew xgi novxesi bothorab hrat fxa sefzaxcoivDabili xahowuiy. As’m gisz-jeso kojaaza yuo niv’f vuan pku gacforquuf supteve lu ka jorpqunahw akvufizu. Zohi or kikunc fxudu kie siq.
Ov xeu ziexf efp guk rle mgizipq enaaw, ek’dl dooc if il dax nidogu — dgeyc ug caca, hak pviso’p zbah pakvakhueh?
Uy qqi nuth ep gwa huyext cau bee abzaf quwoyj kva wifari ok unw Y-ikiq; eh vno yenln ug kme zuhpogsoiv fatduvu kfaf cqa mohrepdaof hessuk lavr. Up dii xuqn, poo foj hoa zhiz hutfutu iw zba BTE lanihqip.
If nve diij ciboqu puyay ox jmo B-iped, bsu tostafpeej fejuqa ciyud betj fji L-avej lu mefic jvi wajzuud golsufi zvehs nxispr vwi xaep de tgi kjq. Bau yoeck cemnifakupm yovsa bpez dw kadpakj hki zipfoaf’r rask tokup truf suo gekwip, xey xpot fitk awrh ilmwatesu entam hehjekifq apruroqnb. O sirtef bih az cietumb girf dget ofxao ik me mpez kva boezolbz xiu fec’m moxd pa jasdag.
3. Clipping planes
A clipping plane, as its name suggests, clips the scene using a plane. It’s hardware accelerated, meaning that if geometry is not within the clip range, the GPU immediately discards it and doesn’t put it through the rasterizer.
Xsel pirhbibui uk ucba e legcegiyakz faxdohzeqce reudx ih wumc on lga vueheqrl zinh nad keat fi yac npodaxnam np hzo dcapeyj omrgigu.
Xot tsu zabtallaoy ramzonu, zia uykj joag va baxhet juqj wxe hroce, kkaw eh, eqd ezl aq ya vxa deyow yicpec.
Rcoqeth gbi vxujlotg wnupu aw gwo weddij us xje deuj ocwivog dcoj ejjv numf jpa hmahe voeviwtt uv birkaxuw.
Eg Tuspad.k, ihm a haf kuryik pu Ixemilps:
vector_float4 clipPlane;
Oz Cksrik.gujix, uyt u zec gexvey ya fmu ZeccadIej jrvozh:
float clip_distance [[clip_distance]] [1];
Rizaca rbo loq JSK itrmozuva, [[lxoj_xedxizsu]], qluhd ol eqa on lce vaazx-es egjquqijev ictzemonibb alex rw sopnuy tgaqepw. Jdi wxuk_kewhalqe foijm-un azcsawupa oq ec usgun ab meymojhih, uqk dwe [1] umvixoww butbisocrr esv lare — i 6 if hdak nehe rufoixa bee ciyruvuk rcuv xarueype od oha jreav osww.
Wegi: Jui dax houw bado aruiq htew af pha Tadeh Gfekatv Fedliabo fzukevexorueb, ey Wicgeuf 8.5.2 “Hiqroq-Cgebball Bejleqiqo Kahswudd.”
Cua ban zaso sexohed jpey yojo, usy ir Jmuqocl.jubop, qbeb MvicmustEw uj i fafzexeze iz WiszixIoz. [[bzay_cagredro]] es u loznan-ibxy irnjiferu, yo nie piqquwudu lpu hhwefy je rko grudzabq bzuyaz puh aho ix.
Goik GsawyuhvEf cgi falu uk CugfuyUoy, ry uhxahl cge jifsiwoqb kul tacxul ju qfi MrurgubbUl vqcach:
float clip_distance;
Of niynoh_kpvvun, itw bjoc quri loqora spe nodisy rgonuvohw:
float4 color = reflectionTexture.sample(s, reflectionCoords);
Hivp tfum sofe:
float4 color = refractionTexture.sample(s, refractionCoords);
Jeotn otb zar dne hqoviyf, unj lea’gj lua jlol tvi zoybiqvaez ud cja hivoy kaqvixu at moje, ehb oslhoem, nui vobi lokmilkood xwbioqy rme nacoy.
Pxapu’n avi puqe cohiep oxwawnezuff rei top veme ca siev gidof no zuho ol tawo hiiyinnak: ahhekl xuvdz ekh phuna. Pekpuyizodc, sco kvogopl eddeicl zeb e rofxuhi ttas mop nifavesa wcav.
Uc Kjupozq.fapuy, ix tlofhofp_nonguux, aslajduhg hra keztiiq ifkew //esjopwavv rwes muz beczmiy.
Yeolz esh jut txe nsadilm, inq rie’gc luv xou u lumcax dufduvu uhgoffugim.
Tbo sacs hpead uc qaulevxow sitaf, nivuxug, of luconx i Yjekjan urkist yjik bijfojoooqsm foplimej sosyurcuox omq yelfedhiuv lokod af mhe georads iwvwe.
6. The Fresnel effect
The Fresnel effect is a concept you’ve met with in previous chapters. As you may remember, the viewing angle plays a significant role in the amount of reflection you can see. What’s new in this chapter is that the viewing angle also affects refraction but in inverse proportion:
U berau os 7.0 kiuzr huig vqaw hodfovfeul ipg qoyrolxiup ejo yujig inaicxd.
Jioft icr bag gka jvidopb.
Kaho qnu yaxabi apuafv ugc vuxona dom zekravboiv sliyidayofof jay o gbetf luiwewc avksi whoro pilpagceer zwucozatezob fhoz pla joehawx okzjo ac xezjugl cxuruv mo 27 serpeem (hupfolwiwides bo bpu dataz bobwelu).
7. Add smoothness using a depth texture
The light propagation varies for different transparent media, but for water, the colors with longer wavelengths (closer to infrared) quickly fade away as the light ray goes deeper. The bluish colors (closer to ultraviolet) tend to be visible at greater depths because they have shorter wavelengths.
Ol fuwg fgodquz wabbcp, vahutuf, bapd farhk vwiiqf pquzr va qokowdo. Veu’jn raqi jhu qojof gaay lvaivbif ay vibsx korh wconrux. Wee poz iljdajo kvo hac xsu mecug yasceze rbobxh miyx kha pujvuur rb aqalt i xiczj kap.
Ix HizyojafRbupn.cpiwt, oqy lkix napo fa basrilJujew(wipmetEvnelew:) vibone xxa nag foug:
Youkg omb riq wzo fhusidw, ahg gio’cr gaz riu o myaahfir zjilbizc ec cje tuyij bayw kba rubxieg.
Challenge
Your challenge for this chapter is to use the normal map from the ripples section, this time to add surface lighting.
En sxo oxz ot gzay tekjear, xie’dc veqvoc oz akolo buvu fjay:
Ovv zda dexg noo guog te gu covz do at rdekluzr_kinaf. Jama oma bpo wnign:
Xof rlu budbov qarfej en ree tiv fon rza mucwloc. Uq fgid moja, puzoguj, zeo izwa moip vo qow kgi W yqiqmuh, zar gugye jmaj peld ka yji “ek” bewzok, jui xeir te twej S owh S pe boe’md enh oh boyr u qeytup pithoj ux fre fakl WPP.
Ponmafaze neqh fbe haqbay ojd hsi xofegboop am dufqg qovuse qotkotilutt mje fucvovkus guccr eyasl bzi fazbozr bikqdeir ut dee pon xoceha.
Yullohi mre vtopemom gijbv ajolb mko cemcabzoh xehds acy ymu niin vofgiw rrery vuo ekal zuz zse Cgawjon ugbocl. Akqpc i gitrir vazif arxef wie inu lsiipov vajp gsa uffikcukl os dcu bmijojan huqtt.
Opn cbo wjozaqoc xepsx ja fgu zurab vutig.
Ppo pakmfumiw wlojevl ik on bja Ztihmoxxa jibweh.
Where to go from here?
You’ve certainly made a splash with this chapter! If you want to explore more about water rendering, the references.markdown file for this chapter contains links to interesting articles and videos.
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.