Particles / Instancing - OpenGL Tutorial

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

Thanks to instancing, they will be shared by all particles. static const GLfloat ... Update the buffers that OpenGL uses for rendering. Particles,lotsofthem! Instancing What’sthepoint? Lifeanddeath Creatingnewparticles Deletingoldparticles Themainsimulationloop Sorting Goingfurther Animatedparticles Handlingseveralparticlesystems Smoothparticles Improvingfillrate Particlephysics GPUSimulation Particlesareverysimilarto3Dbillboards.Therearetwomajordifferences,though: thereisusuallyaLOTofthem theymove theappearanddie. theyaresemi-transparent Bothofthesedifferencecomewithproblems.ThistutorialwillpresentONEwaytosolvethem;therearemanyotherpossibilities. Particles,lotsofthem! Thefirstideatodrawmanyparticleswouldbetousetheprevioustutorial’scode,andcallglDrawArraysonceforeachparticle.Thisisaverybadidea,becausethismeansthatallyourshinyGTX’512+multiprocessorswillallbededicatedtodrawONEquad(obviously,onlyonewillbeused,sothat’s99%efficiencyloss).Thenyouwilldrawthesecondbillboard,anditwillbethesame. Clearly,weneedawaytodrawallparticlesatthesametime. Therearemanywaystodothis;herearethreeofthem: GenerateasingleVBOwithalltheparticlesinthem.Easy,effective,worksonallplatforms. Usegeometryshaders.Notinthescopeofthistutorial,mostlybecause50%ofthecomputersdon’tsupportthis. Useinstancing.NotavailableonALLcomputers,butavastmajorityofthem. Inthistutorial,we’llusethe3rdoption,becauseitisanicebalancebetweenperformanceandavailability,andontopofthat,it’seasytoaddsupportforthefirstmethodoncethisoneworks. Instancing “Instancing”meansthatwehaveabasemesh(inourcase,asimplequadof2triangles),butmanyinstancesofthisquad. Technically,it’sdoneviaseveralbuffers: Someofthemdescribethebasemesh Someofthemdescribetheparticularitiesofeachinstanceofthebasemesh. Youhavemany,manyoptionsonwhattoputineachbuffer.Inoursimplecase,wehave: Onebufferfortheverticesofthemesh.Noindexbuffer,soit’s6vec3,whichmake2triangles,whichmake1quad. Onebufferfortheparticles’centers. Onebufferfortheparticles’colors. Theseareverystandardbuffers.Theyarecreatedthisway: //TheVBOcontainingthe4verticesoftheparticles. //Thankstoinstancing,theywillbesharedbyallparticles. staticconstGLfloatg_vertex_buffer_data[]={ -0.5f,-0.5f,0.0f, 0.5f,-0.5f,0.0f, -0.5f,0.5f,0.0f, 0.5f,0.5f,0.0f, }; GLuintbillboard_vertex_buffer; glGenBuffers(1,&billboard_vertex_buffer); glBindBuffer(GL_ARRAY_BUFFER,billboard_vertex_buffer); glBufferData(GL_ARRAY_BUFFER,sizeof(g_vertex_buffer_data),g_vertex_buffer_data,GL_STATIC_DRAW); //TheVBOcontainingthepositionsandsizesoftheparticles GLuintparticles_position_buffer; glGenBuffers(1,&particles_position_buffer); glBindBuffer(GL_ARRAY_BUFFER,particles_position_buffer); //Initializewithempty(NULL)buffer:itwillbeupdatedlater,eachframe. glBufferData(GL_ARRAY_BUFFER,MaxParticles*4*sizeof(GLfloat),NULL,GL_STREAM_DRAW); //TheVBOcontainingthecolorsoftheparticles GLuintparticles_color_buffer; glGenBuffers(1,&particles_color_buffer); glBindBuffer(GL_ARRAY_BUFFER,particles_color_buffer); //Initializewithempty(NULL)buffer:itwillbeupdatedlater,eachframe. glBufferData(GL_ARRAY_BUFFER,MaxParticles*4*sizeof(GLubyte),NULL,GL_STREAM_DRAW); ,whichisasusual.Theyareupdatedthisway: //UpdatethebuffersthatOpenGLusesforrendering. //TherearemuchmoresophisticatedmeanstostreamdatafromtheCPUtotheGPU, //butthisisoutsidethescopeofthistutorial. //http://www.opengl.org/wiki/Buffer_Object_Streaming glBindBuffer(GL_ARRAY_BUFFER,particles_position_buffer); glBufferData(GL_ARRAY_BUFFER,MaxParticles*4*sizeof(GLfloat),NULL,GL_STREAM_DRAW);//Bufferorphaning,acommonwaytoimprovestreamingperf.Seeabovelinkfordetails. glBufferSubData(GL_ARRAY_BUFFER,0,ParticlesCount*sizeof(GLfloat)*4,g_particule_position_size_data); glBindBuffer(GL_ARRAY_BUFFER,particles_color_buffer); glBufferData(GL_ARRAY_BUFFER,MaxParticles*4*sizeof(GLubyte),NULL,GL_STREAM_DRAW);//Bufferorphaning,acommonwaytoimprovestreamingperf.Seeabovelinkfordetails. glBufferSubData(GL_ARRAY_BUFFER,0,ParticlesCount*sizeof(GLubyte)*4,g_particule_color_data); ,whichisasusual.Beforerender,theyareboundthisway: //1rstattributebuffer:vertices glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER,billboard_vertex_buffer); glVertexAttribPointer( 0,//attribute.Noparticularreasonfor0,butmustmatchthelayoutintheshader. 3,//size GL_FLOAT,//type GL_FALSE,//normalized? 0,//stride (void*)0//arraybufferoffset ); //2ndattributebuffer:positionsofparticles'centers glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER,particles_position_buffer); glVertexAttribPointer( 1,//attribute.Noparticularreasonfor1,butmustmatchthelayoutintheshader. 4,//size:x+y+z+size=>4 GL_FLOAT,//type GL_FALSE,//normalized? 0,//stride (void*)0//arraybufferoffset ); //3rdattributebuffer:particles'colors glEnableVertexAttribArray(2); glBindBuffer(GL_ARRAY_BUFFER,particles_color_buffer); glVertexAttribPointer( 2,//attribute.Noparticularreasonfor1,butmustmatchthelayoutintheshader. 4,//size:r+g+b+a=>4 GL_UNSIGNED_BYTE,//type GL_TRUE,//normalized?***YES,thismeansthattheunsignedchar[4]willbeaccessiblewithavec4(floats)intheshader*** 0,//stride (void*)0//arraybufferoffset ); ,whichisasusual.Thedifferencecomeswhenrendering.InsteadofusingglDrawArrays(orglDrawElementsifyourbasemeshhasanindexbuffer),youuseglDrawArrraysInstanced/glDrawElementsInstanced,whichisequivalenttocallingglDrawArraysNtimes(Nisthelastparameter,inourcaseParticlesCount): glDrawArraysInstanced(GL_TRIANGLE_STRIP,0,4,ParticlesCount); Buesomethingismissinghere.Wedidn’ttellOpenGLwhichbufferwasforthebasemesh,andwhichwereforthedifferentinstances.ThisisdonewithglVertexAttribDivisor.Here’sthefullcommentedcode: //ThesefunctionsarespecifictoglDrawArrays*Instanced*. //Thefirstparameteristheattributebufferwe'retalkingabout. //Thesecondparameteristhe"rateatwhichgenericvertexattributesadvancewhenrenderingmultipleinstances" //http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribDivisor.xml glVertexAttribDivisor(0,0);//particlesvertices:alwaysreusethesame4vertices->0 glVertexAttribDivisor(1,1);//positions:oneperquad(itscenter)->1 glVertexAttribDivisor(2,1);//color:oneperquad->1 //Drawtheparticules! //Thisdrawsmanytimesasmalltriangle_strip(whichlookslikeaquad). //Thisisequivalentto: //for(iinParticlesCount):glDrawArrays(GL_TRIANGLE_STRIP,0,4), //butfaster. glDrawArraysInstanced(GL_TRIANGLE_STRIP,0,4,ParticlesCount); Asyoucansee,instancingisveryversatile,becauseyoucanpassanyintegerastheAttribDivisor.Forinstance,withglVertexAttribDivisor(2,10),each10subsequentinstanceswillhavethesamecolor. What’sthepoint? Thepointisthatnow,weonlyhavetoupdateasmallbuffereachframe(thecenteroftheparticles)andnotahugemesh.Thisisax4bandwidthgain! Lifeanddeath Onthecontrarytomostotherobjectsinthescene,particlesdieandbornataveryhighrate.Weneedadecentlyfastwaytogetnewparticlesandtodiscardthem,somethingbetterthan“newParticle()”. Creatingnewparticles Forthis,wewillhaveabigparticlescontainer: //CPUrepresentationofaparticle structParticle{ glm::vec3pos,speed; unsignedcharr,g,b,a;//Color floatsize,angle,weight; floatlife;//Remaininglifeoftheparticle.if<0:deadandunused. }; constintMaxParticles=100000; ParticleParticlesContainer[MaxParticles]; Now,weneedawaytocreatenewones.ThisfunctionsearcheslinearlyinParticlesContainer,whichshouldbeanhorribleidea,exceptthatitstartsatthelastknownplace,sothisfunctionusuallyreturnsimmediately: intLastUsedParticle=0; //FindsaParticleinParticlesContainerwhichisn'tusedyet. //(i.e.life<0); intFindUnusedParticle(){ for(inti=LastUsedParticle;i(int)(0.016f*10000.0)) newparticles=(int)(0.016f*10000.0); Deletingoldparticles There’satrick,seebelow=) Themainsimulationloop ParticlesContainercontainsbothactiveand“dead”particles,butthebufferthatwesendtotheGPUneedstohaveonlylivingparticles. Sowewilliterateoneachparticle,checkifitisalive,ifitmustdie,andifeverythingisallright,addsomegravity,andfinallycopyitinaGPU-specificbuffer. //Simulateallparticles intParticlesCount=0; for(inti=0;i0.0f){ //Decreaselife p.life-=delta; if(p.life>0.0f){ //Simulatesimplephysics:gravityonly,nocollisions p.speed+=glm::vec3(0.0f,-9.81f,0.0f)*(float)delta*0.5f; p.pos+=p.speed*(float)delta; p.cameradistance=glm::length2(p.pos-CameraPosition); //ParticlesContainer[i].pos+=glm::vec3(0.0f,10.0f,0.0f)*(float)delta; //FilltheGPUbuffer g_particule_position_size_data[4*ParticlesCount+0]=p.pos.x; g_particule_position_size_data[4*ParticlesCount+1]=p.pos.y; g_particule_position_size_data[4*ParticlesCount+2]=p.pos.z; g_particule_position_size_data[4*ParticlesCount+3]=p.size; g_particule_color_data[4*ParticlesCount+0]=p.r; g_particule_color_data[4*ParticlesCount+1]=p.g; g_particule_color_data[4*ParticlesCount+2]=p.b; g_particule_color_data[4*ParticlesCount+3]=p.a; }else{ //ParticlesthatjustdiedwillbeputattheendofthebufferinSortParticles(); p.cameradistance=-1.0f; } ParticlesCount++; } } Thisiswhatyouget.Almostthere,butthere’saproblem… Sorting AsexplainedinTutorial10,youneedtosortsemi-transparentobjectsfrombacktofrontfortheblendingtobecorrect. voidSortParticles(){ std::sort(&ParticlesContainer[0],&ParticlesContainer[MaxParticles]); } Now,std::sortneedsafunctionthatcantellwhetheraParticlemustbeputbeforeorafteranotherParticleinthecontainer.ThiscanbedonewithParticle::operator<: structparticle ... booloperator returnthis->cameradistance>that.cameradistance; } }; ThiswillmakeParticleContainerbesorted,andtheparticlesnowdisplaycorrectly*: Goingfurther Animatedparticles Youcananimateyourparticles’texturewithatextureatlas.Sendtheageofeachparticlealongwiththeposition,andintheshaders,computetheUVslikewedidforthe2Dfonttutorial.Atextureatlaslookslikethis: Handlingseveralparticlesystems Ifyouneedmorethanoneparticlesystem,youhavetwooptions:eitheruseasingleParticleContainer,oronepersystem. IfyouhaveasinglecontainerforALLparticles,thenyouwillbeabletosortthemperfectly.Themaindrawbackisthatyou’llhavetousethesametextureforallparticles,whichisabigproblem.Thiscanbesolvedbyusingatextureatlas(onebigtexturewithallyourdifferenttexturesonit,justusedifferentUVs),butit’snotreallyhandytoeditanduse. Ifyouhaveonecontainerperparticlesystem,ontheotherhand,particleswillonlybesortedinsidethesecontainers:iftwoparticlesytemsoverlap,artefactswillstarttoappear.Dependingonyourapplication,thismightnotbeaproblem. Ofcourse,youcanalsousesomekindofhybridsystemwithseveralparticlesystems,eachwitha(smallandmanageable)atlas. Smoothparticles You’llnoticeverysoonacommonartifact:whenyourparticleintersectsomegeometry,thelimitbecomesveryvisibleandugly: (imagefromhttp://www.gamerendering.com/2009/09/16/soft-particles/) Acommontechniquetosolvethisistotestifthecurrently-drawnfragmentisneartheZ-Buffer.Ifso,thefragmentisfadedout. However,you’llhavetosampletheZ-Buffer,whichisnotpossiblewiththe“normal”Z-Buffer.Youneedtorenderyoursceneinarendertarget.Alternatively,youcancopytheZ-BufferfromoneframebuffertoanotherwithglBlitFramebuffer. http://developer.download.nvidia.com/whitepapers/2007/SDK10/SoftParticles_hi.pdf Improvingfillrate OneofthemostlimitingfactorinmodernGPUsisfillrate:theamountoffragments(pixels)itcanwriteinthe16.6msallowedtoget60FPS. Thisisaproblem,becauseparticlestypicallyneedaLOToffillrate,sinceyoucanre-drawthesamefragment10times,eachtimewithanotherparticle;andifyoudon’tdothat,yougetthesameartifactsasabove. Amongstallthefragmentsthatarewritten,manyarecompletelyuseless:theseontheborder.Yourparticletexturesareoftencompletelytransparentontheedges,buttheparticle’smeshwillstilldrawthem-andupdatethecolorbufferwithexactlythesamevaluethanbefore. Thissmallutilitycomputesamesh(theoneyou’resupposedtodrawwithglDrawArraysInstanced())thattightlyfitsyourtexture: http://www.humus.name/index.php?page=Cool&ID=8.EmilPerson’ssitehasplentyofotherfascinatingarticles,too. Particlephysics Atsomepoint,you’llprobablywantyourparticlestointeractsomemorewithyourworld.Inparticular,particlescouldreboundontheground. Youcouldsimplylauncharaycastforeachparticle,betweenthecurrentpositionandthefutureone;welearnttodothisinthePickingtutorials.Butthisisextremelyexpensive,youjustcan’ttothisforeachparticle,eachframe. Dependingonyourapplication,youcaneitherapproximateyourgeometrywithasetofplanesanddotheraycastontheseplanesonly;Or,youcanuserealraycast,butcachetheresultsandapproximatenearbycollisionswiththecache(or,youcandoboth). AcompletelydifferenttechniqueistousetheexistingZ-Bufferasaveryroughapproximationofthe(visible)geometry,andcollideparticlesonthis.Thisis“goodenough”andfast,butyou’llhavetodoallyoursimulationontheGPU,sinceyoucan’taccesstheZ-BufferontheCPU(atleastnotfast),soit’swaymorecomplicated. Hereareafewlinksaboutthesetechniques: http://www.altdevblogaday.com/2012/06/19/hack-day-report/ http://www.gdcvault.com/search.php#&category=free&firstfocus=&keyword=Chris+Tchou’s%2BHalo%2BReach%2BEffects&conference_id= GPUSimulation Assaidabove,youcansimulatetheparticles’movementscompletelyontheGPU.Youwillstillhavetomanageyourparticle’slifecycleontheCPU-atleasttospawnthem. Youhavemanyoptionstodothis,andnoneinthescopeofthistutorial;I’lljustgiveafewpointers. UseTransformFeedback.ItallowsyoutostoretheoutputsofavertexshaderinaGPU-sideVBO.StorethenewpositionsinthisVBO,andnextframe,usethisVBOasthestartingpoint,andstorethenewpositionintheformerVBO. SamethingbutwithoutTransformFeedback:encodeyourparticles’positionsinatexture,andupdateitwithRender-To-Texture. UseaGeneral-PurposeGPUlibrary:CUDAorOpenCL,whichhaveinteroperabilityfunctionswithOpenGL. UseaComputeShader.Cleanestsolution,butonlyavailableonveryrecentGPUs. Notethatforsimplicity,inthisimplementation,ParticleContainerissortedafterupdatingtheGPUbuffers.Thismakestheparticlesnotexactlysorted(thereisaone-framedelay),butit’snotreallynoticeable.Youcanfixitbysplittingthemainloopin2:Simulate,Sort,andupdate. ◀Billboards



請為這篇文章評分?