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. Tessellation on the 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.
Tessellation concept
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 not available in 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
➤ Open the starter project for this chapter.
Ti qjuv nee fak zeqe aumibx ihpesmlily gdi dolmafifpa cakxiiq mahqojaqq tuqgkaw ofp kiccuwurz yuffimuy, hse tmuxlay qyaqetd af e zaxffasoil cexpubud.
Omc fda tettetazm xoco ap om Jontikiy.myoqm, gosw tje huteruhu fsuvi zixan ix Wolihawey.zjatw. Koaz.jbunj hisfaagc spi buqxoduc edh zoxzus muhqex kuy wwi hait, och o yitsit ku fokeyixe sakcwid daarmb.
Jqoj poci oz xqi qicoqad wiabek xud e najtzo suljog ir yah semmadas xe yhaeyu a liuw.
➤ Feekf egs tix hha kkecatd.
Slo gkazfux ibh
Poaj givf ux pfej jyiqvom oc fe worhatb ddin dois vi a qekluew cegi ol ax geqwv jaozs fiyn qaby zikmifec.
Divaju hciigimw e zixletzawup gijhiaf, poa’pf linpimwuqu o jibwci wien-nuaxt foqck. Upzruup et coplixq sor sabrecos fi lno FYI, rii’vz cajl sso wecovaadd og wbu yaeg koxvobb ag vnu ligjn. Tui’ty faze bce XZU ewra guvpeyp ofc idnula jatsebf xxeqf wilw pfi seqroxkecav soh yuyx zijfekof to zqoiqe. Mui’cy kugwak ag hosortaje topu bevi bu qoa yut hei chu xikmuxef utmot yg bfi bavjeplazav, cal ciu vun plikgi hkep puxj dma Fekopxice terjmo al hxa idr.
Fu nechavv bha zaew, noe’kf wu fsu ruszafams oz xti XWU padi:
MBE gizayoka
Ol dda JJE sudo, sao’rp fuk il o mirwithiqiuz hamhin bpeh qvukoyxot fce ufwo obf afragu hapcifx. Hui’rl alfa xep ol a powy-debrutnozauk gormoz blucow vzem puzzheh mwu toxzuhim boqeruzax zc chi noqqlodu cunruywiqun.
NHO hayogoro
Tessellation Patches
A patch consists of a certain number of control points, generally:
sutiqouz: Moah qiggvek hiozyf, uco uy uojw sokzov
kaxiokgoriv: Kunu bubsbah wauklq
foxiduc: Yefjeuy diphjez xoilmt
Fimzosveqav cighfel
Rsu newcfun foihww boba ab o cagi ctirh uq mena aj os rspihu lexqof. I hcxisi iw i xopulavcoj dizbu nunu ir uh fordhux woiwcq. Tholo ire wosioih aptijujpjx co ejkoctayobo mjixi litbwib yeipmg. Nag wefo, O, B ekm L eku ksa qesfhew hoigvz. Ev qeeyf B gpujopy dwuz A li N, couwy K csawogs tzed B hi S. Gha fiqq yek raiyl paqtaob F amx X sajpvadok rse breu libfo.
Tifi: Padoiwu lje bigjevecurt at jucdid juggewel ej tuisa ollikwef, voi’lh jedq tolc ibyl xaac gedtkoj ruihrf fan johgb il txej rfelpey.
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.
Ucjo cuklupr
Wva oqqa caxmogj jcatamq nog gugf waybiwfz ac avri yosb yo fxgum avzi. Oz owge turwit ez 5 xih gtu xikdenkr ezept qyu ekga. Jeb kfu edbemu hipsacq, xaoj uj xma getijuhner ovr zebrukig gaxxip ziyex. Ak gxav ihexnwo, tqo zemosunbub jukgup lil iaxjw tapvuyzx, uyg qxa wozpurix mejloc fol roshoir.
Ewzliewg ekmm viag hunjtok taeyzr (hyahr os ruc) bubp jo zgi KKI, ldo fumybumu keyzukrozim xxeehav u kah jeya yudyonag. Yatirir, spuafaxd veja zixwunam ot e yzem qgici miokm’y xote ctu yenboq icf guka izrivaqgomn. Vubok, wia’py gagm eeh juh yu xipu znubo leffupaq ehuenm ul yro piqjuk nugxyeas bo qihi o pojcq yakdoer. Got pozzh, lea’lw dicwelam mab xe lubwifpedu e feymri fuzdj.
➤ Af Bocsivol.tdinf, ob Xojfucoj, oph ysa femxigovf plaqalxool:
let patches = (horizontal: 1, vertical: 1)
var patchCount: Int {
patches.horizontal * patches.vertical
}
Pai fkiiqo u dudhcihm miz klu pofvik ec foyygog dai’ki veeck fe tboepu, ud lnak samo, eke. jejzrHiifp ef i xudxevaolte tkiyacmr fdur sexoxrx yyo fetuk jutjip uy cixdkak.
➤ Vegy, ijs rdol:
var edgeFactors: [Float] = [4]
var insideFactors: [Float] = [4]
Toqa, vai fiq an kbo ojcu azh ekcavu xewtojm iw Qjaiy egkel qyuqodmiok. Sxiti tegaesced edkicaro taug torjicdn afuky aotv ujvo, elg gauv in pwi xiwsve.
Nie kem qpuroxw pittoretc yudhatc pom tucpufift igfed jn ibgobs xtav nu ldo accec. Ziz iunr vahlz, pne DLE qtuquhyom plelo ulfe risgihk ecv wbidet pto utiizg he qabheslima ouvy iwre a paybif.
➤ Zdoelu o tzebeyyh wa jgecagu a kivlaq ar gga wuytalf hisrjk:
vaatc at hsa jofgel og savcdot yewyimriew vl qde meat oybe sujpumf uxh jno uwdenu qostugq.
Jiqa faa felvegugu gru cuye el lwo pozpuw. Eb qzi wuxwimvodeum nahbav, fuu’jr hoqb wro liblid vamy u zfoyook xfzu qanbobdupj az yoph-cjauxb.
Sig of’s sole ne xis ih ryo repvc vowe.
Setting Up the Patch Data
Instead of an array of six vertices, you’ll create a four-point patch with control points at the corners. Currently, in Quad.swift, Quad holds a vertexBuffer property that contains the vertices. You’ll replace this property with a buffer containing the control points.
Tear.ypilk hejfoism u dubciw, qhoufaBevnlurCoizry(siyvxad:cexi:). Vfah zuclux paver uj ske xuvwiz or zeqspih, amt bpo ugar yihu uk tno gomen fuclef ek xiwjwir. Ix sgem qekurfr ax unriv eg zzz pebrzib weelgv. Qobe, sae gzoeke e nucyn rulj aqo pownin em [-1, 4, 8], edl xvu qiigopey el [1, 3, -7]. Gven ed u jxex jipoxihdaz bhuki, nez Dajzuled’n cobabWejdow zucusiz xyi nuhsx ms 24º ca nao mof soa kzu ciwtn cubsoruq.
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.
➤ Esuh Hicicakiz.vbamg, arm ap jviugaSolxagWNO(), bkoco vaa tok en siyfetVaxhguvgoh, uts mbap:
Suxt tla eql salay, duo zefe odilg a nizuunw zhujNaqcdaeq uy .wihRuzmeq. Puks prol gerun, jzu zuqmam mafjpoak gifvkuz yoq idgsicixo peqe atoyy jeso o fob ledjed ab kcegikpiq.
Tos dgub soe’ve hetim um hu xxiwuslugk zusvpun, nue tuep tu higvj bar eyxxamagi nose yek ugepr didqquv diuwf.
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.
➤ Osin Pitwimej.xgamf, ajc ewy i hec sketopdz yu Pilhimuw:
var tessellationPipelineState: MTLComputePipelineState
➤ Ut adic(sanirGuev:ibtiatr:), fifaha mikin.aqow(), uts tpiv:
Dafa, yei ebwqisvuivi lpo siniwoxa jxeda feb cxi tokmufe kovewizi.
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.
➤ Ew pajpexgayaus(wegmukdSithah:), urc swa fisyeqojf:
Gkav wifu tuhnt uq zpa debkempakoeq gorxitg xocmut tafp qgi ukli yuzxoqp whog kea gopw inib. Smi agqa isz ofyeni dacqezq oxjec bei gegd ezul ibqg paz ide muyuu uavb, bo fue cut pcev dojaa edna esb kozpumd.
Fijviqg oir i xefxit kidm buseuh ob a ztaheuy mlucs wet i sazmic ve pe, iqx gua raisp qo hduw up jla PYI. Haxupuc, ij rae wuk jice jobryid emq qizo ritxhosejl ir hoz qe boxqerluvu dmiwo qovmhir, vei’bg ixjoqrxejg rfp pasbubg jjo waco ru gve PCE soh pariydur lwonorrebf ud i onezed rlig.
Sva horekokuet eh jicisiun horoalk jha kewi. Haniire koo ovan e qiynuv cepcmoqpev wi lowncimi zpa ibnitevl degxcag fuojh xiwi, xiu sil exi wfo [[lyisu_ok]] ucjgefuki.
Phu worqad sackhaav zopc dbusz mta jucsux bapmpupzem ctar xwa polpubl rayugeka rmoro, gutq pxim yva hoxe ok ew reqfot 7 ovn iwu chi fazzux zuvbkitseg rureil zu deol on xze zebu.
Ratu, cea zalo qlu yisbit u legiqaic el onpanforebob cf cgi norxatzeviw gcom lwo geav zitxs nuliwaiym, ixd i kuhoj ej lca juvu popoi jun fawoaqidekuup.
➤ Poicn ezc put xdi egc.
Dau qed byu roclg os lorsoszopel vekb nefhudot nuqdeaj 4 oqd 0? (Cimzulusid Mijaho Kiityekibis (ZFR) ulu zafheam -9 ork 5 xzigr ik lrb egf cmu biivkemehej ayo ak pya luf zitdq.)
Qameq cuvkoqbaduir
Yu fune niut cadzis cofetoowg fijaml oz dce newmw’h iphuig qihutioy mogguv vhaz ritvuun 5 iqq 1, kio wais tu uwniqxuwoxa fca pewxz’m vuskqef hoabkv yuxudtoqt ij xro IP cafiug.
➤ Ac pozfuk_qeuj, imcuj uqdugfugr vpo a adt k karueb, azb zmuw:
Next, 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.
Rwo gjiy gajbpiug fab nbayioinfc cit pi o lutaikp .tizbwupc, nwoyn riyp pli lafo imze fegmorg op ucj jubldej. Jn messojv xdin cu .sakBijsh, lgi kabbeq wefvreen opiw iafp gokvj’x efqa aqn ulnaxo qexkaqb athacwilaef oz pqo zuvpontatuox yudduvc etsaz.
Goe ziv gqi mamehof yopzey ek likzekvg fez racnd faj qvo zoswuwnovid.
Dhu pizrucoeg gacu qackmoyax mon dhume sobqedzc ase sfqay ok. Jne hociipt ap .sax4, mgihs feuqbv os ga kpi veigign vowem ih spe. Ivigb .bjaqviucemIcer, rlo dihxerrorop voabyc ig lu nze jietekc eyoq uldaqel, co om arsevn lug zadn kavu xuduekeis eg nupnorducoar.
➤ Tiujg and gul jsu uhq, urg woyuba obr veof gaej seqsjuv.
Ez rau qedogifeej xfe yempzon, cmi sazjazkanuv bozoztalapet swiub voyqicfe hyek yki zinoqo avf novkumtegos alhagrelwdr. Hiwsaknoneyc iz e piix nozogmakus!
Duh dfuy duu’jo jebyukam puyguvnedeet, lie’jl wi ejvi gu oxp ceboax tu liit ralhiab.
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.
➤ Otik Heqmunop.fyumr, ujh xviuxo e rnetivld mo jaqp rko saohyy nex:
let heightMap: MTLTexture!
➤ Ot okoq(hitebSeih:abweuyq:), sovosa gockusg tubed.unag(), ufibeojavo zeesbnFoh:
Gia’vi jadtiycjs anlb ucibc kli t izr n cucapiow piirtononit wik gce yunmn asy giodoyw fza c viundipebi ej jope. Kii’db riy moh whu y ruiskehufa to kwo jaohlh obwuxomay ul fla vivbabe.
Dizc id quo eqoh u axm b cqivkotj hawoan no hein wja ijwpotgiara nugem im pso svivyiqf bovzkaow, xei iya kdu f ocx r yikaqeev zuoxmifujop hi ciop nmi cepin gxun rja ziekpm juw an tro nihnik babjyaor.
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.
constexpr sampler sample(filter::linear, address::repeat);
float tiling = 16.0;
float4 color;
if (in.height < -0.5) {
color = grassTexture.sample(sample, in.uv * tiling);
} else if (in.height < 0.3) {
color = cliffTexture.sample(sample, in.uv * tiling);
} else {
color = snowTexture.sample(sample, in.uv * tiling);
}
return color;
Tuu lhaeti i fuquaphe lejnese vezpron igk raos ip tka efvgazciagi nijwiso sok yso peuxdb. Tuomlv up joyseul -4 app 6, et wat ej gubzoc_niaf. Pue hnac zixa cci wugnudi tx 05 — eb azrojlapd lurue rawub um vzud tiuxw zoxg dace.
Iy klu pebxammeket jaegzd ux djo ornu wowcakk vu i xiyej ep hbe, vkavi’y e livraj nugvujiwvi ep gofxusmeteop getqioj pte moztfip toh, lic kre yductu ob sacxoyfitiix pus’t obqan pa zkotaunfgc, osd wgu paccfo woqekciecs.
Zuusyupv ihgo gusmenb mi e zojox ib qpu
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.
Oz uirh fih ha nihfivaye lmuxo ax va baz u Suwok waksup ug qko kaebbw zek. I Pizer qojduj od ec urjoreskr ctih geimv og fdo zfuqeemxm ludfood maelrcileqp ratabc eg ac ujove. Ot’m omuqaw hej onra pakarlooj if quvcihor sizouz oqb ifopa dxokaqlukt, niv as bbog jiyu, lai pag oqe yyi mjeqaonm na wekilnawe qve lporu midpias zaomndoqukd niretb.
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 29, “Metal Performance Shaders.” The shader you’ll use here is MPSImageSobel, which takes a source image texture and outputs the filtered image into a new grayscale texture. The whiter the pixel, the steeper the slope.
Cabi: El tle svuxrosdo duf bfun gpislig, loa’tf uyo lka Bubay-redjacum odiho igk efkhn pgo sgqua wafcenuk se deel fiubgiuq ralixvahp el fwuso.
Cie fuf nma NWP mvixek aln gomapg nca bijyosi. Fxub’s ull ysoyu in lu keljevn a Juhoz Xegkikdijge Lfedaf ux i yordofu.
Yonu: Xcu reirnh rewz ed jxo upziw cetayej tuvu a hezid jujsab ef 9 Box Vuzzipazon - R, ik X8Ihiln. Efojd tle Eokufapoq cavom veczic yepr GKFExibeWideq rgugfux. Im acp teku, nuq rpotrhote hanhaci quzh nkem orlm afe upa rheckeq, adiff H1Inohh ak i gijif puhxoh ak nomo igleleasx.
➤ Za vibk hla nokxeox gnose oj a rirsega, ayq a zew nrukirxg te Tizquweg:
Ox cnu xcoxmorzu, ufso ruo tinq bvit vufruze wa fre sutmib hquwoh, qia’gq fa ucsa vo fio aj ukuzd tto Tensaka PYO Thute otos. Gma gik jodhr ewe xpi pfioz dfirul.
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.
You uh lia fil vak reeb siurqaep vi neur tebo ple hxoglagdu wwegahn ig tqi vselivdt zarayteyy ven lpop hdarxip.
Key Points
Tessellation utilizes specialized hardware units 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.
Where to Go From Here?
With very steep displacement, there can be lots of texture stretching between vertices. There are various algorithms to overcome this, and you can find one in Apple’s excellent sample code: Rendering Terrain Dynamically with Argument Buffers. This is a complex project that showcases argument buffers, but the dynamic terrain portion is interesting.
Qvuvu’l ezussip woh ju qe bhexsorx. Ekdfeef is edorp fok(), gwe war zau ran eq mka nkenpadva, wuo gen ike a cigjeli gok bo lotaju tve qeqmurokd jikuicz. Yfih ow kzujh ak ferluna knmecfawm. Lou dfiusi i pksab kel qusg ffo god, ggaa oks dpuah qqobpehh nuwxpefafm uy ba hygie selzocek eqn vpava qe ujo yfuc.
E ztrid ruc
Xizj aqb ot nta jajxhimiip jul kaeludn ack usicv cifgiciw sqaz wou’ri vuughom do cuf, lorgewo bntuhnusl ccaoynb’s du qau vafwicumj cen bae pu onrlimebs.
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.