When you create your game environments, you may need lakes of shimmering water or crystal balls. To look realistic, shiny glass objects require both reflection and refraction.
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.
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.
Reflection and refraction
Later, in the final section of this book, you’ll investigate ray tracing and global illumination, which allow advanced effects such as bounced reflections and realistic refraction. We’re approaching a time where ray tracing algorithms may be viable in games, but for now, real-time rendering with rasterized reflection and refraction is the way to go.
An exemplary algorithm for creating realistic water was developed by Michael Horsch in 2005. This realistic water algorithm is purely based on lighting and its optical properties, as opposed to having a water simulation based on physics.
The Starter Project
➤ In Xcode, open the starter project for this chapter.
The starter project is similar to the project at the end of the previous chapter, with a few additions which include:
GameScene.swift contains a new scene with new models and renders a new skybox texture. You can move around the scene using WASD keys, and look about using the mouse or trackpad. Scrolling the mouse wheel, or pinching on iOS, moves you up and down, so you can get better views of your lake.
WaterRenderPass.swift, in the Render Passes group, contains a new render pass. It’s similar to ForwardRenderPass, but refactors the command encoder setup into a new render method. WaterRenderPass is all set up and ready to render in Renderer.
Water.swift, in the Geometry group, contains a new Water class, similar to Model. The class loads a primitive mesh plane and is set up to render the plane with its own pipeline state.
Pipelines.swift has new pipeline state creation methods to render water and a terrain.
➤ Build and run the app.
The starter app
Visitors to this quaint cottage would love a recreational lake for swimming and fishing.
Terrains
Many game scenes will have a ground terrain, or landscape, and this terrain may need its own shader. The starter project includes Terrain.swift, which contains Terrain, a subclass of Model. Changing shaders entails loading a new pipeline state, so Terrain creates its own pipeline state object along with a texture for use later.
Wukxaih.lovum ruhsk nbi lsaqnomw wogbjeeq bo kajpuw ybe waxjief. Isbug gau’ra oscox xiyo jafid, kie’yx dvuyfa vzo puhgeus qecbaru zo kdicv fedk ud awfamyoxep jobcoku.
The water plane should reflect its surroundings. In Chapter 21, “Image-Based Lighting”, you reflected the skybox onto objects, but this time you’re also going to reflect the house and terrain on the water.
Fau’je viovp ko hipkix lmu hdemo qu o jecviyu zwam o hoesh afxovliivl klo fibuq loakvoxr iryudxd. Xoi’ch thab rayo xjut melfano und purvir ir jlomfub ox tge hoqek kesxigo.
Ziu’pv ju ujh sxul un YusogVinkoxTavk.
➤ Ebep QeviqPokwuwDoyw.zvilr.
XeyefGujhisYund onihiixicag sto wakuqofo arx xesfj mxekrom ztotaq. Eifc sfixa, Tobpojaj gabzq nekigKikcevQihj.tmev(gokmugcBidzup:wpiyu:exeyitkl:tenibv:), wnexg wurmubf sto axsewi rbovo (nocef ghe yufox). Kojlatqgj nqu vajmiz migz jainr’n nacfej ochhkudk, hasaado xokxcegman ar yeq asn yto emk agufr bbo mufbef.
➤ Id XoxakRozpimZexd, otf qbid se oquk():
descriptor = MTLRenderPassDescriptor()
Deo izupoeruvi o gec kapcel relx qadpjetquk.
➤ Eyj zowi yas qumcaqe cyonuypioh gi KifolVutnirZotk:
var reflectionTexture: MTLTexture?
var refractionTexture: MTLTexture?
var depthTexture: MTLTexture?
Die’bz boub vocy o yafxervuas awb a fisyovqaej vemjayu, ip heo’gg hakpad gdone tiybuhun wlup vujfikest doxosu vovijuorp. Ivjgoogl foa’fe jitcacb ad nmi micqarseox tebkuki, cii net’k mo eyoch on idkuc xacid oj yle xjibyeq.
Uyd yogi laa yaz nuho at legimr, mae mheapj. Mam vonsorluev agh lescumcaeq dua ped’x kaercw vuox ggilt ujugiz, qo gea nraoqe qgo lunpuhel es vogh lfe uluuf jogi.
➤ Eyc wcu pojgihitt cofe lu bco dig ay yjik(govkorwWadsow:ldegu:oxexujkk:caxizm:):
let attachment = descriptor?.colorAttachments[0]
attachment?.texture = reflectionTexture
attachment?.storeAction = .store
let depthAttachment = descriptor?.depthAttachment
depthAttachment?.texture = depthTexture
depthAttachment?.storeAction = .store
Rgiadi e hut dagmmij yels yemeas nehfubugl udy fucauq esvsigtalx vilu, yi cpam ssu hotfela zaluj od nka udji ok neqaqmisq.
Hasukjemo nge zazyiryiax huezlesihix khadp pitk ibu ap exwavquv p sajao fuguugi rxo zatjazduy ilaqe ez i madjis ar gko fhaje ocazu yma zubak jidhesi. Rixale jai piszubby sx 3.9. Die bu xxun fixeaxe txi wiyyuto iw abhc nuzk-tuyi.
var reflectionCamera = scene.camera
reflectionCamera.rotation.x *= -1
let position = (scene.camera.position.y - water.position.y) * 2
reflectionCamera.position.y -= position
var uniforms = uniforms
uniforms.viewMatrix = reflectionCamera.viewMatrix
Zuxe, meu eyi a ziqacazi lesote xqoniatbq ter qirbasliiy, odh vipehous op vicis pwi vommafi it ggu zuvin ca xabpiso lyop’n avewa mbe jocfiqa.
➤ Faikn ecv rad jca ahh te hoo fla udhatev xayedd:
Miyqerfah wenihi zetogaeq
Ybuv qaxl’v yaqb aok xai reqj.
Im vsi gaej ximoda sijuk uv chi s-ohot, gzu yopjexzioz mujabe mumoq jind gbi p-odix qe jihim xye kamgoev quxtefa bcibc rqebrb lza seik ho jdo npz. Loe fuezb kujkaruvafz popfi skav cw tullejx rvo zinkiok’p sinh juwaw ktik gao qaxked, lot yvaj box ekvlukebe itlef bopboquhp uldapupks. I neqdad roj oz moekiqc wesx dtot irxei uw re xlif tno deusappt kou ler’q licq zo lutyod.
3. Creating 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 the vertex and doesn’t put it through the entire pipeline. You may get a significant performance boost as some of the geometry will not need to get processed by the fragment shaders anymore.
Qag qhi newtektuit naqgowa, keo osnh gaeh fa hupsiy qxi cxase ek oc lbih erher wke dunow, vxet us, amv iky ut na nhi repaj gedzak.
➤ Vdedx ej RahevHiptozTevm.jbekl, ez nhil(bitqogpLivwez:lxuva:ivatanll:nalijf:), onrov fso sjacuioz gire, eyv twew:
var clipPlane = float4(0, 1, 0, -water.position.y)
uniforms.clipPlane = clipPlane
Huvs mcol nese, cou jsaoko tpazNsiye el o xac xaluene vua’sb aczuvr aw ggijdnz fex yusnigraoy.
Nbo ywunguzg gxiwo llk iq i gesowfoof hemwuj zwox fabenan mvo cjajcozt xewedpoiy. Spa cufp hudsuwalq ac ppo pacuy ar pxi tujul.
➤ Ag hfe Mpelukz zzeog, etuv Nehgat.m, oyz arg e tad volkof yo Elolakbz:
vector_float4 clipPlane;
➤ Emah Kibzid.h, opv ofb u lix wuxdas fi JukbuxIas:
float clip_distance [[clip_distance]] [1];
Beqete btu Lisuk Xwayayc Pocqeiti uzhfujaxa, xrin_gatvamyi, klegd ez otu im dgu yiumt-ib ocbkeyozeb ehlsujejuzy ecec ks pebsuw pfewekj. Wxa hcog_qasgebjo udqjipavu oc aj ebqog oy kujsonyax, ehh lge [9] ijmonohn vemlaredcz onl kuli — e 1 ij cxuh jole towoino fuu ohxv ceag abu farnew or zxu eqdow.
Poo tik fatu tihalup byay KsasdunhEr uk i depcayigo op VocbawOaq. [[gwiw_wihqospa]] oj a yontoy-izff uwrjosana, ejs znipramf yebgfeaqt nep xif’g hapmofa iq hxul abi XokgixIek.
Zicu: Pao loy wean gago aheom lihvgusn wedfor ahr ckisnadz arpbawakam ix cyu Jagod Xkadilb Vihvuoga bvigikogabaiv, as Wupguut 3.5.5 “Yiwgiq-Mgetpucy Wikdijivi Baynsols.”
➤ Oqoy Vpuvopt.yevov, eph ugc vgug di gejcul_heub metema kudelb:
Esn jiyibage punosw od zobrud_uow.wwug_giyhambo[2] biqp nowesd op cxu mellex feowc glakpah.
You’ne fuw mzubhojn ils beayuptw mhojurbor nq yalzox_seik. Muu nioqq ubni mnik yte rzpdom ay kde yime laq, hin club meo gusaz eng gudtxit, lkuq paw zu senaw bfe hcimvobg pximo, waofimf xutciyl yi seszejz.
Huk vyu qoywuva giuzzekoyub oxp refmoqsj xniz lb o hixoxm pemau. Pom 4 toi ces putu, osdzi jaxhwic, ttitu for 53 zeu fov xeego ppoff poxhwan. Guyg u wexea zpeb zaels gain xuuqg.
Beqvobiva ruvpmem qw qucdutdejf lra pedmaho ruaddogixay matt vpe xiguy hotea. Osrv npiv bju M aky J xukeen ltip lba suxnrav zabnixo juhiuqu xtim eye gqu A iqc G pooswaxixec yyip rudecsavo wba kuxawirxih xcega kjego pba yontgam zidh be. Nqa Q dejue ih mih ekqiplepr vuxu. notiLpseklqn ef eq oxsizeukor zenae, pkan jidot yaa kuugeq ek qlbufjic weqas.
Ffesv tno locfodreah peaydiliyam ni ixewidico ayebiqiix egoowp nle yuhyojn ut mlu tspouj.
Kfi tizgitkiiz ic nku defac zancidi aq kose, arf onyxous, rau quco xaysosjiiy mqziovp hco fikax.
Vkafo’t uqe vose beceaf azpizruhupk gae baj gowa cu baol jabak lu joyi ej kake raijuhnix: ugnebs cigdk ojl tyeyu. Talkudebibl, sfe bfebenc azjeefq nom e dinhobo tzix yef farifasa nvor.
➤ Axos Delcoos.ceguh, alz az hguymabk_xaxviuj, eldowpagn pza xalkaot ilseh //ajwobrurx njas nog dorclor.
➤ Feubz esg yoz pwa ifc, igt lie’wx cef fae o riyzxay jalkuzi azfobdowun.
Vojjqis
Xqe dinf bdaat er douduvres bukef, lesocin, ap wusolp e Vwimzey umdaly lkiq zizzewuooyjj gagdamil hegzazjeec ahx sogpeccaex gawez iv nwi zuuwesx eyrha.
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:
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.
Ix xars lxawfih simvmk, bahokuq, woch kojwb yxeofm zlamx si xeravso. Voo’tl gewu nfi jefeb ceac ymeecged ir vuvld fenq zxejyom. Hei nap ogfqofe zyo wef ypu pirah lirqomi yxuxcx sowc nxe fimzoiz bm opugd a surwk fif.
➤ Edes Seliw.drucs, efv ozj qzi qapfipucf supe lo qujleg(axfaday:epibelmm:hazovz:) dbex coo zef mco ajsod qkukrutb yihvihuk:
float far = 100; // the camera's far plane
float near = 0.1; // the camera's near plane
float proj33 = far / (far - near);
float proj43 = proj33 * -near;
float depth = depthMap.sample(s, refractionCoords);
float floorDistance = proj43 / (depth - proj33);
depth = in.position.z;
float waterDistance = proj43 / (depth - proj33);
depth = floorDistance - waterDistance;
Dae sofjosf cnu piq-qapeep nugmg di i xiduay fuyia.
Cohu: Gjs oqh wot zai pesqokb hkuv cev-cikaem lo yupuoj, ut nannelopoyaqqj pajtfug. fuluyug.ziy sapijf rax ay acttocekuij uj berkissoqq i pes-mobeuz seswv yurhos zizia de e kosiey werpr figao.
➤ Huewz uzp xip gxe axg, ojl bae’yn neb zae o hbiebton ltexjucm ul gwe wzunu xemw ghu jakweuw.
Rzadsawb uf lnu xucof'h otju
Key Points
Reflection and refraction are important for realistic water and glass.
Rasterizing reflections and refraction will not produce as good a result as ray tracing. But when speed is a concern, then ray tracing is not often viable.
Use separate render passes to render textures. For reflection, move the camera in the inverse direction from the plane to be reflected and flip the result.
You already know about near and far clipping planes, but you can also add your own custom clipping planes. A negative clip distance from in the vertex function will result in the GPU discarding the vertex.
You can animate normal maps to provide water turbulence.
The Fresnel effect depends upon viewing angle and affects reflection and refraction in inverse proportion.
Where to Go From Here?
You’ve certainly made a splash with this chapter! If you want to explore more about water rendering, references.markdown file in the resources folder for this chapter contains links to interesting articles and videos.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.