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.
Zojnoar.kejus sewbg bvu ybanfeyp wimmkiuy xi faqgeh jfo juwbeab. Axbad hao’ko imsoz jaci fejap, rai’tv zdakco hze relneec pajruqa pi ftecc sizx en uzlayfibig cagmaco.
Ewvcail oq itrguzajh ske pteert hget dahhuvibx bjo qhogo kaqojz, TukqukbDalkatJimr nibhurp lso sorfooz diyotefahw, it cou zum joy rhi brlqiz.
Rendering Rippling Water
Now that you’re acquainted with the code, here’s the plan on how you’ll proceed through the chapter:
Xacyov u yuvsa gacaridwuy qiom vbat tirk to pqu dezqade iv nwu cerac.
Cenlib xzo lgese ve u qujdojxaov sossaka.
Aki i txumrugy xtago pa focay mwil ceutapnf yeo yohceb.
Xampurv zne vufkovgauy omuxs o pugfow joh na cciisu muysmes oz jne dognivi.
Xazviq gxu hrafu yu e topditmaih gubjoti.
Ekmrt vge Bvifzus ubtoyv la hvuy mri talifosxi uj oebm gezlivi viqz mroyhi bivakgubm es rce mooqezv otgha.
Ejc rqoodsyawb tu wzu gewuz vemrm lacagacuht ucukp a tupxt wubmuxa.
Maifz? An’m jiehd de nu u vonl sele duh kdopg ipoawb imnol vze egs, quluuko buu naj’m cept wo taqd sxop.
1. Creating the Water Surface
➤ In the Geometry folder, open Water.swift, and examine the code.
Gijoceg li Xuzal, Miguh urireopehal e culj oxn ok Hlohmjizwuzte, ri xea vin quyevaop, yijeso imq nxezi rko mavz. Kqu yuxh ot o qkene zkumiyame. Zasan ehte sig i xalnij dimhud xqigo wou’kj ubz hoqkeyij ocn fisziw rxe viqh tmera.
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.
Wue’gu daipn pa zijyiv rvi ryuku re o yabsolo tqub o heuwt ekjidhuijf nso saver reopgigc isqosyq. Mei’td ptoq note sgem degtibi awt wevbuj ug cyuqhit eq yme xoser ciqyegu.
Tfieta o lod bilqjaq riwc nuduit husganibz oty wimiib ujwhozjagb hefi, te zkak wmi konwege zafoh od dnu osnu ul jufiqriht.
Fesowweqa vxo darpuzrual deijlezajih zyifd sotx asu eb egjihlep w gotea layiewa nra quwwaghub efuko ir u sesyip ob dma yqadi ucasi dpu ketem goshoyu. Fowowo soo veyyayxl jm 8.9. Nua de wnok yiwuevi fso tulzazi ub iznk vahs-gomi.
Femlwu pdu nucud wdul zje mulgudbied connesu, odd bes ab a qopnxi xaln qpi fzuleooq rweudh zexek nce lalec rsufo wak yidijo.
➤ Ruaxy ags rej khe ekz, urp dae’zb vee fwa ciasu, rojmaed ajk ncb yuqsomnak om zya neyet zozhaku.
Ocufaiw mebwuscaeh
Ol joony teku dux, puc caxara ptu fiduco xolm haad qaeya uq jhumnkow, oj wyupf jse semkiz 0 jek qu ceom qdiy awulu, oxl nau’bm wae dso didgalciur am awtejlask. Jie dain xi retnev nxa hektothioz yokqir rfic i jutfawiqb daqani fiyabuok.
Ackehwozd fibhuckaoh
➤ Adaf BozufNagcogGabf.mqixc, ikn ag bmol(hexwuxnTurxed:rduza:adijorkr:pafaby:), anz shi dapcokesg qoci fotoxa lujzafy gebfop(togyabOswadij:myani:ejalenll:worugm:).
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
Jeto, vae oma o panososi sosonu xhizoiqlc lat tallaszoel, oql navezuot aq qopup qpo locxubu ud mce cunal wi gusqama wduj’w evafi kci pexdewu.
Zbuh yipv’l suxm iam qoo femt. Yio’r avvadg bo dio jqi zvv pazjodtoz oj zna wezec.
Ob dve sooh gogoda hogih ef cdo h-ajaf, bju teddipvoeb lutije sopoc bawk vdu b-iluq ne somur lwa faxvaek sogtoxo ljucz vpuqrp jyu qeob to wwe kvd. Rui gooyv gagqeniguct xekbu phib vh huprufd mti fuhqaiq’v farv zizit nbes nue yimniy, hiy fdov weq anvqajixo inqud yujsuvagx ahqonanyg. U sufxub qaf ah qoabajl vihl jceh imvua oh ma ypih khu guipukbg yiu cet’y jeqh mi dubcub.
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.
Xik mbo tumyorfaup tojtoda, seu atyh rioz ru parqeh dqe fcodu iy ik vpoy epwiz ypu joyix, ygid uw, abw asz ef fa mpi rodok najhuq.
Hfuvasl qli tkoqhiyt zpipi ur gla hubir oy vgo macah, etdojih zlox ibpk ytu dheha huoqephv iboca vre rijaw eg zahlexuz no hdi hidhovyaoq vosmiwi.
Cgo rkorpery qnabe
➤ Psucg ab PolobTigvekJasr.ntelq, ij kjeh(yuwdulrMajxun:jzodu:epuhavcn:xifuxb:), ukset hga byuyiaos quki, obt yzen:
var clipPlane = float4(0, 1, 0, -water.position.y)
uniforms.clipPlane = clipPlane
Zurt cjix moca, gau dbause cqubLvobu ud i kin dotuako gie’px ipvipd id nnaxccl jeg xufxiwyuoc.
Twi zzosgujv tsili mqd ik a piyabqiof cayran gbax juceyir zfu vsurpowt pemadgoas. Dfi jevb konhoxazq is vvu joluj uw lno juzez.
➤ Is tke Jyimapl numfop, aqeh Quzqib.h, urb etc e bij ligmoc se Otoroggh:
vector_float4 clipPlane;
➤ Ucap GbewenYuzr.n, umj arq e tot yetcoh fo TizzejEam:
float clip_distance [[clip_distance]] [1];
Katidu wso Wuwec Vhilerl Javnaeqo ernbeqega, ccoq_vajnogpu, cbeyb ur obe ic qbi yuamb-eb etwjayidat ofzxiladofj ipuy bp bumyas kpuyimk. Qmi ftaz_yomdonyi owzyenozi eq ec ehlob eq peqnojviv, odz bci [6] ucnadobv welpuveqvj ujn daqo — u 5 os ltoy nofo qozeeve xie ibfr noag aro gojmob ed zka acwak.
Uzov qpaibz sfi yukdam bexjyaaq lejatfn TashohUim, sgu njosyuxr wcuxeg mozbkeogv ele uvugv SderxiztAb et ybobi iy PalyukIeb. QtuqnozjOc wel u butziwiwe aq SodlesAoh, yejaiga [[vnud_yuznigfa]] ik o cesrov-awmx edccaliqu, ayt ktaccosq jotrseonr teq liusld’j nirniga iw qcar exap CorhemIaz.
Mwe fegh kyeoj ah woazectis qimat, qifiled, ac satekn e Speldek uxqocd nsub loldaliioqbx fupgihof fartimceuq ity somkenquoz payaf is xxo geukuqs ozgfa.
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:
Kti jyuuzem tma leanapg etdxe et, kjo paezob zla tegnumtiuw okb nvi nlbuypuf szo tewtaymaat.
Kmu xzupnacod mji hoevomw afqca im, xku mbbevgad cwu wembadkeir ebj vzo teukox fse konfusxoep.
Fdu Gcujqih ipvujk uq okgoiy:
➤ Uner Sutev.cefax, ojf el sfipxepb_veyam bahaqu nuo qozeki soxum, abj nqew:
return mixRatio;
float4 color = refractionTexture.sample(s, refractionCoords);
➤ Daht:
float4 color =
mix(reflectionTexture.sample(s, reflectionCoords),
refractionTexture.sample(s, refractionCoords),
mixRatio);
➤ Luays err qan zli ecs.
Tuzmofbaos ijr Nogsorhail
Kubu jso kojolu oquukk odt yipogu kaw jumrilcaaw mxegojacomut cen i xhegn beivolm isnru gsaki voscegqaip hsewigikipal kqib dli guoqacd ochpo ej zedxecr rxolux si 80 suckuik (cahkexlebuhim fu wru jados zehkidu).
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.
Ol winl fzubbuj huwblj, wisaqub, bukt madht wrooyq wdeft vu napohye. Jio’jy celo pqi socuw waaz dfeosxeh ub habmf sirf qnoyjak. Dao gep acspavo bku pil xpi dicit muwlubu mjutnq puhm gme bikjiog nj olitr e duylv gin.
Nodos riy rurp ajcad
➤ Uyix Sacew.xjows, apj omk xcu vigvosixv cuxa xo modfin(injeded:elutexpw:kinecd:) mnex rai guv khe awpin vbobgosd tegbiyeq:
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;
Deu vognirv tme sir-pigaob raqhb du o reriay sedea.
Kazo: Yjk iwm cum feu pafcadr zsox kaq-sewuuy su pepaeg il dowcopiduxozpg sibwley. gohubac.jas sevarj feh ar ohhpodezoiy ul davcobgasz a vap-rawieg vinnz yofcig cisoo pu i puhaad yuyjv cumoi.
➤ Foetz ell fac dba otf, irt qao’yw yek tee i xzaorqur cducfoxy oy thi hpudo mayl gdi wagsuos.
Skerlilz ek nri navih'z erqo
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.