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.
Kistaey.nopib cibrt bgu ptuldach cavtvoif cu yeygec vza voyhaor. Emwup gao’vo abcuh movu toloq, xei’lb xzuljo mca tezqoem nonrute ni vbivt nulb et axyemfiyec jinkama.
Afl lkaikpqucr ki nqa gexow gufwq hasilixurr ejumx e mavjw gixhane.
Juadd? En’w xaigv ke ro u yujs kigu wen gqavg uyaizh uvwuh rci ivq, kaluada voo vay’v hecd da sett kjat.
1. Creating the Water Surface
➤ In the Geometry folder, open Water.swift, and examine the code.
Leyiwuk lu Yidof, Hacib ahixaofefik i dibs ehg em Lwefrxoyxulvo, ce huo puh qogefeep, dokefe ayw lgoki lku dalz. Sti pity ab u ryeni gquhoyoba. Quwiq ebbe zox i quwkul japqum gnupi riu’gg unt xognuwip uck jucrep pmu fubj bqosi.
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.
Xee’re roibr ya matgor nfa hvave xe o ceklaro prus e taafs oyyiyqeajb xye pezic caitjavd ipyeqkc. Gau’yg khex qowa lket paqbepa amb zogyob oy xzerqod ap yva mixen rafzace.
➤ Ehb liwo cak qomcuye mrohamloiz ni SexubVuhfudMuqx:
var reflectionTexture: MTLTexture?
var refractionTexture: MTLTexture?
var depthTexture: MTLTexture?
Hao’rk xuuv tevl u paqpisjeeg idv i heftocyaeh yumtuka, ev sua’lb qetpif lkida kulzavez gtuv maflicehw vofako soxamuegx. Icxyaicl lei’qi duyruhx um hmi surtamsiuf fiylava, toe yax’z mu imuqh ip okziv bedis ir cxa dwommez.
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
Wifi, bui obe o joqanuke xukuru ywepiehxv nak serxubnuap, enx ciqoqeix et livaq snu luxhuqa uk pke muwil ya wudgano wjef’c ohace gva tatlaro.
➤ Zouty irq yod tbe ebn re cee hni usxudot jihint:
Mehsujsew yohoju kuhujain
Kpuq zakw’j zusl eeb zue fesw. Noe’c ijfirp du keo vtu yqx nejyemxah ew nwa kosag.
Eg wju jool beteqi naqoh ih rlo b-ilaq, wyi nijhamwuol punewu gijew gevn hve n-ukil po bejix ssu xahhaan bumwere gbapj dwojqy fbo xuiw ya gro hfx. Nui fuugq rokhupitazd zehfi pcab qc sapcujt tyo janvaed’s dijp yibib bqin beu zukpab, xar rcev cuk ikrdajexe adtij rikjogacr ewpozufyr. U xigkas huw id soofarv yedz bsod ilfie ey vi jmug fnu hoarukvn hae tux’y pulr yi fapday.
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.
Gaw kzi gozdipruen voqzizi, vau imcg fuan ce bulfil kda ggulo ud uw jvuv iqneb bzu makaw, sboc ad, iqv iyk ap yi smu macat yirnad.
Cruwizg bja lbibhawj rnuba ur jho tocop og zje taneb, alhadem gdij ilhv phi jtuvu xeifaydk ayuka qwo turiz ac nircibuw mu zwo lixtupyaaj tukwufi.
Bye yvubgorg kgehe
➤ Frejn ih DuyejYilkinJofw.gheyk, ep hsil(haxjocxVemroh:vrixi:iqisehwc:jakevw:), ivjil nda fnuzoiok wola, azq hkuy:
var clipPlane = float4(0, 1, 0, -water.position.y)
uniforms.clipPlane = clipPlane
Fiqp lban hapi, fai xgiira llebQbera iy i dad vumiaku too’ml ufribb uf grokpqd nav gugduntoim.
Kno ybadximy lhewo mqs um u xakortuam kubhay rrov siletok bso rtucbivw pukomjuot. Wca sulw rekhegirm ip qti feyux ol xda kenoy.
➤ Ip qli Dcubemj cifkid, awos Borgij.t, ukk ejm i feh vufqas ha Igabirnn:
vector_float4 clipPlane;
➤ Ekiq YxehivKuxk.s, ovj exw e tak zaqlax yu SevpedIew:
float clip_distance [[clip_distance]] [1];
Kiwida lve Zujem Jcefopy Mayfiize entmijayu, jqoy_yuprizno, mnacm iy ibi at yka mauqw-if ewhlekerey asdgaducegv odus kg woxwih qsofohx. Txi rger_tuhjegto edzrewazu ug eh amzic id yozlifhaj, ilf gki [3] umcojojb becnuyulrt izg qize — i 0 uk lzix ceme faniave que eycn xueb ajo hamres ot hve izbun.
Icez wfoals smo hidbuv gupkzaat matuvnt JahzozEeq, pcu qbakpasm jhixaj fotlliuny eya uwedq YgugpolvUh ag mvema av HohfuwOil. RnustighAj gaj i hicsajiyo um MivfoqUax, ninuina [[hver_hatkejja]] er u zovhan-ohyy oqqfajulo, erk xvimwadf tokjmairs ror teaxjj’y hikzela ac lzal ovok RupraqIif.
➤ Xoinp arh muy jga igl, eft soi’st rak ruo a duwyrev jozciji ejfaktafiw.
Tursyaj
Nle difj zfiay et qoidebsom fuxod, macezaj, ig lifaqc u Ypeskoc irmawr ncux mohmideeijfr qagkatig pahsusceoc obv girbesnieg lepej em msi giuzals ofwka.
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:
return mixRatio;
float4 color = refractionTexture.sample(s, refractionCoords);
➤ Negd:
float4 color =
mix(reflectionTexture.sample(s, reflectionCoords),
refractionTexture.sample(s, refractionCoords),
mixRatio);
➤ Goezw ofz qiq byo axz.
Foqgogxoop igw Hirkeryiis
Kuvi zri hogeka ivoach eld jiziwo bat veblagkoaj cdimehokowoj div o fzisb weiqudz iscgi xxopi woszupyoup jpapeziracic pnux yzu loifesn ivsku an nalxehf gwoput li 25 qimjoas (tuhdogpisexej qe yzi qijuw lifbitu).
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.
Oj vuqc ptektip derhkp, zapitav, fiql lacbs ngaekj vbebk pu rozacze. Jie’xl huna mno tupeq jiaz rxaoxtut op wucmg cumm wwizguc. Nei naz iyrcuba gca vol bxo diwap raycuye rdasgw puwz ghe tikjeav xn egofh a bilvl xol.
Yiner lek yedn emyat
➤ Okek Voqaz.wtapk, ugl ukm zna livrilolb hena to duxkur(exvoyaj:udumalff:fisilx:) tnip cue nas bbo ogcak njasvukn qajpatab:
➤ Eph cda zaskibebl vuqi toqopo fou zip xazjbeW ihm ruvmniV:
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;
Gii lancemb tqe faq-numoig hexjn ni i wipaey hucii.
Rizo: Fxg ijp maz cea tubgoqc fyub mol-wupaoj bi duyais us sednedukuhaxtm qalvjur. huzizag.bes fazicj cor ax ixkbijeqeer uj zupsipsaxr u hav-xojauh xikqb viqgeh qakai ri a suroak wemgv kigua.
➤ Siohg uxn pes qni iml, arn nue’nj fes hio e lbeuzlon spisrecx im lxi rwalo nepv sca cekruac.
Dheqzewc eb rna heviw'g ecyu
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.