Instancing - LearnOpenGL

文章推薦指數: 80 %
投票人數:10人

Instancing is a technique where we draw many (equal mesh data) objects at once with a single render call, saving us all the CPU -> GPU communications each time ... Ifyou'rerunningAdBlock,pleaseconsiderwhitelistingthissiteifyou'dliketosupportLearnOpenGL;andnoworries,Iwon'tbemadifyoudon't:) IntroductionGettingstartedOpenGLCreatingawindowHelloWindowHelloTriangleShadersTexturesTransformationsCoordinateSystemsCameraReviewLightingColorsBasicLightingMaterialsLightingmapsLightcastersMultiplelightsReviewModelLoadingAssimpMeshModelAdvancedOpenGLDepthtestingStenciltestingBlendingFacecullingFramebuffersCubemapsAdvancedDataAdvancedGLSLGeometryShaderInstancingAntiAliasingAdvancedLightingAdvancedLightingGammaCorrectionShadowsShadowMappingPointShadowsNormalMappingParallaxMappingHDRBloomDeferredShadingSSAOPBRTheoryLightingIBLDiffuseirradianceSpecularIBLInPracticeDebuggingTextRendering2DGameBreakoutSettingupRenderingSpritesLevelsCollisionsBallCollisiondetectionCollisionresolutionParticlesPostprocessingPowerupsAudioRendertextFinalthoughtsGuestArticlesHowtopublish2020OITIntroductionWeightedBlendedSkeletalAnimation2021CSMSceneSceneGraphFrustumCullingTessellationHeightmapTessellationDSACoderepositoryTranslationsAbout BTC 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa ETH/ERC20 0x1de59bd9e52521a46309474f8372531533bd7c43 Instancing Advanced-OpenGL/Instancing Sayyouhaveascenewhereyou'redrawingalotofmodelswheremostofthesemodelscontainthesamesetofvertexdata,butwithdifferentworldtransformations.Thinkofascenefilledwithgrassleaves:eachgrassleaveisasmallmodelthatconsistsofonlyafewtriangles.You'llprobablywanttodrawquiteafewofthemandyourscenemayendupwiththousandsormaybetensofthousandsofgrassleavesthatyouneedtorendereachframe.Becauseeachleafisonlyafewtriangles,theleafisrenderedalmostinstantly.However,thethousandsofrendercallsyou'llhavetomakewilldrasticallyreduceperformance. Ifweweretoactuallyrendersuchalargeamountofobjectsitwilllookabitlikethisincode: for(unsignedinti=0;iGPUcommunicationseachtimeweneedtorenderanobject.TorenderusinginstancingallweneedtodoischangetherendercallsglDrawArraysandglDrawElementstoglDrawArraysInstancedandglDrawElementsInstancedrespectively.Theseinstancedversionsoftheclassicrenderingfunctionstakeanextraparametercalledtheinstancecountthatsetsthenumberofinstanceswewanttorender.WesentalltherequireddatatotheGPUonce,andthentelltheGPUhowitshoulddrawalltheseinstanceswithasinglecall.TheGPUthenrendersalltheseinstanceswithouthavingtocontinuallycommunicatewiththeCPU. Byitselfthisfunctionisabituseless.Renderingthesameobjectathousandtimesisofnousetoussinceeachoftherenderedobjectsisrenderedexactlythesameandthusalsoatthesamelocation;wewouldonlyseeoneobject!ForthisreasonGLSLaddedanotherbuilt-invariableinthevertexshadercalledgl_InstanceID. Whendrawingwithoneoftheinstancedrenderingcalls,gl_InstanceIDisincrementedforeachinstancebeingrenderedstartingfrom0.Ifweweretorenderthe43thinstanceforexample,gl_InstanceIDwouldhavethevalue42inthevertexshader.Havingauniquevalueperinstancemeanswecouldnowforexampleindexintoalargearrayofpositionvaluestopositioneachinstanceatadifferentlocationintheworld. Togetafeelforinstanceddrawingwe'regoingtodemonstrateasimpleexamplethatrendersahundred2Dquadsinnormalizeddevicecoordinateswithjustonerendercall.Weaccomplishthisbyuniquelypositioningeachinstancedquadbyindexingauniformarrayof100offsetvectors.Theresultisaneatlyorganizedgridofquadsthatfilltheentirewindow: Eachquadconsistsof2triangleswithatotalof6vertices.Eachvertexcontainsa2DNDCpositionvectorandacolorvector.Belowisthevertexdatausedforthisexample-thetrianglesaresmallenoughtoproperlyfitthescreenwhenthere'sa100ofthem: floatquadVertices[]={ //positions//colors -0.05f,0.05f,1.0f,0.0f,0.0f, 0.05f,-0.05f,0.0f,1.0f,0.0f, -0.05f,-0.05f,0.0f,0.0f,1.0f, -0.05f,0.05f,1.0f,0.0f,0.0f, 0.05f,-0.05f,0.0f,1.0f,0.0f, 0.05f,0.05f,0.0f,1.0f,1.0f }; Thequadsarecoloredinthefragmentshaderthatreceivesacolorvectorfromthevertexshaderandsetsitasitsoutput: #version330core outvec4FragColor; invec3fColor; voidmain() { FragColor=vec4(fColor,1.0); } Nothingnewsofar,butatthevertexshaderit'sstartingtogetinteresting: #version330core layout(location=0)invec2aPos; layout(location=1)invec3aColor; outvec3fColor; uniformvec2offsets[100]; voidmain() { vec2offset=offsets[gl_InstanceID]; gl_Position=vec4(aPos+offset,0.0,1.0); fColor=aColor; } Herewedefinedauniformarraycalledoffsetsthatcontainatotalof100offsetvectors.Withinthevertexshaderweretrieveanoffsetvectorforeachinstancebyindexingtheoffsetsarrayusinggl_InstanceID.Ifwenowweretodraw100quadswithinstanceddrawingwe'dget100quadslocatedatdifferentpositions. Wedoneedtoactuallysettheoffsetpositionsthatwecalculateinanestedfor-loopbeforeweentertherenderloop: glm::vec2translations[100]; intindex=0; floatoffset=0.1f; for(inty=-10;y<10;y+=2) { for(intx=-10;x<10;x+=2) { glm::vec2translation; translation.x=(float)x/10.0f+offset; translation.y=(float)y/10.0f+offset; translations[index++]=translation; } } Herewecreateasetof100translationvectorsthatcontainsanoffsetvectorforallpositionsina10x10grid.Inadditiontogeneratingthetranslationsarray,we'dalsoneedtotransferthedatatothevertexshader'suniformarray: shader.use(); for(unsignedinti=0;i<100;i++) { shader.setVec2(("offsets["+std::to_string(i)+"]")),translations[i]); } Withinthissnippetofcodewetransformthefor-loopcounteritoastringtodynamicallycreatealocationstringforqueryingtheuniformlocation.Foreachitemintheoffsetsuniformarraywethensetthecorrespondingtranslationvector. Nowthatallthepreparationsarefinishedwecanstartrenderingthequads.TodrawviainstancedrenderingwecallglDrawArraysInstancedorglDrawElementsInstanced.Sincewe'renotusinganelementindexbufferwe'regoingtocalltheglDrawArraysversion: glBindVertexArray(quadVAO); glDrawArraysInstanced(GL_TRIANGLES,0,6,100); TheparametersofglDrawArraysInstancedareexactlythesameasglDrawArraysexceptthelastparameterthatsetsthenumberofinstanceswewanttodraw.Sincewewanttodisplay100quadsina10x10gridwesetitequalto100.Runningthecodeshouldnowgiveyouthefamiliarimageof100colorfulquads. Instancedarrays Whilethepreviousimplementationworksfineforthisspecificusecase,wheneverwearerenderingalotmorethan100instances(whichisquitecommon)wewilleventuallyhitalimitontheamountofuniformdatawecansendtotheshaders.Onealternativeoptionisknownasinstancedarrays.Instancedarraysaredefinedasavertexattribute(allowingustostoremuchmoredata)thatareupdatedperinstanceinsteadofpervertex. Withvertexattributes,atthestartofeachrunofthevertexshader,theGPUwillretrievethenextsetofvertexattributesthatbelongtothecurrentvertex.Whendefiningavertexattributeasaninstancedarrayhowever,thevertexshaderonlyupdatesthecontentofthevertexattributeperinstance.Thisallowsustousethestandardvertexattributesfordatapervertexandusetheinstancedarrayforstoringdatathatisuniqueperinstance. Togiveyouanexampleofaninstancedarraywe'regoingtotakethepreviousexampleandconverttheoffsetuniformarraytoaninstancedarray.We'llhavetoupdatethevertexshaderbyaddinganothervertexattribute: #version330core layout(location=0)invec2aPos; layout(location=1)invec3aColor; layout(location=2)invec2aOffset; outvec3fColor; voidmain() { gl_Position=vec4(aPos+aOffset,0.0,1.0); fColor=aColor; } Wenolongerusegl_InstanceIDandcandirectlyusetheoffsetattributewithoutfirstindexingintoalargeuniformarray. Becauseaninstancedarrayisavertexattribute,justlikethepositionandcolorvariables,weneedtostoreitscontentinavertexbufferobjectandconfigureitsattributepointer.We'refirstgoingtostorethetranslationsarray(fromtheprevioussection)inanewbufferobject: unsignedintinstanceVBO; glGenBuffers(1,&instanceVBO); glBindBuffer(GL_ARRAY_BUFFER,instanceVBO); glBufferData(GL_ARRAY_BUFFER,sizeof(glm::vec2)*100,&translations[0],GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER,0); Thenwealsoneedtosetitsvertexattributepointerandenablethevertexattribute: glEnableVertexAttribArray(2); glBindBuffer(GL_ARRAY_BUFFER,instanceVBO); glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,2*sizeof(float),(void*)0); glBindBuffer(GL_ARRAY_BUFFER,0); glVertexAttribDivisor(2,1); WhatmakesthiscodeinterestingisthelastlinewherewecallglVertexAttribDivisor.ThisfunctiontellsOpenGLwhentoupdatethecontentofavertexattributetothenextelement.Itsfirstparameteristhevertexattributeinquestionandthesecondparametertheattributedivisor.Bydefault,theattributedivisoris0whichtellsOpenGLtoupdatethecontentofthevertexattributeeachiterationofthevertexshader.Bysettingthisattributeto1we'retellingOpenGLthatwewanttoupdatethecontentofthevertexattributewhenwestarttorenderanewinstance.Bysettingitto2we'dupdatethecontentevery2instancesandsoon.Bysettingtheattributedivisorto1we'reeffectivelytellingOpenGLthatthevertexattributeatattributelocation2isaninstancedarray. IfwenowweretorenderthequadsagainwithglDrawArraysInstancedwe'dgetthefollowingoutput: Thisisexactlythesameasthepreviousexample,butnowwithinstancedarrays,whichallowsustopassalotmoredata(asmuchasmemoryallowsus)tothevertexshaderforinstanceddrawing. Forfunwecouldslowlydownscaleeachquadfromtop-righttobottom-leftusinggl_InstanceIDagain,becausewhynot? voidmain() { vec2pos=aPos*(gl_InstanceID/100.0); gl_Position=vec4(pos+aOffset,0.0,1.0); fColor=aColor; } Theresultisthatthefirstinstancesofthequadsaredrawnextremelysmallandthefurtherwe'reintheprocessofdrawingtheinstances,theclosergl_InstanceIDgetsto100andthusthemorethequadsregaintheiroriginalsize.It'sperfectlylegaltouseinstancedarraystogetherwithgl_InstanceIDlikethis. Ifyou'restillabitunsureabouthowinstancedrenderingworksorwanttoseehoweverythingfitstogetheryoucanfindthefullsourcecodeoftheapplicationhere. Whilefunandall,theseexamplesaren'treallygoodexamplesofinstancing.Yes,theydogiveyouaneasyoverviewofhowinstancingworks,butinstancinggetsmostofitspowerwhendrawinganenormousamountofsimilarobjects.Forthatreasonwe'regoingtoventureintospace. Anasteroidfield Imagineascenewherewehaveonelargeplanetthat'satthecenterofalargeasteroidring.Suchanasteroidringcouldcontainthousandsortensofthousandsofrockformationsandquicklybecomesun-renderableonanydecentgraphicscard.Thisscenarioprovesitselfparticularlyusefulforinstancedrendering,sincealltheasteroidscanberepresentedwithasinglemodel.Eachsingleasteroidthengetsitsvariationfromatransformationmatrixuniquetoeachasteroid. Todemonstratetheimpactofinstancedrenderingwe'refirstgoingtorenderasceneofasteroidshoveringaroundaplanetwithoutinstancedrendering.Thescenewillcontainalargeplanetmodelthatcanbedownloadedfromhereandalargesetofasteroidrocksthatweproperlypositionaroundtheplanet.Theasteroidrockmodelcanbedownloadedhere. Withinthecodesamplesweloadthemodelsusingthemodelloaderwe'vepreviouslydefinedinthemodelloadingchapters. Toachievetheeffectwe'relookingforwe'llbegeneratingamodeltransformationmatrixforeachasteroid.Thetransformationmatrixfirsttranslatestherocksomewhereintheasteroidring-thenwe'lladdasmallrandomdisplacementvaluetotheoffsettomaketheringlookmorenatural.Fromtherewealsoapplyarandomscaleandarandomrotation.Theresultisatransformationmatrixthattranslateseachasteroidsomewherearoundtheplanetwhilealsogivingitamorenaturalanduniquelookcomparedtotheotherasteroids. unsignedintamount=1000; glm::mat4*modelMatrices; modelMatrices=newglm::mat4[amount]; srand(glfwGetTime());//initializerandomseed floatradius=50.0; floatoffset=2.5f; for(unsignedinti=0;i



請為這篇文章評分?