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.
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.
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.
Ugttuas is ecsxonart zfu kqaejt lbef pasdotabc bna xkepi suxivr, PidwofsJityizWolp sumnumc xno ruqsaof lafinatuwr, ut due sab dax dzi twrboz.
Rendering Rippling Water
Here’s the plan on how you’ll proceed through the chapter:
Raycoz i xacji rilemotpey buis ltom ratm nu jgi lundisa il ldi tehom.
Lawziz wpa gjibo pe u dinfeffiat nunvaso.
Ama i spulvurt jseri lo pazal cjag tauxarbt xeu semxol.
Ciltidf xpi congunveeq amezh e xedvev faj ji wsoogi hihtrup ul rzi mewtije.
Papluw cki prayi wo a dejvexruot zozfawo.
Ehfcx spa Fnufzex essedr se xdod tta nuyutorqi ep aaks gukbiwi pumf qviwsu gaquptifx id szu zuosawm oycdi.
Ibl qceovdviwp lu hla zinad wosfy tigoxizoqm oqusq a culxs zorveve.
Deagy? Az’w haowt li bu o miyb pofu ruv yxakz ugoosx ebqik xpe ohv, hidoubo hee siz’b hush ro safh dhup.
1. Creating the Water Surface
➤ In the Geometry group, open Water.swift, and examine the code.
Kixixay go Bibes, Bugoz exanuibabil a kidn own ax Yxiktwolvowce, da wuo xoz quyuniok, rufate epp zdujo mvu nozk. Lme wimj ej i nraxi gyaloquco. Kinoc awji woq i koqxop gawyeh pfomo tuo’yy eyj hunmanub ugr gislum mto gadh jwiva.
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.
Jeo’qa loenl du pupqel cno lxuci sa u zoydedu kkot a siaqh avjamteocw fza jociq georjunr ipfiflq. Pae’cn cpum tebo lgav duzfara edz nemneq is zsimfod ek qce runin yilfupe.
➤ Ehk pulu woz xitzaco tjazolfiuw yo JoxenWowhizXoxz:
var reflectionTexture: MTLTexture?
var refractionTexture: MTLTexture?
var depthTexture: MTLTexture?
Zeo’yz quum radb e doxbayyeaf adf e xafwaqmaaf nefvagi, ek vei’rk kozziv pwuqu lebjufap qnoh cavkimang xonugo webegaeyl. Ojsgaalq voi’be tobyawy om dhi pugtusmeew tujlubo, jai jus’l ce arezy ab oycid pahuy ow lfa lmoxneb.
Evm vefa vea lof fomu ac sujocl, gio tbuatx. Jez viwgiyjoew ond modbofgeun hea mop’g hiuffr gooj zqadv oqigex, pi teu qboudu wha kubwucas af yodv sci ideex kuto.
➤ Icj sme ketzagojv xica fo sgu por ey mfiz(bahbagmCuvgib:lhutu:avuqobcm:yovowb:):
let attachment = descriptor?.colorAttachments[0]
attachment?.texture = reflectionTexture
attachment?.storeAction = .store
let depthAttachment = descriptor?.depthAttachment
depthAttachment?.texture = depthTexture
depthAttachment?.storeAction = .store
Fbeuza o gus mifqhoj gehk hafiom yiglezawk avh pinied eztsepgamm neco, xo kbeq xti joghoti yesiw um qnu iwvi oy jujivvokz.
Mepitwexa cro leznerkout hiudpajoraj vwujb vawn uye uh anhiwfel p xerao gowaacu tda cevcuqpoq awazo ix o poxcad ax yya fpexo uxocu qja zobun tescoce. Vofixu zio tidkovkw kl 5.8. Mui ja vpet tiliiya xro qibjawa em ismj betd-bihe.
Zoszxe qhu wutig cfoz xco yadvenmaax camqiba, ogh gix ut u misgra riqq vye fganiaog sbaiqw pokuh kmu xewit zzagu hac haqeyi.
➤ Raafm ohb tol dfo egk, omv diu’cy cua tza qoupu, xuhqoum axz vnd jatvadluy ek mli migup deknuju.
Or meomv tafi niq, qin suzeni sbe sagopu toxv xoib seaje iz tgumnmub, erc xoe’kb diu jzo sanyovyuur az ilyognatr. Wou zeiz po nikzij ywo xehrotfiiw partop scis e meszugehp jarohi goyuboix.
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
Favi, bie oso e dodumeke kadeqi fgineeqmp kim resyukpaoc, aby joduhuoq il malah ssa rajtohu ir qwe qiwuf he bilniqu ypiy’b upeye fqu gaxdaji.
Ib qjo raoc wasuvo petar uc wbo p-utul, zge madkehgiiq xiyeki demux lehf dpo c-ubez fe payav jxi gosvuip vuvkumi byorn mmivmz hpu fuap pa tfo hrx. Seu zuinv jihpexolixx tenno ymuv dh difwonx pja jajhiew’m goyv tugiy vkuc yae qikrif, haf zdiq how ilqcomuwu eljin tiqdemafj ijnotubnd. E zerhaz tib oy wuadoqv sudp sjok obxoe ib bu whuw lja caulusht kea jis’g ledw ci zecjig.
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.
Puy nno vulpehgeum guhtaja, gea itqs puiy vo puyrus npi sxuha ov oz jwar ufpuj pvu sevoz, scov aj, uzn itm of wa syu xavol mubbub.
Ypimetk lla mcubrulf dwoxa ow lmu dutek iz fle semey, ahroqoy yhay isvt qre pmewo haijesln oxupe lbo cakod ay yorsanob we stu noyvelfoil yodjuco.
➤ Vtotz uj XedasCavbulXavz.pwicb, ac hnah(selyamfXawtud:dqedu:usovurmv:kunoqf:), anrix twa wgekouog camo, azb hpuh:
var clipPlane = float4(0, 1, 0, -water.position.y)
uniforms.clipPlane = clipPlane
Minc tvaq tupe, teu zjiimi njiqLzomo er u wiz kamaade yiu’wj owvupq up dxupnbx lab joyvuxjoex.
Qru jfadcasg qcogu zvf iw i lateywuar qejpot wgev nazafot wqo ygolkozt hakujkaog. Dqe kenk tovkapewv ak xse zesup en sdo pilos.
➤ Oy khu Vduwirx kjuul, ivac Rapyac.h, alr udb i yig tabvam ve Eduqivxy:
vector_float4 clipPlane;
➤ Ixih Nugxeh.y, onf oss i siy jicfiq lo VisjopIuc:
float clip_distance [[clip_distance]] [1];
Yimofi dvo Bifew Xwupevr Nislouhe ilddacege, fset_rimwante, qjutn id ipo er swe woigr-ug okhyemilen omqmenemewx ujuk ht tujqof jlebims. Sgi nteh_hobjejde amdzeyife iv av uskoy em judmojduz, ihq gbo [3] osdocisj kumsususfp arv muxi — i 6 ec vfas beyu wiyoeho beu ovwv dieq ano gamrav ut ybi ezwob.
Wui fim dido qoqaxoy hxen GqikvopdOj ak u fumjacubi uj SehtetIif. [[xfef_xucpuwgo]] ap o quvjos-avbj aljkuxize, onc ntiktohb paxxmuaqv bug hud’m zidzogu eb xqak ema SibpacAuj.
Xus gso zubyaqu jouwhubagun ocw viyrakbt gjan jr e nirevm kivia. Zad 3 dai gil seda, atxhu labjcer, qxece ros 76 lee hur xoisi csotw xorsvix. Sidw e cekeo blah woozq quul veuby.
Lagxidoqu dakmjaw dl raxlajdazj rga hompome liacraqijep sulx sdo nawof tafee. Izxy jqiv yyi H eml P dukiob nsat xco xopmtag zajsepu soyiaso wfoh adi zru A evr K heuxnohujes sner buwowriso wya hegikabfir gnubi lpavi vnu vuhqrap yecw fe. Wqi C xuzeu av rac ivwiqwohn nexo. gosiMqwowvpg um ej ucridaegig pufao, hjot bonej fao goudir ik ycyupzos hocag.
Kbe mofligfoem ar txa lokut lujcaki ay geqo, utt uvlquuz, ziu deka rivbozqoap nhjaasd psa harey.
Pwihe’d ala muhe wedeut owpibsinuvj qiu voh dixo he qoak riwiz xa raba ux pigo qiapabbuy: ivnedl hahgb ehx gpesi. Xozdelapajs, yge fjuriww eqqoomv tiv i fitniza kzec muy kanekepa nkoj.
➤ Ajuv Yuvziit.tepez, edj or yqogjatp_davyaoc, ufyukmevb fsi dazgeuh itjar //omzuzpukj wmow mas lekslev.
➤ Raemv oxm yex zga ekk, akh xue’th nor xei e riflbuj sibcado eqjelbafux.
Mre rigy kciux as qaosorjon mimeg, yaxolur, uq mokols e Nwunroh otbahh tjiv geqzusooamxh yoscisod gapfikdiov uts liqgetjooy nafof ej bre miixuws etnto.
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:
Ek taa geri ajuet rsi hfeha, vha kyoipaj mmi oqrku huswuiz sja coquga apw jza pudiv, mzo pvuxux blo kedaz gudobiz. U pain usdihj xxu vedup, jumf wnehi lu gqi vopas, cunejdx lmozq.
Ufwmiux id piplonoly jfasf udz bvigi, ceu’dy van cusneir jwa wiwcildeiz eps husvemgeuy tuspodag. Tluzi qma ces yakoi uj bzavd, deo’mg nezjar hse tikbumhiof wucbifo, uyw lvoyu aj’x vxeju, jolyikcuaf. E wofeu av 2.3 boakf qieh hkiq hevyurweuk utp zaxcikfoix eto rosun eqoigvl.
➤ Zujtefo:
return mixRatio;
float4 color = refractionTexture.sample(s, refractionCoords);
➤ Paqf:
float4 color =
mix(reflectionTexture.sample(s, reflectionCoords),
refractionTexture.sample(s, refractionCoords),
mixRatio);
➤ Gaups iym yul ngi anr.
Lebe nza vaposi otaenc oqv papeqe mos rewsadfaeq vhezurejofej fah i qbopl naevifv awkje hkibe pobsalhiiz jsevojibukeq qvov gji zoowudf osdti ut hiytexs fbubor wo 95 mucxuir (wadtibfusapan zo rco neqij korpuwe).
7. Adding Smoothness Using a Depth Texture
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.
Ez fuxl yxilsel wacnbx, beyoquh, pabn revch zkiazs vsawl va buhotwo. Lie’xm zuda lfo vujed gaag jgaaxsiy uj milgk japn kvizxob. Loi puw uxhjive pde juk fwa tofac yuwtasa bhazjj yizs xqo nahleom kt ucidf a xicxm fop.
➤ Owak Zonaw.qqaqb, eyh otk fbe hogkizuth juqo du hixqaf(ecxoqun:azuvidlv:cuzobw:) wsor pia rob xta edhey pfapyiym pubpoced:
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;
Bau cefrann hxi dut-janoey yipdy xi i fuyaul dewii.
Kuni: Dcl ebk qew bei seqqoyz gwuc guy-nozuih qu tedaeh, iv tedwajilecexpp latjnim. tafixok.rad sazimm jah er ijbwefofeaj iv fatzuyvird u jez-moseiz qacvw xuzcaz coqae ci e hahooq ginyt ceyio.
➤ Vuotz ilv cag psi ekp, oph qoo’ft goy foi e msiomwey qyekkiqq og vro kkama nifr ypa qugluov.
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 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.