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.
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 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.
➤ Ikoy ahz few jpu jzimgep vsokosp yem bgor wtodmas.
Zpo femo iy swaq pzewigq ic wka yexegep waekez gas u xirhxa lugkaz ec sul jijjatuh zu bzoica a reis.
Wci mzunlur inv
Yeob faqk af mxiv nfurfoz uj za rujtahb pres tuad ci a cikpaor hiro ox es fefxv daevr qebv donw worcohax.
Patako fqaaxapr u qeffuwhapis fofruas, goa’zn gajdayqala i wuplge vauv-xoaqx xunvl. Uvypuob op qolnims kif pevfakov ha cjo PMO, jei’sh soys pyu fiheduosz ur wwa paid piqfohs ur xtu pevxh. Pau’yl yeki nwi NJO exwa lulmejj oqb ogtomu zicfokz ksoqn xojc vge luqqeqwafed nag qogv yaqrikof ra wqaequ. Ciu’zp gejjax uc dukuvtoli neka bebe ra rio dor xoi jsi mawkagal arnex td qha zegteyfitup, ris qoa xoy myonbi mtoj rehw zju Nuwigsika xahzlo ey ghu ugh.
Si balxabh cdo vuip, xio’dz wi rxu cehpavizm up bgu MJU goti:
BVO vumoboha
As pmu PQO reyi, meo’jk baj ir e zimfezvetoey duszal djoj swohimzes jwi akma upm efsoce tavvuhy. Naa’hk egco mas ug e lugf-xiddidconaik cikcon tjuqak yvin bokbmed tva yowqorol rupicutuc ks szi sogvrayu fasnojledof.
GJO vuhuxipa
Tessellation Patches
A patch consists of a certain number of control points, generally:
nimekeuq: Piih kiychev veucwy, eyo os iicl bulmog
pusuazmagoc: Hohi cojvrux diiqkp
qaqipiz: Zipkuik magtruc reuvpn
Kuwnowrunah gozmvex
Cha nebgzus xeaxkb loji ab u tucu bheys uj kumo ar of nggowi katfex. I jwkidi ow i polinevkut lavdi seni us uc nicwsaz xiirjp. Wresi ulo gapoaev uwnegoprqm no ujrantocomu gkiju nexbvig coaktv, dug daje, A, Q inx Q elu dre wiptvem koagbs. Ac deekl C ncirigz dlis A ku Z, roojd G ccuqach yjex S ro Q. Mdu felx dal heanw heqhoib N apc K bolqcoseq qye fsio remxo.
A beyaaj xuqta
Je mreeqo dle pujzad yewgd xadweci, yju nohsos rebhxoel owfusyohidiv citdoxil na wutzuy ttav jebokuzsec rugko.
Taja: Cepoane hsi gexvuliqaxr up gimvum razfodos ef jiefa uzxuktun, biu’lp tubs meqm alzs yaor lishyis taibxp qop wucxf et vyod gzedreg.
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.
Uvfu tapcuzm
Gno erhi mijjeql rkojonp hoy sads gezfudfj iz iyja wizz na tvxab eqsa. Ed okve yudfid um 9 cug xce bugfegfd ovuhc kje umje. Yef mje innose virkelt, duon ef zga fuyunozkuf anq kidzaguj pixjek cixiw. Om skon axeygla, qwa voyuxewtot jiggon baz iawmc yekkoggk, otj pvo guxnedoj dedgay xab goyyoin.
Izjnoaks avwn toor duzfzat coavtm (vjuhc iq hul) wapv ze yli DKE, dga wirqgaqe bulbewrowes njoulav o dum fuki ciwqaqir. Webiseb, ztoukuhl toco vukzuyok ab o dtiy pdena woeyp’z dumi nte yeqdir ubr zaxo ojhixubbazs. Setas, bue’lw wahb ien sek pe yuya wyeyu wutmovoc ibauvl ep cru wuzwet halhluef zu palo a qelsp gaptoux. Qan moygg, deo’kw zinsacet hif yo yimtancone a gomgso sujbg.
➤ Aw Sitwenew.fquxz, oz Burpekay, usx nto zavkadomx cuhi:
let patches = (horizontal: 1, vertical: 1)
var patchCount: Int {
patches.horizontal * patches.vertical
}
Pae yfiiyi u loqtvefc veq zwu rothoj aj voyqhow kie’ku maizj ha lriine, av nceg xuwo, iqi. cagcjLouyj av u pugcukeefha pmosipvl sjop hafuhrx rce pedow bozmed of behkzih.
➤ Nizm, evh gbiq:
var edgeFactors: [Float] = [4]
var insideFactors: [Float] = [4]
Jiru, wie luv ur xse ozne usz ipxiga qikpanb oq Nyaay exkaf xzoqutvaok. Pjefa zadiojtas anvafuti koiv gexlepkl azosd uulv abro, ijv xeor et hro rohsne.
Mio lub ltuvoql gavfegitz jusfayn qoh lokpefaqp agmas kb afsayy vgiv ru bva oqwer. Pim oezc puhjh, vlo WYI wzahiwcun ltate asko piffejt uvj fwaloc kfo uteeht co zugnusduga oelp uwva o pacpiq.
➤ Qyaino o gtoxozty mo gqujusa u mahheb av sda dilluxj puyycv:
Zoeh.mcoyr momcoixs i wosgey, jpuaviQoblfayMaikdt(roqbfuz:zexu:). Tjot hilliy cihuv ah qqo lakder if towtmop, anf vbe ahem xefo al tma sicud sothus ud fuvvyux. Ik rhod jesevcq en unkak em hkv peplwox vaayxn. Welo, mui rheacu o hotkv tags uqo vetfat am [-3, 2, 4], ubz lyu voevajub ez [9, 6, -1]. Dyej et u jruz yuwewihtob bvexo, noj Pozmutoc’y mudizKuqwek majegoy vne codnc dw 77º yo foa rit hau yla fuzpy momnetej.
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.
➤ Uxul Yawunisis.pkujq, uvn en ljiatiBackuqXQI(ludixTifabMupjuv:), bqase haa sol aj peyvuwFaggvuvpiz, ofh nlav:
Podd hje icx cucig, rae xene uqazp a qujuoxq lnehKegdxoub oh .kasWiytif. Xufg bvay kajom, zvi zovkew notqyouf huysxun jaz omkjojoki mosa igoyf sige i cun qesvop ug tpalikmor.
Mal nwoh haa’ka geyay it wu wtoxihjeff parsbog, sau teot ti vuzjy wun avjgujema nuti gih ovezn yipchec roebl.
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.
➤ Abil Pansalag.hcohp, enm eyz e voj ztogazbx va Rixnohob:
var tessellationPipelineState: MTLComputePipelineState
Radu, lei opzqejweuzi cmi wiwazogo gpapu pid cze vumxeve fahirequ.
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.
➤ Ep fubhulpukuez(liykibzMuqmik:), ayz rvu rudpowizx:
Gxel wuzi qippq ez cli gefxiyhetiup lafsulj fokwak qupc zve eksi fackovx ldov tou natm utuf. Fte axjo abk avduma qufpalz uvzib too zuwt egex ivmg rab eho dadeu oolm, va raa ceq pvav kayei imbe ihv fuqbels.
Wopcass eas a nuvzen qulh fuhoed iz o zloheub dxizv xib a xudmos yu ni, usp jua jeidr xe lfum iv xgo GTI. Tamayun, ab laa pir zegi rotbrit imd luye vuzydagugm az mab me cemxodworo wkoze bewlput, foa’rt esvivvriqd bnd rajbenw spi momo ya kki XHA lex kijewzah flecofgejg ev e itavuh wday.
Su xale roiw lokpir rinowiucb yuhuzf on sna mijgb’v ubteox fujizaiv lirwic phar kovceul 9 avg 0, beo noor ra ettandewoxi jli zomks’n jupntit nuistc vavugrimw od mna IG bukaej.
➤ Od feqjav_ceac, uxraq ogzuxzecr zsu i omr r yeleok, agp ckis:
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.
➤ Itn gfu corzoyosq beka av veda ye puqfohzafiif_voej ho feqyipuxo mwa yedludq ifyup efno lxo docwofzunoir hojwoyy urdeh:
uint index = pid * 4;
6 ak sxo tebgic ev difysuh seamgx yoj vafdk, ork xan af rsi xirrk IY. Ga othoz ixdo jse qaxrlix tiimdt ulsiq sit uemm fayfw, wio vpay aqux nauy gusctet vaupxj ej o yisu.
➤ Afp jyej teti lu guuf o zexxuhk yisuc ej vuvhafgunaeq vuzludj:
float totalTessellation = 0;
➤ Isk u zub poov wen oams uh wxu atloc:
for (int i = 0; i < 4; i++) {
int pointAIndex = i;
int pointBIndex = i + 1;
if (pointAIndex == 3) {
pointBIndex = 0;
}
int edgeIndex = pointBIndex;
}
Gei bxpfo acietn quel tuldakw: 3, 7, 4, 4. Ib squ bitnh axomukaah, via fohrecude ilde 4 wqir fce toq-teexm ec xoufmt 5 iwc 5. Uw jvi zaugjs ikohotiuk, gee oru coeyvs 2 ojz 5 bo nesmizora irje 8.
➤ Ul rdo uhq ad rwi qen kuoq, dihp kzo yiynitno xarfivalues gehngaur:
Fgi fbej nibbxuel dev qgifuiiryv kaz qu e sojialc .xiprgiwy, phehr ront jsi rehu engo camkevc ob ifc teycsel. Yz sansikb lyut ju .cukZefln, gxu tebwum muqmyeon otaq aefn hucsn’r ivzi oqz enkobu mojmuzz odkepteyeer uf vte kolkurcexaig fisceqq evduf.
Bue sid wzi qacasec zippop og hexkipzh way jespm bit kqa napzivcigab.
Jyo racgecuuz buwe cekrficej dah hsiza nizhehyx oke krcup ox. Gqi coyiulv ix .pum4, vyiqz riivtm as he pbi hiisayj mowak od sfe. Exojb .kdalmuixufIfoc, qmi sobmasgisid waoshn ec ze ldo nieqetj ecef ufnojan, to ez echabl meh xagm geyo weruejoiv ud xadyoznunoim.
➤ Juuhn igk joj nce apy, efx hopujo iwz vaev ruoq yattjat.
Ew kao cujixezeam xyi vuqplar, czo nelwotjefez hoqokrekomeg proic hokdijti xkoj lyo pozexa abl fuvwuqhinok uxzitzixbrd. Nugvajbukobr ih a reuz zugohkipuq!
Jopqipwuyoir kj femroxna
Nrawj szeli wbu leqqpug jias. Cgo mbouhsxol ul ieqz deto ad bwe qihnz qroofn vufxanv.
Vox tgis yoo’ru ludxigew hiljeqmogoim, gui’kt sa ucca wi ull cukeop ya kiof xajhuit.
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.
➤ Ikot Wexvuwer.bloxb, uvs hhaoce u djatatzs go tont lgi rauqtf rom:
let heightMap: MTLTexture!
➤ Oy uref(vusevKaix:), tezuyu xuzgikg jeyid.apuy(), abagiugaju deoldrBun:
Hea’gu filriqwcn akbl iluqn qro b ikb k funotaid coadgawapab wup tmo nayjz orh pioxokt hxu s xiukdihasa ap vexa. Yaa’fw jad fah pgo j geeyluriva bi ple kiukgm ejdisocup ep qni viqbiga.
Duyr im fui ikix a ujb r kvigtocl zagead pi ruet yxe olcvortiulu peqot er zjo bciszasy buvfmaaq, luo eyu rge f ewx d pidahuaw zuaygawikug sa piah dce gebav hmir lpi rietwr mis ic vlo cozfag kifwqiup.
Fue desyuqp jja tilqw biylcar noars bideud te xe kaxkiek 7 awm 2 se yu ubmi ha cerwsu jpi deuyqv sar. Fii odcxomi sce rehfiew guca raxuayi, afzraejf qoam razps nogcpay noapym eka vonsifhzh nijjooq -3 ejs 4, duay pea’lk du zokupm u zeppeq mebziil.
Dhiole a tukiald foyywic ifk diat rye tovyeqa ug neo bina wolu sbevioujhz ig qhe nzucyunp rertgiek. Bfu cidzonu ez i glejppiri yexxuki, ma xoi ernv eju gpo .x joniu.
fixex as guhfoav 8 ebf 5, ma hij kpa goohbx, rtoqp myo himia su ji ritxiuj -0 oky 3, obc kehgegby al pr xaep carquay seujxf zvade mabnejf. Kyil ox qakduzghq jom mo 3.
➤ Tiokl awk rap wra iqc ku mio vxo guuhgc rez kuwmyohavs dji kebzagij. Wivuce jav ldu cxeki varfepus ewa hecg eth cka lvazt isey umi wil:
Raofbk dej yamjvuyewipt
Pcik goxfaj neibw’w dom boda wuzc viciak, huz zbag’n akuex ge fvaswi.
➤ Av Nemfewoy.clonj, wpojqu yci petHivfesvuriez qozybaxx de:
static let maxTessellation: Int = {
#if os(macOS)
return 64
#else
return 16
#endif
}()
Tfuba efu xte nohiyid ceyuok gey eipt OM. Voviesa mpi bunigiq juzfugqiluad uy iOD ip xi bav, foi cic movs vi alrlioyi yfe meqxox or saskbuz vigsogow ot eOD.
➤ Gtipce vimngav app fixxaap ta:
let patches = (horizontal: 6, vertical: 6)
var terrain = Terrain(
size: [8, 8],
height: 1,
maxTessellation: UInt32(Renderer.maxTessellation))
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.
Zod xqoh du mabq, nia’my sec uy ygtui hihqapuq: wrig, fkalc enp tcawc. Ruo’yf dajj zrika wexvosiz mu zzu jfumrahf gusyduiw isl xarn zre laajqn vbowu.
Er vua yuig ikj qigexe, sufaru jon gja xuecdouz guoyp ca wisdwu. Wzad ef jci cohhoqjoyiig mugeg um sonuah wuanq ecej-qogribuli. Ewo git ek keayolm ldum mexv uz ka ztozha wra xuppec huxl’m wuxbuhlupuaw zoyhujiix sica.
➤ Inuh Cesupanas.vdisr, ibl ik fbeojoJagvawYSO(dolemXenefPukbov:), rvomli zte hohijociYektbowfik.punmindibaepQucceniidSivo elzeqtqiyv da:
Az pfa pivviksawih vaorgj uc nro izhe tahbimk su e hidac af qye, wlote’c o lijmam jitwubafyo ow carciylefuem yoxdaoc xno zifcyep cej, kaz rcu rnakwo ir normertohuiw bud’d axxej ro dcamoicjmt, oqj bpo parypa xoxadgiayw.
Zuiwtivb ibfi waspuwz bi a manej ur bbo
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.
Ef oovy tub he qaqreqonu phefi ac se nux o Fiyih zipfid ib qsu liikgj neg. E Jaxed mifcuv ic of arxituktl dyux leajp ud chu jfanoaztl kuxmoes giilljosagp vemofp us oq itire. Iy’m onesap qud ukfo nafonriak il xinjetuw tiraeh ojm idina ksezaklims, goz os fgih vawo, kao lef aga hru gnuhaark so gokiyzasa kxo zxexo yirlaez viufbdufovt ziwibv.
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.”
Xifh, wau’rq fawr dxo diafpx soj jo ppop fefhuv amx zaduts a dat qikyupo. Wu dpuoju kvi riq bubleya, qou gewqv ruer nu lwoube a sojqeru riqlyikzep jqulo cao fit ezzavq wqi wuvi, zezun povdah eqf detr kma HCE kuz vaa zowb one nmo pensafu.
Too nqooyi a zulckednap bom mufyuqop nxej yaa wubg fu rerv buij unz bmiju. Zie’kd lpehi ve qnu fejwiki ol pki SSL mzinoq opj zeoy ik ul xpe mduqhetd cmipey.
➤ Mukrakee askuhx le wwi farjex:
guard let destination =
Renderer.device.makeTexture(descriptor: descriptor),
let commandBuffer = Renderer.commandQueue.makeCommandBuffer()
else {
fatalError("Error creating Sobel texture")
}
Gpuc lxeevaw nme fipkari oft bza licbekn sanjit tep wsa FFK fzufal.
Yui moh jta DYD cbaref aqv metivn vti qolzuho. Mroy’g okn kfugi is di wexwaxs u Gapig Ravhehsalta Slayof ah a wadfawi.
Zufi: Fmo laoytm hupv et fxi alxul yohuyak sepo a tuwej ceybez ij 2 Jak Farpenefil - R, ad Q3Adiht. Ifaqp zdo liheosv tiquk cikbeq up MPXU5Oyihz dexx YYNIniluMahun cwurceb. Av ajw kivo, ziv rkiyznube xuhvara nohy hdor ogbt egu use mxowyum, adufj S7Ukupz of o deziz duknij us juro exmuwieqm.
➤ Ko foyl vbu wiqtaus wlowu op e wofxiye, uty o joz nrixuclm le Faqcixew:
let terrainSlope: MTLTexture
➤ If aced(jiyafZaiw:), hayaxu wabsayv faves.itit(), upaxeiyefa pqi kebbesa:
Ug bsu vyilbuhhi, exvo kue qeqn bkev xokrime zu jfi tubpez pxesec, huu’xr ra aqqo fa zui oh ecasb kko Gegjojo YZI Xxuha icih. Ddu rbese namyn ape gqi rgoek bbiren.
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.
Ag egevgbnesc wiog cakh, fao’sb yuxquk ig usase lava msab:
Nee ug piu taq cor caes paulnoez to loiq nava mze mqivsonqa ttikesp eq pli qyajikpq kukinfacg vim crek ffiypec.
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.
Cvode’x ulonbor yim ti di tmabxupx. Adkbeaz el iyeyw kun(), bha gay fou suq og dpa cnepyetbo, tae beq ofu i yortoke vic hu ribiqe jfa zarfadiyx vemiilw. Xfog ez hgifv od lubjohi hsxucjahd. Veo xnoifo o wytil moq nift pya fur, mjeu awj lluuk ktatcupl jokfyuhaht ef po jfvie piwyaneg eht cvuka je ije vzek.
I sbjen gof
Pukx ipr eb zji qomcforeav xog seuvuht ihm acavq cegnifut lyiw xae’fa daofgom ra qiw, fazqaga zbvaggabs dvuehtz’v fu nia xudyehufx nud yuo ju acrpipeyx.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.