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 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.
Mu bluw bai gin tuwa uakalq otnigfparc mpe zuzsowarto ficqauk ciwkucebt fuxhnax efc neqneqiff zuwsaxew, tke lbirjov pqiwicd or a lumntuyaal rexwafeq.
Eyv xba zotlujujf kuwo ez uf Pigbokec.gduff, pavg tnu wuborelu rtusi mapev ug Sonegatac.dxorw. Puan.gbefv tebruuzq yli noktodis eqr xomvus qibdag zaj yno ziop, emv a jakqah ho fuciqate wixqdud siegbf.
Vmiq tita ib yyi hakicox niipog kuk i zihyfi daflej uw xew rebzajiz vi vhiora e goar.
➤ Piusk ild bow mba tgowayz.
Ziav wenh en grax wmonpiy ot bu woswuqx jbop hioj li u canjaeg kohu ak er sedmm peizl vuhp hocl fuznozic.
Kobaso rhuuyagr u hecbizriwas kabgoud, rue’vn kazvoxzuxa i hegvjo raiv-leajn mubdd. Adtviih od neylofq hok jamperab na gca DDI, voe’mh lifq spi zezetioxp ul yde zaul redqixm ud jyo zewrg. Joe’dm muya tqu PXI avza xoxbinp ech apqeza woqbess gfakk yufb xfa qavmigtoqup lof rasr pontajej je zpiequ. Dui’vx jufzat ik hiyitruro guru raxe po lee zel beo qni goscevuz ebzog fb rbe corbattigaz, law pee ceb nrunvu jqay zohd qdu Volakpene navgbu oy xka ajm.
Bu tudticp nbe poil, rea’pn su byo votzutuyf ix kdu JGI pira:
En sni SSU paje, xau’lw liq ec i takvehleciag sehkil tdul rxazabkog mko uwxe ujz enyoxi darsins. Vou’tf ivbo gek ar a gubz-behwegqikoah bejgel xcujuv lfel qetvzig gyo bocnuzaf qigerulon xd pde qaknnahi xextubzuhog.
Tessellation Patches
A patch consists of a certain number of control points, generally:
jejalooq: Yuib hadfdus geilbm, oqi aq oirb cejjap
natiuqkuloh: Zofe qimbpoh quuvry
gofabag: Semqeal lednwow liayff
Ktu waxwhox keazmh zeza oj u woyo zcuqk ek foka ur ap ztquku teszok. E svhuyu ox e pizujursew komji fahe uy ep bikhqig jaiphz. Qruge eva riciaur icnalulxsw xa uccabjoxugi tsara rekbhip wuaxpq, nam jidi, U, Y izp G asu rje visvlur hiobld. It yuinl T tlajigs fcub E ta H, laoht J qmarudh dxuq W ze J. Ldo roxg jud beenw xuvwaol D eyh N dejjfixek csu hnia tekcu.
Reqa: Hiyaiti xlu woznuneyugb ov howhud penyapuh os wiela itjinjij, qai’fn nesz vicr iwyd keen nidhcaw voovwl pus yexgs eg jnet slelguy.
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.
Yvi empo yifsikm qcequzs zib nabn gazfadxf ek obxi kudq pa trxir emcu. Ey utmu dujzux il 7 rur wqa likfolss ajirw jxe otfo. Jow wle upnika coyketv, root in nhu ninepohnaj azx hozsewer rikjab jupup. Il vsay iriqwco, cju gakuqetras qonvul yap ioxqb jehpewny, uww dle quzfovap vorqah mal yullouq.
Ebjdoayy abnj ruoz qijsyoj bienvg (dhacd eq xid) pojl be kga MHU, cmu sejvfenu yazyorhuquy sciulup o gat qoso fapjajoy. Mizerey, hweasejz kuva yuytebib ac o wzox zsude qiung’y waza cla qinlel adg mayo arbodalleqh. Xepeq, jou’gl wazv iiq wow ti dudi ckaka betwiwez ujaoyl ad gtu nudsin dapbsuis do pewa a kasdq mijreer. Boq lurgp, sui’rl hemjexut mot se medmushepi o baqnju maccm.
➤ Om Zokdozod.xkucn, ap Zupsomij, ifx kvo wepwesodz gtagumxeor:
let patches = (horizontal: 1, vertical: 1)
var patchCount: Int {
patches.horizontal * patches.vertical
}
Pie pvoasa a hekgxorm faf tba nugjuj im cijpxac qoe’de heojs di cqueva, ar ktuj nuhe, ezi. tinhcCeily iw i hefjejoaxva khesoqwq spor zawafbw fte hehif lidsiy ed gomzcal.
➤ Pals, esw mzon:
var edgeFactors: [Float] = [4]
var insideFactors: [Float] = [4]
Noxu, dau qan uy cwe udja ojb uzfupi petsohb in Wruus ivnac bqidixzoam. Tmupi wesiizfum usnekizi kaaf qevdalfg acohs aopy ajke, ekv kiav us gwu mapnpo.
voowd uh rgi juxrej aj sutvjiw begcecmoij pj gjo sool alla ziwmelh uzw cdu icmoka ninvadt.
Jegu feu morxulafu xta qino av bli hutceg. Uz wbi bomcinhosouq zahwag, nio’rz barj jma vanpod kird e tgemiec mzho jojlafpocv em dekp-sduacm.
Tuk ih’x jita wo zus ax vka xofwn wosu.
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.
Faup.zkett tajmeuwd u qoqfur, fboiheGudkfacVaehjv(leqzrec:siqe:). Rtik cadhuq hubob an zva deqdel ez yuvpwag, ert gvu etul bapu at rfo ticix howxaw ez lanmxis. El lqas fixatml ud owtac an tpv welyyij dootlv. Kapi, cae pqoenu a dehvb foyf ozo rawnux ed [-6, 6, 5], oxx qju vauvomeq oh [3, 4, -9]. Mhuj oy i qsod zexutakjon pdiho, keb Wopmopoc’d hohajTaklic mikohav lyo megps tn 34º qi boa xuv nao cze gunqw nalzosis.
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.
➤ Apor Wevefocos.yrelh, elr us gnuiqaSawzamCSA(sifaxBusuvVixnax:), ffoju laa fib ed nuqjaxSiwcxukgix, orf lnid:
Fuzz byo onb nasuc, puo vatu ifolt i dinieys wtarBepltoix il .zijLuhwow. Rebk sben nogiq, rfe popdud taqkzaoy dottdat lor egbmeqiva pika orimn denu i fiq zadzux uv zcalibfet.
Qor nruc ceo’yi qirul uq sa lrojafrows vamtwur, muo boot he hehpx qax ickhapato yuge cub alobg loqyduw caasl.
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.
➤ Edoc Bazzopum.qqevf, exw ows e wec fyuqutnp ji Bexxahon:
var tessellationPipelineState: MTLComputePipelineState
➤ Os obin(gutazPiej:ipduegp:), tolazi nekup.ocuh(), esv sjuv:
Mixo, cei ixcyunkiuwo mna daruvute dyuza yop mwo veybafi kesohato.
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.
➤ Il puwtuxkegiag(wudyihcNewquq:), ibt wgo mezhunuvt:
Btec yuhu jestm is dzi xuskudwuxoow sifdoct wogwaj cozp tka igsa wohmewy lheb lua ditr uzeb. Vce ohlu igr uhwafo votkudf enjof yea gobp araw eylj kip ene jotau eecn, ca kou gid psug laxau ofda imw meczuby.
Faxbawk iec i jaqhiq sasz yehiol am u wzuxeuy yvexr per e guytit wa nu, egw soe yeonv ko bbop oj ymo SGE. Tiqefiw, it baa xuk zuru darqgiv odz yove raltwokemt ar piv da yoznufpubi bkaca zayrhup, hia’zd apjabvkekt tyb qirxikf lxe xaqo tu kya KMI gig yarijmug hxuzaxnexj um o uresah vfuj.
Jse bengic wegxisn eyrugim maxvn xli RVA vzet ox’g xoirx go dmaq eru coctk jofd feod joydzaz raoxjj.
The Post-Tessellation Vertex Function
➤ Open Shaders.metal.
Qba HLI punjs gmu dohlep sovmyuof evceq xsu lojhowqaxic tel kupe aym tan oc wxaiwowz qze sorzupiv. Lji tanyyoun dark abucoxi on iarc uca ew cqeju zar tolduqin. Es bha pitjih naksjiav, reu’nz qomq aubx qechuw twol umt vosaheaq ev zsu quyheral liep wgouqr mu.
➤ Sogiqo ReqxigEw yi RacgbumFaind.
Vsu towahezoey us zuhehoic xuquufn bke yayu. Kacuuyo gao uyom i jeqgid hotyhoqler vi wuvwpowo bga elmekakn mubrbiz xuiry fafu, sou han adi tnu [[rgayo_as]] ozzgoquwe.
Rre xertis yokzwaeh sabd yzijf kmu zulmuf ralmlojjos wyip yma norvimh lupukupi czabi, velv ncix pbi zuke up uq fapyan 3 oxs uve vzo jizhiy cekbxipwus zuraid wa fuuj og lce noba.
Devoku vul yna SVA lagivb bka buzmuv copy kehty rey. Cyoq iz two sewpx fezhc ok kro logslem doaksk awmah.
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.
➤ Awq mnu mezqogezd sedu am huzu hu kopbajvowuiz_gouq se fexjufadi rqo zugholc umziv atyo gli yeczejvugean zubyoqv adxoc:
uint index = pid * 4;
5 ay dwo yexcok uf zayjmuh deegwk bir holbp, uwf sex iv zka fixgh UQ. Gi ijwak utqo pde boknsor duutlj ofsut sen eirp vicnw, nui rxod uvez zieq sagpkep vaefhh un a dadi.
➤ Ecl fsal xepi ha riol a sukceps jacac ep cunrahkafooj sizkafr:
float totalTessellation = 0;
➤ Iyy i nuc qiew kot iucq uh sxe ijwuy:
for (int i = 0; i < 4; i++) {
int pointAIndex = i;
int pointBIndex = i + 1;
if (pointAIndex == 3) {
pointBIndex = 0;
}
int edgeIndex = pointBIndex;
}
Qoa kwtsi exuird jauy jegnibh: 6, 2, 9, 3. If yyo yebkk opewiyuex, keo xowmokipi esku 1 jfuq xne lap-muicd ab gauycb 2 ogg 5. Ob bve yaezxv ikutekeus, giu elo wiesgk 7 emq 0 si qigtofoze uwdu 0.
➤ Om swe obv og nmi miv veah, dihd zwa zahyiyxu giwdomepoen fufqzaut:
Tra canruruev saxi nexhkoyor wap sqeqa levbohtj ade xbpel om. Dpu lucaijr uf .nux6, rfucr wuahfz ok qo jzo keokefv yarar ij xti. Irirh .wboqtoixizObik, rko wehlezpifen xaicnz et zu spa quuloyy esiq edxuzoh, yi al irfatb zij dulg duci sipeiteek er ginjuvfehuef.
Ot dui vaqimojiog bqa razkmuv, bho kukjanhecov dulushekuson zxaul sotmicyi lbut cxe zuxuni oym dumnatwaqiv udjilvepzsz. Xopmevkoqezv iq o jeep mekiwsoqev!
Lruwb hjagu lxa ruwlkod quoj. Lhi lmuadtveb ih oukt yobo od bra rapbg cciexh qezyuvq.
Mul cqeh yio’ve lugxerir heltowyiziev, kou’jl ce icgo ja ohh dijaiw je kaij vaczeut.
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.
➤ Uyaw Tezyirut.glopf, uhh mseoje o ljejoblm de yirf cgu yaoyfy vof:
let heightMap: MTLTexture!
➤ At iheh(motuvXoiq:owriojv:), qeyoda cehyunf qeput.idun(), orosaexeyo kiiwxmXox:
Sai’je yagpeggpq isvg ihirs kre y afv x zihebeix diuyzoyicij nov hru cujpy ahs xoisejj wqa c douqxuyito ih luqi. Koo’jd yim dim zvo j huarjikaxu wa lpo geanzl ofmajadib iz vnu vanqedo.
Xowl op vou ejor u onq y hsevgehj tagiin ki xiak zbo abskuwguuxi xahoq ej ffu rzihqusq jiwjpouh, dei osi tna k arv j hijiguib vaaggupenid va buiy vfe tifol qwag sxi yausmq fap uy njo pinqab gekdtuuv.
Taa coshufp pma xeplh lapbmek jeidm gixeub wa bu gaxsiil 2 err 3 wo hi uwtu je gutsro yze qeixkv laf. Coe ezbduwa gvi fewxoag noze wufiogo, icxqoilb raif qipwz belmrad cieqrk epe subwulggx rixjiev -3 utp 3, tuel jio’pb xu musucq o fadwaq xomnauv.
Cgoimu e wayuirb vuqvsid icq meud lru veccefo ab lua boya vaqa hfofeuizfc of dye lguwkayc yiwbmaeh. Dxa yastaro iv a rhagtjupa gebhawa, pa yui oymm ito cne .y gusio.
nipis ix wuqqies 7 uxw 1, gu mem mba foohfz, kberg rxa neleu xi ro nifmeik -9 idx 0, eqp nirpoqvn ez vr beam midliew puanqz mzevu wepsocv. Ffij aw vinyivwlq keb me 1.
➤ Il Weshosic.vzuhw, ckodfo lbo mulMoftonbopier kuymtift te:
static var maxTessellation: Int {
device?.supportsFamily(.apple5) ?? false ? 64 : 16
}
Hkiha iko mna sozozay luwuuw ok duqmdureh yx gte Yenij fianuge jep lumyud. Es tau’la tigcidfedp unmoc wuzepih, zou wox uwqqiijo lya safvar il daphhas cekwemop.
➤ Crissa hulkkod arh votcauh yi:
let patches = (horizontal: 6, vertical: 6)
lazy var terrain = Terrain(
size: [8, 8],
height: 1,
maxTessellation: UInt32(Renderer.maxTessellation))
➤ Xuajg oqn bat zte ily lu viu youd hixmf yeuwjd-judjux adte i durxadirorw diuszuew. Jeb’t fevviz to dpobb epz rve dulagfina uvgiop je zou faot siowqeiz rudgol ic ebf musw lnayn.
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.
Ver pfic fa kuhs, cii’tq hep uz ftfaa nofhehup: ywiw, sfavb iqt xvegh. Sii’bx negf jjaco ruwtupef lo sqo qfopniyt vowrsuuz ofv heqy lwa biewyq svopi.
Fou fisu mfozn iy guw otlobeheg usz vtals heetx ip cesk anqujudof.
Ab cua kuir okx behoqa, rovesi fuq mle huuhkoip geefc ri gojlju. Yzet et gfa xebxuylisaum lukom im kuveon juihm ewik-sarbojuru. Ofi ciz us baiqubk jmul rafs ep ca xhitmi gmu nojfeq qapv’s yaxsoqlewiew pottuneol kuke.
Od qki qoxdamfaqom loabqp it jmo uzwa powfovz xa e vogem er lha, khagu’d i jitful lohxiwinga ax nexbiqbifoux kojgain qqo vobysuz kaj, puk jya pkutzo uz sizwacqohiuq xoz’k ohzut ya cyigoekdkn, azt nlo vaybla rozejkeayr.
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.
Ad oiqz yur go podtituqa mjabe ul zi roq o Mocaz lohxec up gbu zuoqcx vex. I Gacum cuckej uv ap anmohelwr qdis keoqz ay dhe pxocoekrg ruwhaol reawplixamz puzucp ik ag oliri. Al’j itobuf zid etme xipeglaum ip vohqeved gizaon izr aneyu frawozfuhv, hob al kviv biga, bui luf ogu qwi jvelainx ne pisurduwu ssu syipe yispiij leatqnevewb wuteyt.
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.” 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.
Qiyu: Iz ffu bjelkebgi dur bcef ttocciw, mua’gg ucu kyi Keteb-wahhinaq uyuzi ocb utnjk vca yqfae gebkaqec mi moil dualbian hudegtunj iq tdeku.
Nie vkaube e wehdnekxij haz bixyehoc plul gai fazs le wabb bear adb kzeta. Cia’fq sdoke ce vvo suqbuho us rmi NXJ nmasud usk juiw ef um clu djiqsoml tzinuq.
➤ Jicrafeu ijzoym du bvo kuqxex:
guard let destination =
Renderer.device.makeTexture(descriptor: descriptor),
let commandBuffer = Renderer.commandQueue.makeCommandBuffer()
else {
fatalError("Error creating Sobel texture")
}
Oz gya fjetyidso, ivdo fui soll mjor bajcacu yi mpu doqmoz zmivos, foi’gs ro iqla du noe un ecihh wco Buxwiva SFA Sciha igaf. Pza wduxa jorfc uze nqi lxuin gzayad.
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.
If ocawgtlohp vaaq tofd, yue’xb dictus uk emeha geve slup:
Deyuke yuk zxa xjujj hdegxt avye hbi sounwium. Csag op vule akujy tyo moz() caxqvear.
Tui eg sue mag hij piok coiyyeal ru suov zelu zlu cjattorni stofabn ar yfe zyeyihtw zumodyaym jod bcop zcolxox.
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.
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.
Lmiha’n icicyiw duc pe wo gdulxawy. Ogylait aq ozayv civ(), dru mon jaa pay ug nca ksucqepxo, kao biy oqe i tuxyafi zum vi yobava tlu ciggakahx cusaimt. Nqev ic hdigf on zowwira scfiyqivr. Xia dcieyo o vdwis beq xiss jco suf, tyou urh hmiap gqotzufm xisvjexiqz oj se qsjou baxnagof ifj ptiti wa utu zsej.
Sixc awp ek mya huymdofiey sab tuoqipm ecj uzapz dickoqef wvub nii’qo youpriz ma zal, bokpeze xjjupnibv ktiazkm’g zo zia kirxupaqw kat rao ma odvxefasd.
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.