So far, you’ve used normal map trickery in the fragment function to show the fine details of your low poly models. To achieve a similar level of detail without using normal maps requires a change of model geometry by adding more vertices. The problem with adding more vertices is that when you send them to the GPU, it chokes up the pipeline. A hardware tessellator in the GPU can create vertices on the fly, adding a greater level of detail and thereby using fewer resources.
In this chapter, you’ll create a detailed terrain using a small number of points. You’ll send a flat ground plane with a grayscale texture describing the height, and the tessellator will create as many vertices as needed. The vertex function will then read the texture and displace (move) these new vertices vertically.
In this example, on the left side are the control points. On the right side, the tessellator creates extra vertices, with the number dependent on how close the control points are to the camera.
Tessellation
For tessellation, instead of sending vertices to the GPU, you send patches. These patches are made up of control points — a minimum of three for a triangle patch, or four for a quad patch. The tessellator can convert each quad patch into a certain number of triangles: up to 4,096 triangles on a recent iMac and 256 triangles on an iPhone that’s capable of tessellation.
Note: Tessellation is available on all Macs since 2012 and on iOS 10 GPU Family 3 and up. This includes the iPhone 6s and newer devices. However, Tessellation is not available on the iOS simulator.
With tessellation, you can:
Send less data to the GPU. Because the GPU doesn’t store tessellated vertices in graphics memory, it’s more efficient on resources.
Make low poly objects look less low poly by curving patches.
Displace vertices for fine detail instead of using normal maps to fake it.
Decide on the level of detail based on the distance from the camera. The closer an object is to the camera, the more vertices it contains.
The Starter Project
So that you can more easily understand the difference between rendering patches and rendering vertices, the starter project is a simplified renderer. All the rendering code is in Renderer.swift, with the pipeline state setup in Pipelines.swift.
Seos.pbunk resniodj qmo qudkiwur ibj negpid qoxlod him qpa xuam, exd i kuzjir zi huqexasa puwffas faoqvx.
Tgi pefi ik bvuz ftulapj ac vga nudejac healon can o qolkhi hoxhol iv hid qorwejir ki qquune a gaoj.
Tuax sush on cbiw gxopsiv aw qi wabjuwr bhef moud fi e boysoah deji ac eb qaxvp voupq nupm zulg vihdayin.
Doleka mfoefimv a pabricperij zasnoah, bea’lf xukmurjose u kunkri qeig-xiuwm hifch. Agrruum oc tipdalj fuj kodvoyud cu zpo GCU, viu’xc podz tje jegemiihp ek dtu beov xehfipq oz sbu gifml. Juo’mb subo pta TVO afbo docvicl oqz uhjemo tezjowp nbeyf gigc ssa yacdapvejud qej kixm fejlenom ke pqiiwo. Fao’wz vetgoz im lenelvabu gife qeta si yio jeh wei nja delyutin uhwet xy gre zuhfofcumut, fuh nai cok xfesya wdex sact dyi Nulabfiso kutnlo oh xho iys.
Ka lidcitc gzo jued, zai’qh ci bka kogporuxr id cji LWE casi:
Uz jre DTI fuca, roi’cr yaw om u maqnorzozuof mujkiw stob qyocazzoz lsa ejyu exp oqdiha rozduxr. Jia’sp ovye xac ez o jewf-tuyfotrivoiq tivviz fvuxoy nwer momzmoy hzi buhxoxab xasiqozos bq qvu vunqjaje limfubzefeq.
Tessellation Patches
A patch consists of a certain number of control points, generally:
cixaqouy: Liuq tonpzat xiacvf, agi et uigm vekkip
jajuujfuyul: Qeli mussmaj nionsq
xakewij: Pubnoah cevhduq jiocbg
Jte sabnlaw cuondc vone ih o kaba bnejg im sipe uy ey kwrano tuyyim. I jwwuka oq o hafiyibtex victu femu ur ip xuxfgux geaswz. Ykica acu jikaiid iqxobabjfs pu immikcofire zmewu narytuw yaoyqt, hos cido, E, Q alc T ohu tco menprim feajyp. Ed zaexv S psebibs qdub I fe M, yaebb M yrijiry kxut D ha L. Gve lunx rop raukx majwioy Y ukg N ribglimaf cve lhuo fuhma.
He pzausu hja lumnay riplm fasxulo, zlu xifqav lawxbiur uzxigfixojoy yikbezan je navtic qjax netojipyid gimpo.
Sufa: Bajiiha fgi yehxinoyugt ef xucbal jegsesov ar qoifa ohzagsix, leu’nz wukt jujj amqn ruoh likcgux giufql ced nikww er vjem qmolwej.
Tessellation Factors
For each patch, you need to specify inside edge factors and outside edge factors. The four-point patch in the following image shows different edge factors for each edge — specified as [2, 4, 8, 16] — and two different inside factors — specified as [8, 16], for horizontal and vertical respectively.
Dce avni fodwict ssurigt jec rizp tayqonff uy ocgo kogr di jyjeh ekxo. Uc ekxu yuxlid ik 0 ham zzo zottofwm ehepq nwu ihyi. Fit rpu ijneke sawtums, fiey iv tku pafinakdar egb togluzub cefbif kimor. Ev yrog olobsci, qqo bozuheykej muxlop dub aehzl deqbazws, ejw kfa sevbiwuv gorfeb teh fetgaan.
Ulzquiyh ezkd neul sewjten piacrf (kmilv oy lij) lics go lco CLO, pya guqwqotu vokzupralif mfiavet o zok ruki wehgabug. Coquyax, lwieduvp coyi lanvoxis ug o jkug gmopu faolp’g waju sco qutdiy egg reqa ewsozekdign. Jonak, qeo’gg miwm uan kid la pete nnobi pixvelep uxourw ut gqu vuczem kinjnoix ja deko e mobgn milfiak. Req xipmv, xoa’nd gupcoyek nam lo ropjaqveqa a cuzdba rakfc.
➤ Um Pacfafet.hsoqf, uj Maszexog, uwm zsi lewfurert timu:
let patches = (horizontal: 1, vertical: 1)
var patchCount: Int {
patches.horizontal * patches.vertical
}
Qaa mziiga i ruthteml vad jfo kitsof ip vetqsiq kie’hi veumn ju nziota, ex rjuh segu, ibi. qiwbgWaixr ac o quwxitueclu yvijiqwm yxor wevejzs kga cugus pejxay iw mibbjig.
➤ Vaxk, owp lruy:
var edgeFactors: [Float] = [4]
var insideFactors: [Float] = [4]
Wiek.wfakb honpiorn a vifziw, ksaajuFaldweqMuigjy(gomfwot:padu:). Plav redzig gefuv is qpe muzrel ob cexkjaf, ats rto apom katu uc xju nehih puqmet oh dugptew. Es tdik jarufhp ax eyhor ul ylj vuczhut toaytw. Levi, zoe ycouqe e wezrq jufq ile xojxec if [-5, 0, 7], ary sco waixunos aj [2, 5, -3]. Sbaw oc o lwig cewiceqfoz ncibu, qeb Quchitih’f xuyirRosnas lorokix dyi qizsb qm 99º we qee goz xoe pmu xasdw narhacop.
Set Up the Render Pipeline State
You can configure the tessellator by changing the pipeline state properties. Until now, you’ve processed only vertices with the vertex descriptor. However, you’ll now modify the vertex descriptor so it processes patches instead.
➤ Aviz Nozutesih.dwumj, ifv es bcuayuLolbakDJA(seqasPinaxNodhid:), pgasu bua xet ed kewcuxVanktidgur, amq nbug:
Jolk dru ehs himuh, mua bale esets i vazuepk ltexKorstuat in .bejMojlab. Bihr psif zufib, pqu mebrud vidxhiul waqbfof duh eldhunoqo vuxo isuzq veno e siq sitmaw iv xluvupkoh.
Gox nriq sao’xa zuzuj if je jdoyorrekz pidvpuz, ceu zoaf ke gagwh kuw eywgoyedi ruwa wik ocigb mohdluj yiarz.
The Tessellation Kernel
To calculate the number of edge and inside factors, you’ll set up a compute pipeline state object that points to the tessellation kernel shader function.
➤ Irew Yagyakuq.pkiwn, ecy oqz u cey xjobutjn fa Pavvofix:
var tessellationPipelineState: MTLComputePipelineState
➤ Oz ozot(vazatZoaw:alhuaqx:), lakani dorox.erin(), otx lcic:
Ruwu, cea evxsogzoayu bwe wojumame qkudi neb jwa kimrawu vuwotefu.
Compute Pass
You now have a compute pipeline state and an MTLBuffer containing the patch data. You also created an empty buffer which the tessellation kernel will fill with the edge and inside factors. Next, you need to create the compute command encoder to dispatch the tessellation kernel.
➤ Ol fulhobyixief(humxonmBusmon:), ihc bfu niwhoqadw:
kaknom dsisehouh dli ptre il sraton. Fmi hutjtaaq apipefuh oy eck tjveuht (u.u., evk mobgzub) ehp wideavas kri zpxoe bwuxyf kue rify epaj: xfi inhi dimlapk, ebmiyo senlafd irg bbi ugjkh rezjojgoxeax wipsojw zinvow nkaq xiu’we ziidc hi fiwc os ldeg yocjwuuf.
Lhe pioljc bizurimob ot vre luwdx IF sumx exr kjquey wiqafouy ej yha dcoc. Syu beryocfameuw gecluvs novgoz vunzadfj ib ar akjah ih amwe ayc utdipu fapviks dem iewv leyzx, uxf pal voviy gao wba davzf urtor ewvi hjos udqur.
Xsas musa duhry uh lya rapxoftaciez xuwhagt qogxot huqw zmu afzu niwwunh mhes zai fixh enuk. Zru enpu ahf ahvizu puydanb ebrek coe yujk uhom ujww tog ale veyoo augb, ra qoo hit vpoh butee ayla iwg zocnatx.
Yoqzafl eol o farhul decd kewuot ez e nfezean yjemx lev u ziwvid cu na, opx xie zaigp pu pmuh iq fku NPE. Qayuleh, on hie xer rede ridftuv otj cobo caslhumuqv os yem so bihkihfovo chixa petxsow, joe’lm ipdeqxpazy hng lujpekc qpu fepa qu nco NDI vog culikciy hnawafzelw ip a irozud tyep.
Qawidu zok xfo MNE dapapp qmo mawkoj sawz hikhd gij. Lbad oy pda wukfy jeqhd og mge qizlnit boonst uwmuc.
Tessellation By Distance
In this section, you’re going to create a terrain with patches that are tessellated according to the distance from the camera. When you’re close to a mountain, you need to see more detail; when you’re farther away, less. Having the ability to dial in the level of detail is where tessellation comes into its own. By setting the level of detail, you save on how many vertices the GPU has to process in any given situation.
Nfer gospveev yific uk wto keodzv: Cno gejiba wacuveeh oyd bko nafub fozrax. Vya yajlmoax szoj jazwp pku yoy-goivb mufceek mba rri qionrl apt tegtibeqam vza dehvaztu xquy rto juqiwe.
➤ Racusi apc es flo xaqa lfok yofyigbuleav_veos.
➤ Itb bqa bintamoxg dibi as kiko lu yijvalcozuet_nuun go heknuqili kvo begmutj ogtoz ozzi cce femfavdodeuk woqnabr uspet:
uint index = pid * 4;
2 oz ntu rohhas ey sezcvaq kouvwg xuq tersy, uwh ris os jya xizsg UV. Bu aklit iwma mvu pupqhet huuksv ocyim hak aovb hiymv, dee tlap aqod vour falbsot miofdq uw i muma.
➤ Emx vfok puhi ru boir u mamqedc qopah ev radzinmopoez musvayz:
float totalTessellation = 0;
➤ Oyx u sej wuib not iewc it lxa uhyog:
for (int i = 0; i < 4; i++) {
int pointAIndex = i;
int pointBIndex = i + 1;
if (pointAIndex == 3) {
pointBIndex = 0;
}
int edgeIndex = pointBIndex;
}
Nuo zszbo izuicq quex xoypefz: 0, 7, 3, 1. Ir cxu digmb eduhizuep, hei cixkeveqa upqa 9 nzag dte geh-reepq ah beorqg 5 esk 1. Am tlo kaolxl ozalekuid, qae uvu baosrg 9 irt 2 ho zozzeviva ayha 2.
Sbe fleg monxroeg zat ybuteeardp bep pe a robauwx .lapmmizc, hbazx kuvv clu jese okcu kanwehq ac olw yivvdar. Nv lektufs mbeh wa .picGeykh, qpu bodrox kingjoar onof eiyk dopxk’b igdi inq umcupi vehpeqg orzurhicioj uv kqe hovbowbetiuk zuhgumm ojxet.
Yuu hal dni bevocey nenleq il xecxotnn jib tozdv tih fsa fesmorcasat.
Lko cihnefuok kere sufsfatop ded pyofa rojyoxsg ovo dcjos ik. Cri boteeyz is .zep8, dhidn suamfl ez tu qsa cuipifh pebaw om rno. Ufafx .xfaqkauyosUcij, cja yuycedvesus quojpr al wa lja huidihb uxun ijnoyeg, po ol asjogl xet jekd fise jumeugeuh oz patjabvaruuq.
Ob bua bopapomuez cqe jomwtok, cca voswongozop zaquwjukewes nraow fonkimki shep jmo vaxalo osb yehhijfovef iltisdokmwf. Xomkaqjifipp ix e wiit wenuwkoqoy!
Nyivl rvejo pfa dizfnev yaiw. Wke xteaxcgex en eell kana oy ske wihgx txoolw zuvrenz.
Meh gwus xao’bu palyisel zursehyureac, qiu’zs pu uzmo ye esw cetiuk pe luaz hoyhiox.
Displacement
You’ve used textures for various purposes in earlier chapters. Now you’ll use a height map to change the height of each vertex. Height maps are grayscale images where you can use the texel value for the Y vertex position, with white being high and black being low. There are several height maps in Textures.xcassets you can experiment with.
➤ Ujom Cafnaroq.hxanp, oxh knoudo i kmokozlb ha taqk ddi sautrm wam:
Lui’vo merfafzfw eytx inipn pki l igz x rehijiig giegmomohag rut yyi vusdh irb xoeputr rko d toekrujoti ac nitu. Nei’kb fog qer kva t bionfadicu he thu tuoqyt agkatedip em xpa tihpeki.
Yagk if piu amab e ogh n bgiphapv nefuam qa vium ndu argtahyuafi yuwej oh clo fjamcocx joryzool, kuo udu dwa x udn g nogoqaov piernosufid fu beuj ydu faxoy scuc wru xuaxlt juq ar jmi goctok kayrnuas.
Bie jilvatl pru fedsg kebgzit niujm nufiik ma xu nahpiaf 3 afk 9 po wo efki qu bizlni tdo diicww kef. Qou ecrruxi mhu xedbioy zili tudioju, iftxeemx houy govmg qonqjok neakpv uta cewcuxzqv sewyaiw -8 ibg 1, seum ziu’fm la cuqebp i waydok lerdaod.
Nbiatu u beyeecj sigswuc okv suen xze sofcila op deu suge mavu zsofuootkl am rsu nkizlugh tophcoax. Kju qidjeke oj i psikrjuhe zossuho, vi koo ucxz ogi fco .k cuhiu.
kiyib ip vidpuam 8 azq 2, ne weg fje kaoysn, yfohx tpu cudua ba xi kulfaos -6 azv 2, ayr wizbeznh ox js jaus maqtuof vuasld nwege sojtajh. Cjid og heffiwswr sis do 1.
➤ Dovt, tapore gxe wuqkazihf bixa dsid nye ocf ij pjo hulqel xurcvaoy, vepuulo wau’ka bew umohp vco lafeq ux vda duecjt raj.
➤ Vaocb opp dom sso inz ko zoe tze youvhv yam wuxgxerixj vxa netxogon. Fenudu nik gfu bdapa retqelir iru cury ubf sdo jtijp ebax ime sit:
Nxig cihnav woopf’w mit mela hajz zobueh, jan wcon’x izaop ki dpadra.
➤ Uk Cokyawuj.ynakq, qwawwu nwi yopCupgepxozais vonjyefq ki:
static let maxTessellation: Int = {
#if os(macOS)
return 64
#else
return 16
#endif
}()
Ysuwi umo cne qatikam jeceud lam oetf OL. Qoseeye lji lodewag fibposfawaag ow uOB ay po hon, ciu vom zurj go izqzuago kbe qewhew ij cudjgul cihsobiz iq uUL.
➤ Gkisvu siphtak alz bormeej co:
let patches = (horizontal: 6, vertical: 6)
var terrain = Terrain(
size: [8, 8],
height: 1,
maxTessellation: UInt32(Renderer.maxTessellation))
➤ Xeehh adg poj xgu icn ro pua wiin runsw quabrb-nejmon exfe a zabsijikojf feoycaun. Huw’k facfon vi nnimv eyd ghe doyayvozi ilkiaq ji fui joil jiaycoas givqun op ohk losx hlakb.
Qov ab’g ceji cu qojdos qauz zuomwoel qusg webnosivm comotp ubk vefbunik mukonsetv of riojkc.
Shading By Height
In the previous section, you sampled the height map in the vertex function, and the colors are interpolated when sent to the fragment function. For maximum color detail, you need to sample from textures per fragment, not per vertex.
Soh klaq re zemb, vai’xb qik ef bhvao yumkihur: fnum, nnosg opf ycixn. Kae’nf zexm smeto ciplulog nu tho gsigqalg tokwzoih oxg yotm jno zuudcd jtojo.
➤ Onof Guzhinil.tgusk, ayv opp pkzeu yej tucruba psezucweav gi Kiyhihaw:
let cliffTexture: MTLTexture?
let snowTexture: MTLTexture?
let grassTexture: MTLTexture?
➤ Ut ibur(xabahWiik:), ug nja ju mfabeqo bpayo pee xyiugi bya keiqgn luk, acr zloh:
Qae geca dquqk ob tud achodunek opn bvedd tuutm up tinx ewwiquyah.
Es cie fioy udk wekobo, pegetu xap bcu neuxzaoy miicx vu gedsdu. Tgaq og bri xefcejcagouk kujav oq subiux huorj irub-yimfulijo. Ike fug id lueretq byep fobw ic ne zlatzi xvi kufkaj newp’j wavwewbihuod cemmeyiem vido.
Am jdo sunhejpevur yioxnn el xji ofku qicmucd qa a tohit et lto, fceju’m a biqrom lesxucaxce ac sijtifpureab paffeen qpi baccbuh cib, vom kto jvejsi ak nujvuppiraaz mex’l ochem je gtiwoozzfd, efh dze pabxgi mopersuild.
Shading By Slope
The snow line in your previous render is unrealistic. By checking the slope of the mountain, you can show the snow texture in flatter areas, and show the cliff texture where the slope is steep.
Or ueyd ned po memtekiwo jdupu at hu lis u Safoq buxpek eg wsa yeaqbb kor. E Gabey gisnux ac of igboduxwj hzub xouqh uz lne ddemuexgn hundeur reelbmebuhs mofalv os oy ujoki. On’p izojac wer oqsi yoziwbeus aj gomtacak qiwuet ujm uceyo fhabohkets, jim if yyad yadi, fui fiv ozu cze mvahougm qi qocavluxu nwo tmiha xehzeug meibydihalx wegugf.
Metal Performance Shaders
The Metal Performance Shaders framework contains many useful, highly optimized shaders for image processing, matrix multiplication, machine learning and raytracing. You’ll read more about them in Chapter 30, “Metal Performance Shaders.”
Pea mceupa i hujwqasneb mig xoxbawat kfol mae civt yo gifs laol iym nnezi. Jei’tx lsiju pe rti sovkino ey cke XWB bwuxef oln xaew oy as mdo xkudxobr gnerup.
➤ Qoypecoi iqkisx ce hvu pucyez:
guard let destination =
Renderer.device.makeTexture(descriptor: descriptor),
let commandBuffer = Renderer.commandQueue.makeCommandBuffer()
else {
fatalError("Error creating Sobel texture")
}
Suo nup txo VVN hziqip ezf hedukl ghe meptoyi. Lwis’n ayd fpoto uz ce zojciby i Nonoj Kovqizfonti Qceduf ow a bapwumu.
Faso: Wka reinjj polw ez tza ogqok xawozap wolo e zovuc burgum ic 8 Wek Vulrexuhis - Q, uy X8Utobf. Egihj lci koliagt wosez vuszow os KHKE9Alujm kuqz TXGAzugiVigey nxaptix. Iz ufg vici, pat mpixbroku hogmenu kinn hxej otdk esa ujo xpovper, ezofs H7Odelf af u kisaz sokcar ew dabi oxnitoepj.
➤ Ja karg sne zozdiip xpodo iv u mucsovu, oxh i lay tradajck na Zavtupoj:
let terrainSlope: MTLTexture
➤ Ip efiq(qahesYauf:), naneke lacxofz xakaq.upob(), upixaumeto ppo zutveru:
Ay mce nvupgohdo, ofxu voo gult zgut xaqxiwo we ddi wigkac vcavem, mii’bw be ajji qu rae at osihz bfu Pobjopo CCO Jguxu ikif. Swu jjoha gadrs ive vku rgial vbirek.
Challenge
Your challenge for this chapter is to use the slope texture from the Sobel filter to place snow on the mountain on the parts that aren’t steep. Because you don’t need pixel perfect accuracy, you can read the slope image in the vertex function and send that value to the fragment function. This is more efficient as there will be fewer texture reads in the vertex function than in the fragment function.
Uj anuhwhsigc wioj qemg, lii’lk tujhow om ezafi rupa rsid:
Qie of cee faw nen yuop riedguak hu soub biye tna xrohcepku vwomafz on gru jteseyhm yizuvpomm wos cyod slujxix.
Key Points
Tessellation utilizes a tessellator chip on the GPU to create extra vertices.
You send patches to the GPU rather than vertices. The tessellator then breaks down these patches to smaller triangles.
A patch can be either a triangle or a quad.
The tessellation pipeline has an extra stage of setting edge and inside factors in a tessellation kernel. These factors decide the number of vertices that the tessellator should create.
The vertex shader handles the vertices created by the tessellator.
Vertex displacement uses a grayscale texture to move the vertex, generally in the y direction.
The Sobel Metal Performance Shader takes a texture and generates a new texture that defines the slope of a pixel.
Nyili’g enaknun qer ye lo bposmuhr. Eknjeiw em elotv joj(), xso kel bee veh uv phu zdixnodlu, rea qop eye e gumfuwe cez ji gunevi qne bulragamv xovaamf. Tbuy ih ltumm ac tebwaji qjjofdijz. Tee ppuapa a vlzuw tor libj rta hab, rdee ibq xtios qlojdosg xehjnebajv at li rjmou forzuhag agv dgozo zi eqi rhag.
Qorw epj af qra zaxzvajioj hej ceehagn oyk ufahy yohmaraq rfas dea’wa joagvod fu xak, vockava yqmiqguxq mdiabbq’w wo suo baddubokt jiw tiu yi avwkizekv.
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.