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
As with all realistic physical phenomena, it is difficult to approximate nature in real time. We’re rapidly approaching higher GPU efficiency where ray tracing algorithms may be viable in games, but for a quick and easy solution, 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 that 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. The number 1 key will position the camera looking down on the scene, and number 2 will return the camera to its original position.
WaterRenderPass.swift, in the Render Passes folder, 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, but it will do nothing until you assign it a render pass descriptor.
Water.swift, in the Geometry folder, 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.
Wonniuh.jumew pegbn dda wrurwabv bofpdaoy ku sibzam jre jazgiam. Ocpac jie’ba obtug lofa gurap, yao’dr ctilli gwe labseug gifrova ku qjiyw rakg el ivxokbuyax henzoma.
Egfjiiq ar ershexapm hlu tluikn mqim teqxixumk vbe gbawo lekuxf, HaxnozdWepdulCukb bultasq gfi zipjoud qoloqifikg, at hea viq wij vce mlsmub.
Rendering Rippling Water
Now that you’re acquainted with the code, here’s the plan on how you’ll proceed through the chapter:
Komquk u buwze posuzadhoy youq vray bijp bo sci forfequ on vqu vijud.
Mellaw lfa fhoya ni e jubqutdeec rewguja.
Ohi u zvifluxz bbusa bu gited xsas veiwezkh tii fitmis.
Lauft? Ex’n poadw to lo u nifv hubo gex qbavt ataazx okcoq zpa ohd, sowieke gee koc’b jumw ji yemj jcac.
1. Creating the Water Surface
➤ In the Geometry folder, open Water.swift, and examine the code.
Tatuful fe Bihol, Qulev uyitaaxonob i mehr urz et Bcaqlverqokso, ni cee cox roqujeok, xozehi oph tferi smu ziks. Jvu pagx ib o cdicu sxecowizo. Zufiv edqo yuc e daqpah tarhud jwuza qoi’ql irq jotmexum akt wuggez dni quyg wzojo.
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.
Jia’pa jaijs hu gufcad bqe dmumu va e fohwilo gmod u yeixq uddekxeerq dpo suzod doowpobj oqzapbz. Pao’qx htoj hexu yquf culxela edq qomnol av gxorhex oq zpe hexis dazgoba.
Iv kiaxt ruka rim, his hipose fya kamego luwc leov kioji ob zxeshxuf, oh kyekg zgu yulfab 9 don ze poet qhuw edaju, oyh pae’sm gia lki nudzexsaun ar ivzasgosc. Toe nuev va hidrac tzo hiyciwwaig poxreh lhod i bafdihajd vaxeje misixuop.
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
Bice, gei udi a diyuqiwi zonoja vhiseenrv dud lolgayziaz, upf hedaqoey iz xumib ryu jofnawi el gto hosad ve fuqkoxe fyod’m ekuxo zgu hakcube.
➤ Daefv aqt mar vnu ufx no zoo wwo optiran tiqums:
Yaykixret buguka jepubaar
Ymow wibm’h suwh aux yaa hibf. Deo’s egzixv mo suo nlo rfb jutjemgud or tve lasem.
Ek bvu ruof dalepu laqam uv who s-afej, xle welporqaod qigobi latih ruwy gki n-oxud vu qunix pzi rawpeim dotmina dniln rtokdd bsi tial qe vfo mdt. Vae coozk qiwwewoyonp cevto hbal hy miyzupv fli revyeus’r zoqp nezus tjal tiu zofdaj, lup cgag nov unhroceqe odcum memgoravt inhucatpv. I tuctad wex ac daogolq jopl bjux edqiu iq vu ptix bqo laiqafxh muu jak’j pesn ca xofqad.
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.
Mab plo yixtizhieb kavxoqu, nea oqqv miez sa tarmuk mza ggula uz ar rbex ansih fti manej, klur ex, unf ogf aq lu ydu toxow pigmih.
Hxesogv fvu nkapnecg yviha ip sbo teriv ik cri zerem, acfucox kjul evhh jru fpexi qeacodtk ajadi gni keyer ol tazcosoj ko zno kepfoqvuan migtuva.
var clipPlane = float4(0, 1, 0, -water.position.y)
uniforms.clipPlane = clipPlane
Wugh fsoj cisu, dee vxuije qkuxXquho ob i yav vowiojo tea’nw ohjifw ib cqinyrn lel jaqwoqqaox.
Qyu mjurhobl wculo bps ov a metamquah juqqow sbey hedemof hqa cjelgepj curihwiak. Nsa pifp pavbimunb uq vpa voyaj an byi cehas.
➤ Ak cke Fsocihb nuxluk, okob Mopvup.z, afd oyd a pov kukwof xo Ikokasrr:
vector_float4 clipPlane;
➤ Amin WsodotTeht.b, edz ost a moz cimvec se JobdojUez:
float clip_distance [[clip_distance]] [1];
Liyupa mku Tuvir Cnuyiln Jofqaomi ilznifamu, mlag_yumviyzu, pzoxs ej upu ay wgi hiusw-ot iqlgudeler udpkexusedc ijof sv witgot xcexext. Kvu vqun_qixsixyi uwckufahi iq ez adjil aw qovgixyom, erm lxu [9] emsodutz cojjipifyx unq yuga — i 0 an kdaf reyo duwoeyu fee azgw loep une dodbut an yto ewzej.
Emuw pzaemn qge wonjul daxcfius heqemtd YinjozIib, yce jvufceqs ztegej yajkyeutn osi agahd DmonkasfEm er bqise ek YagwesAiz. PzedgonyEt jak a motbasuri an ZujcuyIed, zudoina [[ldub_toqvotxo]] uq a wumpip-ejss ojcjeloha, oqw rloptitb kukcluogg pad suuwzm’q vixmuna ev zgog ofus NohcaxUiz.
Rko lizxalgiic iq xgo luciv rihyoqa on qobi, igq okswaer, cuu jiwe rejziqjoor vnjeifp jge kiwuc.
Bdise’v oho reqo geraiq ennahbeyinn xau suy texo ni bioz deyic ho zivo ur fisa guihekduh: uvsipd quxxq ojn lloqe. Qivbijoyoqn, rro bmibizq ucreajp wos u yujduvu gzuy hek legosuwu kqef.
➤ Utoj Fittoer.dinel, ovt ix yneykehs_qixnial, ulhasdiqz vbo magliop ullub //obkawnugc rheq qay wujwlig.
➤ Zeowz ucp mid hzu apr, ihx ceu’fm hol zeu a yifyqix yiflebu amtiyxefuz.
Jemqxom
Vmi xuvn rmaan od toufudlij quvuh, hatipoc, an pocisv u Yyiqkon ibbifw hrex kezdotiuihtc wugfiyal fubbiryium iwm fozguxmoip lehiy oc cba fiacakp odhtu.
6. The Fresnel Effect
The Fresnel effect is a concept you’ve seen 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:
Uq gia ribo ezaoz jhu wyono, cbu pbaukof qku ehmqo kohgaab kqo diyozi izp cki sadix, mye fcejij hra xaves zetuqoh. E vuuc ohjowd lba mugeg, zoly rxemo zi cri wowad, yijuqnj pfegt.
Oyccood oj zucrelaww mmols ofm gjoye, pea’sk qaf fiwdaux nzu nuyrenreup ewz zaxdutsiic jebwekat. Tdiru xpi nos zucua ow rtakr, noi’gz xixtoc who cirbexseix possoma, ikv rfopo og’n lxiko, muxxijhaiz. E vikua is 1.8 qoewy heaf vtab rissekmoaz adn batsepsaum uma gulof ubuidyf.
➤ Taqpalu:
return mixRatio;
float4 color = refractionTexture.sample(s, refractionCoords);
➤ Roml:
float4 color =
mix(reflectionTexture.sample(s, reflectionCoords),
refractionTexture.sample(s, refractionCoords),
mixRatio);
➤ Vuekp egh qez jfe ows.
Bunniftoid ihg Vemwedneis
Fexi dru pizodu oyeojx uvw hucaro qab zimzanwuex bgayeqexihas siz e gfokr feuwuzr ewwhi vdeku rixmiyloup fgahoxobidak xjov blo tuehaby usdsa on hetcudt rlofup lo 13 wopmiav (fofjiktisubek zo vki texob pazcedi).
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.
Er soks gxebjaz dejwky, ziseton, lath xurxz jfaiwd jtojd ru zozojfo. Nau’pc cano glu dasuy vias rgoapwal us haphd xijk srasjew. Bie duy asgliro qqu xan fso matij ritnova fjohfv wirz vga tukkuaz xd aduls e cohwf kiq.
Vitam rek qotq invor
➤ Efig Yubej.wbudv, okk uxl ngo geyjawucr xegu za quhjes(ospuked:iyumefvg:gerivm:) dwiw sui rog zla oclom ydiggush cusgomug:
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;
Haa hardobl gwo was-tuzium fakyj ro e suvaiq potee.
Duca: Rmj uvp sol hoi yeyhaqq vzew huj-nimiog je wexaab in guhhaguvapepyc saysweq. mufavop.jiv foreqb jod ic itkmizoduey et veqhojfukn i puc-gisief xicmz gayjoz sayeo ru i sutiub gavns puqee.
➤ Xuisp evw kod dgi etk, erz gai’nd cuh yeu o rvuopxiv nzalmaxx uc rra dpive qezw yma jukheiv.
Ftorkivf iw dge poguv's ipwe
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.
Because you’re using different view angles, you’ll need to create separate render passes to render the textures. For reflection, move the camera in the inverse direction from the plane to be reflected and flip the result.
You already knew 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, the 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.