Advanced GLSL - LearnOpenGL

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

GLSL gives us two interesting input variables called gl_FragCoord and ... way you can bind only a specific range of the uniform buffer to a binding point. Ifyou'rerunningAdBlock,pleaseconsiderwhitelistingthissiteifyou'dliketosupportLearnOpenGL;andnoworries,Iwon'tbemadifyoudon't:) IntroductionGettingstartedOpenGLCreatingawindowHelloWindowHelloTriangleShadersTexturesTransformationsCoordinateSystemsCameraReviewLightingColorsBasicLightingMaterialsLightingmapsLightcastersMultiplelightsReviewModelLoadingAssimpMeshModelAdvancedOpenGLDepthtestingStenciltestingBlendingFacecullingFramebuffersCubemapsAdvancedDataAdvancedGLSLGeometryShaderInstancingAntiAliasingAdvancedLightingAdvancedLightingGammaCorrectionShadowsShadowMappingPointShadowsNormalMappingParallaxMappingHDRBloomDeferredShadingSSAOPBRTheoryLightingIBLDiffuseirradianceSpecularIBLInPracticeDebuggingTextRendering2DGameBreakoutSettingupRenderingSpritesLevelsCollisionsBallCollisiondetectionCollisionresolutionParticlesPostprocessingPowerupsAudioRendertextFinalthoughtsGuestArticlesHowtopublish2020OITIntroductionWeightedBlendedSkeletalAnimation2021CSMSceneSceneGraphFrustumCullingTessellationHeightmapTessellationDSACoderepositoryTranslationsAbout BTC 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa ETH/ERC20 0x1de59bd9e52521a46309474f8372531533bd7c43 AdvancedGLSL Advanced-OpenGL/Advanced-GLSL Thischapterwon'treallyshowyousuperadvancedcoolnewfeaturesthatgiveanenormousboosttoyourscene'svisualquality.ThischaptergoesmoreorlessintosomeinterestingaspectsofGLSLandsomenicetricksthatmayhelpyouinyourfutureendeavors.BasicallysomegoodtoknowsandfeaturesthatmaymakeyourlifeeasierwhencreatingOpenGLapplicationsincombinationwithGLSL. We'lldiscusssomeinterestingbuilt-invariables,newwaystoorganizeshaderinputandoutput,andaveryusefultoolcalleduniformbufferobjects. GLSL'sbuilt-invariables Shadersareextremelypipelined,ifweneeddatafromanyothersourceoutsideofthecurrentshaderwe'llhavetopassdataaround.Welearnedtodothisviavertexattributes,uniforms,andsamplers.TherearehoweverafewextravariablesdefinedbyGLSLprefixedwithgl_thatgiveusanextrameanstogatherand/orwritedata.We'vealreadyseentwooftheminthechapterssofar:gl_Positionthatistheoutputvectorofthevertexshader,andthefragmentshader'sgl_FragCoord. We'lldiscussafewinterestingbuilt-ininputandoutputvariablesthatarebuilt-ininGLSLandexplainhowtheymaybenefitus.Notethatwewon'tdiscussallbuilt-invariablesthatexistinGLSLsoifyouwanttoseeallbuilt-invariablesyoucancheckOpenGL'swiki. Vertexshadervariables We'vealreadyseengl_Positionwhichistheclip-spaceoutputpositionvectorofthevertexshader.Settinggl_Positioninthevertexshaderisastrictrequirementifyouwanttorenderanythingonthescreen.Nothingwehaven'tseenbefore. gl_PointSize Oneoftherenderprimitiveswe'reabletochoosefromisGL_POINTSinwhichcaseeachsinglevertexisaprimitiveandrenderedasapoint.ItispossibletosetthesizeofthepointsbeingrenderedviaOpenGL'sglPointSizefunction,butwecanalsoinfluencethisvalueinthevertexshader. OneoutputvariabledefinedbyGLSLiscalledgl_PointSizethatisafloatvariablewhereyoucansetthepoint'swidthandheightinpixels.Bysettingthepoint'ssizeinthevertexshaderwegetper-vertexcontroloverthispoint'sdimensions. Influencingthepointsizesinthevertexshaderisdisabledbydefault,butifyouwanttoenablethisyou'llhavetoenableOpenGL'sGL_PROGRAM_POINT_SIZE: glEnable(GL_PROGRAM_POINT_SIZE); Asimpleexampleofinfluencingpointsizesisbysettingthepointsizeequaltotheclip-spaceposition'szvaluewhichisequaltothevertex'sdistancetotheviewer.Thepointsizeshouldthenincreasethefurtherwearefromtheverticesastheviewer. voidmain() { gl_Position=projection*view*model*vec4(aPos,1.0); gl_PointSize=gl_Position.z; } Theresultisthatthepointswe'vedrawnarerenderedlargerthemorewemoveawayfromthem: Youcanimaginethatvaryingthepointsizepervertexisinterestingfortechniqueslikeparticlegeneration. gl_VertexID Thegl_Positionandgl_PointSizeareoutputvariablessincetheirvalueisreadasoutputfromthevertexshader;wecaninfluencetheresultbywritingtothem.Thevertexshaderalsogivesusaninterestinginputvariable,thatwecanonlyreadfrom,calledgl_VertexID. Theintegervariablegl_VertexIDholdsthecurrentIDofthevertexwe'redrawing.Whendoingindexedrendering(withglDrawElements)thisvariableholdsthecurrentindexofthevertexwe'redrawing.Whendrawingwithoutindices(viaglDrawArrays)thisvariableholdsthenumberofthecurrentlyprocessedvertexsincethestartoftherendercall. Fragmentshadervariables Withinthefragmentshaderwealsohaveaccesstosomeinterestingvariables.GLSLgivesustwointerestinginputvariablescalledgl_FragCoordandgl_FrontFacing. gl_FragCoord We'veseenthegl_FragCoordacoupleoftimesbeforeduringthediscussionofdepthtesting,becausethezcomponentofthegl_FragCoordvectorisequaltothedepthvalueofthatparticularfragment.However,wecanalsousethexandycomponentofthatvectorforsomeinterestingeffects. Thegl_FragCoord'sxandycomponentarethewindow-orscreen-spacecoordinatesofthefragment,originatingfromthebottom-leftofthewindow.Wespecifiedarenderwindowof800x600withglViewportsothescreen-spacecoordinatesofthefragmentwillhavexvaluesbetween0and800,andyvaluesbetween0and600. Usingthefragmentshaderwecouldcalculateadifferentcolorvaluebasedonthescreencoordinateofthefragment.Acommonusageforthegl_FragCoordvariableisforcomparingvisualoutputofdifferentfragmentcalculations,asusuallyseenintechdemos.Wecouldforexamplesplitthescreenintwobyrenderingoneoutputtotheleftsideofthewindowandanotheroutputtotherightsideofthewindow.Anexamplefragmentshaderthatoutputsadifferentcolorbasedonthefragment'sscreencoordinatesisgivenbelow: voidmain() { if(gl_FragCoord.x<400) FragColor=vec4(1.0,0.0,0.0,1.0); else FragColor=vec4(0.0,1.0,0.0,1.0); } Becausethewidthofthewindowisequalto800,wheneverapixel'sx-coordinateislessthan400itmustbeattheleftsideofthewindowandwe'llgivethatfragmentadifferentcolor. Wecannowcalculatetwocompletelydifferentfragmentshaderresultsanddisplayeachofthemonadifferentsideofthewindow.Thisisgreatfortestingoutdifferentlightingtechniquesforexample. gl_FrontFacing Anotherinterestinginputvariableinthefragmentshaderisthegl_FrontFacingvariable.InthefacecullingchapterwementionedthatOpenGLisabletofigureoutifafaceisafrontorbackfaceduetothewindingorderofthevertices.Thegl_FrontFacingvariabletellsusifthecurrentfragmentispartofafront-facingoraback-facingface.Wecould,forexample,decidetooutputdifferentcolorsforallbackfaces. Thegl_FrontFacingvariableisaboolthatistrueifthefragmentispartofafrontfaceandfalseotherwise.Wecouldcreateacubethiswaywithadifferenttextureontheinsidethanontheoutside: #version330core outvec4FragColor; invec2TexCoords; uniformsampler2DfrontTexture; uniformsampler2DbackTexture; voidmain() { if(gl_FrontFacing) FragColor=texture(frontTexture,TexCoords); else FragColor=texture(backTexture,TexCoords); } Ifwetakeapeekinsidethecontainerwecannowseeadifferenttexturebeingused. Notethatifyouenabledfacecullingyouwon'tbeabletoseeanyfacesinsidethecontainerandusinggl_FrontFacingwouldthenbepointless. gl_FragDepth Theinputvariablegl_FragCoordisaninputvariablethatallowsustoreadscreen-spacecoordinatesandgetthedepthvalueofthecurrentfragment,butitisaread-onlyvariable.Wecan'tinfluencethescreen-spacecoordinatesofthefragment,butitispossibletosetthedepthvalueofthefragment.GLSLgivesusanoutputvariablecalledgl_FragDepththatwecanusetomanuallysetthedepthvalueofthefragmentwithintheshader. Tosetthedepthvalueintheshaderwewriteanyvaluebetween0.0and1.0totheoutputvariable: gl_FragDepth=0.0;//thisfragmentnowhasadepthvalueof0.0 Iftheshaderdoesnotwriteanythingtogl_FragDepth,thevariablewillautomaticallytakeitsvaluefromgl_FragCoord.z. Settingthedepthvaluemanuallyhasamajordisadvantagehowever.ThatisbecauseOpenGLdisablesearlydepthtesting(asdiscussedinthedepthtestingchapter)assoonaswewritetogl_FragDepthinthefragmentshader.Itisdisabled,becauseOpenGLcannotknowwhatdepthvaluethefragmentwillhavebeforewerunthefragmentshader,sincethefragmentshadermayactuallychangethisvalue. Bywritingtogl_FragDepthyoushouldtakethisperformancepenaltyintoconsideration.FromOpenGL4.2however,wecanstillsortofmediatebetweenbothsidesbyredeclaringthegl_FragDepthvariableatthetopofthefragmentshaderwithadepthcondition: layout(depth_&ltcondition>)outfloatgl_FragDepth; Thisconditioncantakethefollowingvalues: Condition Description any Thedefaultvalue.Earlydepthtestingisdisabled. greater Youcanonlymakethedepthvaluelargercomparedtogl_FragCoord.z. less Youcanonlymakethedepthvaluesmallercomparedtogl_FragCoord.z. unchanged Ifyouwritetogl_FragDepth,youwillwriteexactlygl_FragCoord.z. Byspecifyinggreaterorlessasthedepthcondition,OpenGLcanmaketheassumptionthatyou'llonlywritedepthvalueslargerorsmallerthanthefragment'sdepthvalue.ThiswayOpenGLisstillabletodoearlydepthtestingwhenthedepthbuffervalueispartoftheotherdirectionofgl_FragCoord.z. Anexampleofwhereweincreasethedepthvalueinthefragmentshader,butstillwanttopreservesomeoftheearlydepthtestingisshowninthefragmentshaderbelow: #version420core//notetheGLSLversion! outvec4FragColor; layout(depth_greater)outfloatgl_FragDepth; voidmain() { FragColor=vec4(1.0); gl_FragDepth=gl_FragCoord.z+0.1; } DonotethatthisfeatureisonlyavailablefromOpenGLversion4.2orhigher. Interfaceblocks Sofar,everytimewesentdatafromthevertextothefragmentshaderwedeclaredseveralmatchinginput/outputvariables.Declaringtheseoneatatimeistheeasiestwaytosenddatafromoneshadertoanother,butasapplicationsbecomelargeryouprobablywanttosendmorethanafewvariablesover. TohelpusorganizethesevariablesGLSLoffersussomethingcalledinterfaceblocksthatallowsustogroupvariablestogether.Thedeclarationofsuchaninterfaceblocklooksalotlikeastructdeclaration,exceptthatitisnowdeclaredusinganinoroutkeywordbasedontheblockbeinganinputoranoutputblock. #version330core layout(location=0)invec3aPos; layout(location=1)invec2aTexCoords; uniformmat4model; uniformmat4view; uniformmat4projection; outVS_OUT { vec2TexCoords; }vs_out; voidmain() { gl_Position=projection*view*model*vec4(aPos,1.0); vs_out.TexCoords=aTexCoords; } Thistimewedeclaredaninterfaceblockcalledvs_outthatgroupstogetheralltheoutputvariableswewanttosendtothenextshader.Thisiskindofatrivialexample,butyoucanimaginethatthishelpsorganizeyourshaders'inputs/outputs.Itisalsousefulwhenwewanttogroupshaderinput/outputintoarraysaswe'llseeinthenextchapteraboutgeometryshaders. Thenwealsoneedtodeclareaninputinterfaceblockinthenextshaderwhichisthefragmentshader.Theblockname(VS_OUT)shouldbethesameinthefragmentshader,buttheinstancename(vs_outasusedinthevertexshader)canbeanythingwelike-avoidingconfusingnameslikevs_outforafragmentstructcontaininginputvalues. #version330core outvec4FragColor; inVS_OUT { vec2TexCoords; }fs_in; uniformsampler2Dtexture; voidmain() { FragColor=texture(texture,fs_in.TexCoords); } Aslongasbothinterfaceblocknamesareequal,theircorrespondinginputandoutputismatchedtogether.Thisisanotherusefulfeaturethathelpsorganizeyourcodeandprovesusefulwhencrossingbetweencertainshaderstageslikethegeometryshader. Uniformbufferobjects We'vebeenusingOpenGLforquiteawhilenowandlearnedsomeprettycooltricks,butalsoafewannoyances.Forexample,whenusingmorethanoneshaderwecontinuouslyhavetosetuniformvariableswheremostofthemareexactlythesameforeachshader. OpenGLgivesusatoolcalleduniformbufferobjectsthatallowustodeclareasetofglobaluniformvariablesthatremainthesameoveranynumberofshaderprograms.WhenusinguniformbufferobjectswesettherelevantuniformsonlyonceinfixedGPUmemory.Wedostillhavetomanuallysettheuniformsthatareuniquepershader.Creatingandconfiguringauniformbufferobjectrequiresabitofworkthough. BecauseauniformbufferobjectisabufferlikeanyotherbufferwecancreateoneviaglGenBuffers,bindittotheGL_UNIFORM_BUFFERbuffertargetandstorealltherelevantuniformdataintothebuffer.Therearecertainrulesastohowthedataforuniformbufferobjectsshouldbestoredandwe'llgettothatlater.First,we'lltakeasimplevertexshaderandstoreourprojectionandviewmatrixinasocalleduniformblock: #version330core layout(location=0)invec3aPos; layout(std140)uniformMatrices { mat4projection; mat4view; }; uniformmat4model; voidmain() { gl_Position=projection*view*model*vec4(aPos,1.0); } Inmostofoursampleswesetaprojectionandviewuniformmatrixeveryframeforeachshaderwe'reusing.Thisisaperfectexampleofwhereuniformbufferobjectsbecomeusefulsincenowweonlyhavetostorethesematricesonce. HerewedeclaredauniformblockcalledMatricesthatstorestwo4x4matrices.Variablesinauniformblockcanbedirectlyaccessedwithouttheblocknameasaprefix.ThenwestorethesematrixvaluesinabuffersomewhereintheOpenGLcodeandeachshaderthatdeclaresthisuniformblockhasaccesstothematrices. You'reprobablywonderingrightnowwhatthelayout(std140)statementmeans.Whatthissaysisthatthecurrentlydefineduniformblockusesaspecificmemorylayoutforitscontent;thisstatementsetstheuniformblocklayout. Uniformblocklayout Thecontentofauniformblockisstoredinabufferobject,whichiseffectivelynothingmorethanareservedpieceofglobalGPUmemory.Becausethispieceofmemoryholdsnoinformationonwhatkindofdataitholds,weneedtotellOpenGLwhatpartsofthememorycorrespondtowhichuniformvariablesintheshader. Imaginethefollowinguniformblockinashader: layout(std140)uniformExampleBlock { floatvalue; vec3vector; mat4matrix; floatvalues[3]; boolboolean; intinteger; }; Whatwewanttoknowisthesize(inbytes)andtheoffset(fromthestartoftheblock)ofeachofthesevariablessowecanplacetheminthebufferintheirrespectiveorder.ThesizeofeachoftheelementsisclearlystatedinOpenGLanddirectlycorrespondstoC++datatypes;vectorsandmatricesbeing(large)arraysoffloats.WhatOpenGLdoesn'tclearlystateisthespacingbetweenthevariables.Thisallowsthehardwaretopositionorpadvariablesasitseesfit.Thehardwareisabletoplaceavec3adjacenttoafloatforexample.Notallhardwarecanhandlethisandpadsthevec3toanarrayof4floatsbeforeappendingthefloat.Agreatfeature,butinconvenientforus. Bydefault,GLSLusesauniformmemorylayoutcalledasharedlayout-sharedbecauseoncetheoffsetsaredefinedbythehardware,theyareconsistentlysharedbetweenmultipleprograms.WithasharedlayoutGLSLisallowedtorepositiontheuniformvariablesforoptimizationaslongasthevariables'orderremainsintact.Becausewedon'tknowatwhatoffseteachuniformvariablewillbewedon'tknowhowtopreciselyfillouruniformbuffer.WecanquerythisinformationwithfunctionslikeglGetUniformIndices,butthat'snottheapproachwe'regoingtotakeinthischapter. Whileasharedlayoutgivesussomespace-savingoptimizations,we'dneedtoquerytheoffsetforeachuniformvariablewhichtranslatestoalotofwork.Thegeneralpracticehoweveristonotusethesharedlayout,buttousethestd140layout.Thestd140layoutexplicitlystatesthememorylayoutforeachvariabletypebystandardizingtheirrespectiveoffsetsgovernedbyasetofrules.Sincethisisstandardizedwecanmanuallyfigureouttheoffsetsforeachvariable. Eachvariablehasabasealignmentequaltothespaceavariabletakes(includingpadding)withinauniformblockusingthestd140layoutrules.Foreachvariable,wecalculateitsalignedoffset:thebyteoffsetofavariablefromthestartoftheblock.Thealignedbyteoffsetofavariablemustbeequaltoamultipleofitsbasealignment.Thisisabitofamouthful,butwe'llgettoseesomeexamplessoonenoughtoclearthingsup. TheexactlayoutrulescanbefoundatOpenGL'suniformbufferspecificationhere,butwe'lllistthemostcommonrulesbelow.EachvariabletypeinGLSLsuchasint,floatandboolaredefinedtobefour-bytequantitieswitheachentityof4bytesrepresentedasN. Type Layoutrule Scalare.g.intorbool EachscalarhasabasealignmentofN. Vector Either2Nor4N.Thismeansthatavec3hasabasealignmentof4N. Arrayofscalarsorvectors Eachelementhasabasealignmentequaltothatofavec4. Matrices Storedasalargearrayofcolumnvectors,whereeachofthosevectorshasabasealignmentofvec4. Struct Equaltothecomputedsizeofitselementsaccordingtothepreviousrules,butpaddedtoamultipleofthesizeofavec4. LikemostofOpenGL'sspecificationsit'seasiertounderstandwithanexample.We'retakingtheuniformblockcalledExampleBlockweintroducedearlierandcalculatethealignedoffsetforeachofitsmembersusingthestd140layout: layout(std140)uniformExampleBlock { //basealignment//alignedoffset floatvalue;//4//0 vec3vector;//16//16(offsetmustbemultipleof16so4->16) mat4matrix;//16//32(column0) //16//48(column1) //16//64(column2) //16//80(column3) floatvalues[3];//16//96(values[0]) //16//112(values[1]) //16//128(values[2]) boolboolean;//4//144 intinteger;//4//148 }; Asanexercise,trytocalculatetheoffsetvaluesyourselfandcomparethemtothistable.Withthesecalculatedoffsetvalues,basedontherulesofthestd140layout,wecanfillthebufferwithdataattheappropriateoffsetsusingfunctionslikeglBufferSubData.Whilenotthemostefficient,thestd140layoutdoesguaranteeusthatthememorylayoutremainsthesameovereachprogramthatdeclaredthisuniformblock. Byaddingthestatementlayout(std140)inthedefinitionoftheuniformblockwetellOpenGLthatthisuniformblockusesthestd140layout.Therearetwootherlayoutstochoosefromthatrequireustoqueryeachoffsetbeforefillingthebuffers.We'vealreadyseenthesharedlayout,withtheotherremaininglayoutbeingpacked.Whenusingthepackedlayout,thereisnoguaranteethatthelayoutremainsthesamebetweenprograms(notshared)becauseitallowsthecompilertooptimizeuniformvariablesawayfromtheuniformblockwhichmaydifferpershader. Usinguniformbuffers We'vedefineduniformblocksandspecifiedtheirmemorylayout,butwehaven'tdiscussedhowtoactuallyusethemyet. First,weneedtocreateauniformbufferobjectwhichisdoneviathefamiliarglGenBuffers.OncewehaveabufferobjectwebindittotheGL_UNIFORM_BUFFERtargetandallocateenoughmemorybycallingglBufferData. unsignedintuboExampleBlock; glGenBuffers(1,&uboExampleBlock); glBindBuffer(GL_UNIFORM_BUFFER,uboExampleBlock); glBufferData(GL_UNIFORM_BUFFER,152,NULL,GL_STATIC_DRAW);//allocate152bytesofmemory glBindBuffer(GL_UNIFORM_BUFFER,0); Nowwheneverwewanttoupdateorinsertdataintothebuffer,webindtouboExampleBlockanduseglBufferSubDatatoupdateitsmemory.Weonlyhavetoupdatethisuniformbufferonce,andallshadersthatusethisbuffernowuseitsupdateddata.But,howdoesOpenGLknowwhatuniformbufferscorrespondtowhichuniformblocks? IntheOpenGLcontextthereisanumberofbindingpointsdefinedwherewecanlinkauniformbufferto.Oncewecreatedauniformbufferwelinkittooneofthosebindingpointsandwealsolinktheuniformblockintheshadertothesamebindingpoint,effectivelylinkingthemtogether.Thefollowingdiagramillustratesthis: Asyoucanseewecanbindmultipleuniformbufferstodifferentbindingpoints.BecauseshaderAandshaderBbothhaveauniformblocklinkedtothesamebindingpoint0,theiruniformblockssharethesameuniformdatafoundinuboMatrices;arequirementbeingthatbothshadersdefinedthesameMatricesuniformblock. TosetashaderuniformblocktoaspecificbindingpointwecallglUniformBlockBindingthattakesaprogramobject,auniformblockindex,andthebindingpointtolinkto.Theuniformblockindexisalocationindexofthedefineduniformblockintheshader.ThiscanberetrievedviaacalltoglGetUniformBlockIndexthatacceptsaprogramobjectandthenameoftheuniformblock.WecansettheLightsuniformblockfromthediagramtobindingpoint2asfollows: unsignedintlights_index=glGetUniformBlockIndex(shaderA.ID,"Lights"); glUniformBlockBinding(shaderA.ID,lights_index,2); Notethatwehavetorepeatthisprocessforeachshader. FromOpenGLversion4.2andonwardsitisalsopossibletostorethebindingpointofauniformblockexplicitlyintheshaderbyaddinganotherlayoutspecifier,savingusthecallstoglGetUniformBlockIndexandglUniformBlockBinding.ThefollowingcodesetsthebindingpointoftheLightsuniformblockexplicitly: layout(std140,binding=2)uniformLights{...}; ThenwealsoneedtobindtheuniformbufferobjecttothesamebindingpointandthiscanbeaccomplishedwitheitherglBindBufferBaseorglBindBufferRange. glBindBufferBase(GL_UNIFORM_BUFFER,2,uboExampleBlock); //or glBindBufferRange(GL_UNIFORM_BUFFER,2,uboExampleBlock,0,152); ThefunctionglBindbufferBaseexpectsatarget,abindingpointindexandauniformbufferobject.ThisfunctionlinksuboExampleBlocktobindingpoint2;fromthispointon,bothsidesofthebindingpointarelinked.YoucanalsouseglBindBufferRangethatexpectsanextraoffsetandsizeparameter-thiswayyoucanbindonlyaspecificrangeoftheuniformbuffertoabindingpoint.UsingglBindBufferRangeyoucouldhavemultipledifferentuniformblockslinkedtoasingleuniformbufferobject. Nowthateverythingissetup,wecanstartaddingdatatotheuniformbuffer.Wecouldaddallthedataasasinglebytearray,orupdatepartsofthebufferwheneverwefeellikeitusingglBufferSubData.Toupdatetheuniformvariablebooleanwecouldupdatetheuniformbufferobjectasfollows: glBindBuffer(GL_UNIFORM_BUFFER,uboExampleBlock); intb=true;//boolsinGLSLarerepresentedas4bytes,sowestoreitinaninteger glBufferSubData(GL_UNIFORM_BUFFER,144,4,&b); glBindBuffer(GL_UNIFORM_BUFFER,0); Andthesameprocedureappliesforalltheotheruniformvariablesinsidetheuniformblock,butwithdifferentrangearguments. Asimpleexample Solet'sdemonstratearealexampleofuniformbufferobjects.Ifwelookbackatallthepreviouscodesampleswe'vecontinuallybeenusing3matrices:theprojection,viewandmodelmatrix.Ofallthosematrices,onlythemodelmatrixchangesfrequently.Ifwehavemultipleshadersthatusethissamesetofmatrices,we'dprobablybebetteroffusinguniformbufferobjects. We'regoingtostoretheprojectionandviewmatrixinauniformblockcalledMatrices.We'renotgoingtostorethemodelmatrixintheresincethemodelmatrixtendstochangefrequentlybetweenshaders,sowewouldn'treallybenefitfromuniformbufferobjects. #version330core layout(location=0)invec3aPos; layout(std140)uniformMatrices { mat4projection; mat4view; }; uniformmat4model; voidmain() { gl_Position=projection*view*model*vec4(aPos,1.0); } Notmuchgoingonhere,exceptthatwenowuseauniformblockwithastd140layout.Whatwe'regoingtodoinoursampleapplicationisdisplay4cubeswhereeachcubeisdisplayedwithadifferentshaderprogram.Eachofthe4shaderprogramsusesthesamevertexshader,buthasauniquefragmentshaderthatonlyoutputsasinglecolorthatdifferspershader. First,wesettheuniformblockofthevertexshadersequaltobindingpoint0.Notethatwehavetodothisforeachshader: unsignedintuniformBlockIndexRed=glGetUniformBlockIndex(shaderRed.ID,"Matrices"); unsignedintuniformBlockIndexGreen=glGetUniformBlockIndex(shaderGreen.ID,"Matrices"); unsignedintuniformBlockIndexBlue=glGetUniformBlockIndex(shaderBlue.ID,"Matrices"); unsignedintuniformBlockIndexYellow=glGetUniformBlockIndex(shaderYellow.ID,"Matrices"); glUniformBlockBinding(shaderRed.ID,uniformBlockIndexRed,0); glUniformBlockBinding(shaderGreen.ID,uniformBlockIndexGreen,0); glUniformBlockBinding(shaderBlue.ID,uniformBlockIndexBlue,0); glUniformBlockBinding(shaderYellow.ID,uniformBlockIndexYellow,0); Nextwecreatetheactualuniformbufferobjectandbindthatbuffertobindingpoint0: unsignedintuboMatrices glGenBuffers(1,&uboMatrices); glBindBuffer(GL_UNIFORM_BUFFER,uboMatrices); glBufferData(GL_UNIFORM_BUFFER,2*sizeof(glm::mat4),NULL,GL_STATIC_DRAW); glBindBuffer(GL_UNIFORM_BUFFER,0); glBindBufferRange(GL_UNIFORM_BUFFER,0,uboMatrices,0,2*sizeof(glm::mat4)); Firstweallocateenoughmemoryforourbufferwhichisequalto2timesthesizeofglm::mat4.ThesizeofGLM'smatrixtypescorresponddirectlytomat4inGLSL.Thenwelinkaspecificrangeofthebuffer,inthiscasetheentirebuffer,tobindingpoint0. Nowallthat'slefttodoisfillthebuffer.Ifwekeepthefieldofviewvalueconstantoftheprojectionmatrix(sonomorecamerazoom)weonlyhavetoupdateitonceinourapplication-thismeansweonlyhavetoinsertthisintothebufferonlyonceaswell.BecausewealreadyallocatedenoughmemoryinthebufferobjectwecanuseglBufferSubDatatostoretheprojectionmatrixbeforeweentertherenderloop: glm::mat4projection=glm::perspective(glm::radians(45.0f),(float)width/(float)height,0.1f,100.0f); glBindBuffer(GL_UNIFORM_BUFFER,uboMatrices); glBufferSubData(GL_UNIFORM_BUFFER,0,sizeof(glm::mat4),glm::value_ptr(projection)); glBindBuffer(GL_UNIFORM_BUFFER,0); Herewestorethefirsthalfoftheuniformbufferwiththeprojectionmatrix.Thenbeforewerendertheobjectseachframeweupdatethesecondhalfofthebufferwiththeviewmatrix: glm::mat4view=camera.GetViewMatrix(); glBindBuffer(GL_UNIFORM_BUFFER,uboMatrices); glBufferSubData(GL_UNIFORM_BUFFER,sizeof(glm::mat4),sizeof(glm::mat4),glm::value_ptr(view)); glBindBuffer(GL_UNIFORM_BUFFER,0); Andthat'sitforuniformbufferobjects.EachvertexshaderthatcontainsaMatricesuniformblockwillnowcontainthedatastoredinuboMatrices.Soifwenowweretodraw4cubesusing4differentshaders,theirprojectionandviewmatrixshouldbethesame: glBindVertexArray(cubeVAO); shaderRed.use(); glm::mat4model=glm::mat4(1.0f); model=glm::translate(model,glm::vec3(-0.75f,0.75f,0.0f)); //movetop-left shaderRed.setMat4("model",model); glDrawArrays(GL_TRIANGLES,0,36); //...drawGreenCube //...drawBlueCube //...drawYellowCube Theonlyuniformwestillneedtosetisthemodeluniform.Usinguniformbufferobjectsinascenariolikethissavesusfromquiteafewuniformcallspershader.Theresultlookssomethinglikethis: Eachofthecubesismovedtoonesideofthewindowbytranslatingthemodelmatrixand,thankstothedifferentfragmentshaders,theircolorsdifferperobject.Thisisarelativelysimplescenarioofwherewecoulduseuniformbufferobjects,butanylargerenderingapplicationcanhaveoverhundredsofshaderprogramsactivewhichiswhereuniformbufferobjectsreallystarttoshine. Youcanfindthefullsourcecodeoftheuniformexampleapplicationhere. Uniformbufferobjectshaveseveraladvantagesoversingleuniforms.First,settingalotofuniformsatonceisfasterthansettingmultipleuniformsoneatatime.Second,ifyouwanttochangethesameuniformoverseveralshaders,itismucheasiertochangeauniformonceinauniformbuffer.Onelastadvantagethatisnotimmediatelyapparentisthatyoucanusealotmoreuniformsinshadersusinguniformbufferobjects.OpenGLhasalimittohowmuchuniformdataitcanhandlewhichcanbequeriedwithGL_MAX_VERTEX_UNIFORM_COMPONENTS.Whenusinguniformbufferobjects,thislimitismuchhigher.Sowheneveryoureachamaximumnumberofuniforms(whendoingskeletalanimationforexample)there'salwaysuniformbufferobjects. HI



請為這篇文章評分?