Depth and stencils - OpenGL
文章推薦指數: 80 %
The stencil buffer is an optional extension of the depth buffer that gives you more control over the question of which fragments should be drawn and which ... Introduction Contextcreation Drawingpolygons Textures Transformations Depthandstencils Framebuffers Geometryshaders TransformFeedback Links OpenGLboilerplatecode Easy-to-buildcode Matrixmathtutorials OpenGLreference Extrabuffers Upuntilnowthereisonlyonetypeofoutputbufferyou'vemadeuseof,thecolorbuffer.Thischapterwilldiscusstwoadditionaltypes,thedepthbufferandthestencilbuffer.Foreachoftheseaproblemwillbepresentedandsubsequentlysolvedwiththatspecificbuffer. Preparations Tobestdemonstratetheuseofthesebuffers,let'sdrawacubeinsteadofaflatshape.Thevertexshaderneedstobemodifiedtoacceptathirdcoordinate: invec3position; ... gl_Position=proj*view*model*vec4(position,1.0); We'realsogoingtoneedtoalterthecoloragainlaterinthischapter,somakesurethefragmentshadermultipliesthetexturecolorbythecolorattribute: vec4texColor=mix(texture(texKitten,Texcoord), texture(texPuppy,Texcoord),0.5); outColor=vec4(Color,1.0)*texColor; Verticesarenow8floatsinsize,soyou'llhavetoupdatethevertexattributeoffsetsandstridesaswell.Finally,addtheextracoordinatetothevertexarray: floatvertices[]={ //XYZRGBUV -0.5f,0.5f,0.0f,1.0f,0.0f,0.0f,0.0f,0.0f, 0.5f,0.5f,0.0f,0.0f,1.0f,0.0f,1.0f,0.0f, 0.5f,-0.5f,0.0f,0.0f,0.0f,1.0f,1.0f,1.0f, -0.5f,-0.5f,0.0f,1.0f,1.0f,1.0f,0.0f,1.0f }; Confirmthatyou'vemadealltherequiredchangesbyrunningyourprogramandcheckingifitstilldrawsaflatspinningimageofakittenblendedwithapuppy.Asinglecubeconsistsof36vertices(6sides*2triangles*3vertices),soIwilleaseyourlifebyprovidingthearrayhere. glDrawArrays(GL_TRIANGLES,0,36); Wewillnotmakeuseofelementbuffersfordrawingthiscube,soyoucanuseglDrawArraystodrawit.Ifyouwereconfusedbythisexplanation,youcancompareyourprogramtothisreferencecode. Itimmediatelybecomesclearthatthecubeisnotrenderedasexpectedwhenseeingtheoutput.Thesidesofthecubearebeingdrawn,buttheyoverlapeachotherinstrangeways!TheproblemhereisthatwhenOpenGLdrawsyourcubetriangle-by-triangle,itwillsimplywriteoverpixelseventhoughsomethingelsemayhavebeendrawntherebefore.InthiscaseOpenGLwillhappilydrawtrianglesinthebackovertrianglesatthefront. LuckilyOpenGLofferswaysoftellingitwhentodrawoverapixelandwhennotto.I'llgooverthetwomostimportantwaysofdoingthat,depthtestingandstencilling,inthischapter. Depthbuffer Z-bufferingisawayofkeepingtrackofthedepthofeverypixelonthescreen.Thedepthisanincreasingfunctionofthedistancebetweenthescreenplaneandafragmentthathasbeendrawn.Thatmeansthatthefragmentsonthesidesofthecubefurtherawayfromtheviewerhaveahigherdepthvalue,whereasfragmentscloserhavealowerdepthvalue. Ifthisdepthisstoredalongwiththecolorwhenafragmentiswritten,fragmentsdrawnlatercancomparetheirdepthtotheexistingdepthtodetermineifthenewfragmentisclosertotheviewerthantheoldfragment.Ifthatisthecase,itshouldbedrawnoverandotherwiseitcansimplybediscarded.Thisisknownasdepthtesting. OpenGLoffersawaytostorethesedepthvaluesinanextrabuffer,calledthedepthbuffer,andperformtherequiredcheckforfragmentsautomatically.Thefragmentshaderwillnotrunforfragmentsthatareinvisible,whichcanhaveasignificantimpactonperformance.ThisfunctionalitycanbeenabledbycallingglEnable. glEnable(GL_DEPTH_TEST); Ifyouenablethisfunctionalitynowandrunyourapplication,you'llnoticethatyougetablackscreen.Thathappensbecausethedepthbufferisfilledwith0depthforeachpixelbydefault.Sincenofragmentswilleverbecloserthanthattheyarealldiscarded. ThedepthbuffercanbeclearedalongwiththecolorbufferbyextendingtheglClearcall: glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); Thedefaultclearvalueforthedepthis1.0f,whichisequaltothedepthofyourfarclippingplaneandthusthefurthestdepththatcanberepresented.Allfragmentswillbecloserthanthat,sotheywillnolongerbediscarded. Withthedepthtestcapabilityenabled,thecubeisnowrenderedcorrectly.Justlikethecolorbuffer,thedepthbufferhasacertainamountofbitsofprecisionwhichcanbespecifiedbyyou.Lessbitsofprecisionreducetheextramemoryuse,butcanintroducerenderingerrorsinmorecomplexscenes. Stencilbuffer Thestencilbufferisanoptionalextensionofthedepthbufferthatgivesyoumorecontroloverthequestionofwhichfragmentsshouldbedrawnandwhichshouldn't.Likethedepthbuffer,avalueisstoredforeverypixel,butthistimeyougettocontrolwhenandhowthisvaluechangesandwhenafragmentshouldbedrawndependingonthisvalue.Notethatifthedepthtestfails,thestenciltestnolongerdetermineswhetherafragmentisdrawnornot,butthesefragmentscanstillaffectvaluesinthestencilbuffer! Togetabitmoreacquaintedwiththestencilbufferbeforeusingit,let'sstartbyanalyzingasimpleexample. Inthiscasethestencilbufferwasfirstclearedwithzeroesandthenarectangleofoneswasdrawntoit.Thedrawingoperationofthecubeusesthevaluesfromthestencilbuffertoonlydrawfragmentswithastencilvalueof1. Nowthatyouhaveanunderstandingofwhatthestencilbufferdoes,we'lllookattherelevantOpenGLcalls. glEnable(GL_STENCIL_TEST); StenciltestingisenabledwithacalltoglEnable,justlikedepthtesting.Youdon'thavetoaddthiscalltoyourcodejustyet.I'llfirstgoovertheAPIdetailsinthenexttwosectionsandthenwe'llmakeacooldemo. Settingvalues Regulardrawingoperationsareusedtodeterminewhichvaluesinthestencilbufferareaffectedbyanystenciloperation.Ifyouwanttoaffectarectangleofvalueslikeinthesampleabove,simplydrawa2Dquadinthatarea.WhathappenstothosevaluescanbecontrolledbyyouusingtheglStencilFunc,glStencilOpandglStencilMaskfunctions. TheglStencilFunccallisusedtospecifytheconditionsunderwhichafragmentpassesthestenciltest.Itsparametersarediscussedbelow. func:Thetestfunction,canbeGL_NEVER,GL_LESS,GL_LEQUAL,GL_GREATER,GL_GEQUAL,GL_EQUAL,GL_NOTEQUAL,andGL_ALWAYS. ref:Avaluetocomparethestencilvaluetousingthetestfunction. mask:AbitwiseANDoperationisperformedonthestencilvalueandreferencevaluewiththismaskvaluebeforecomparingthem. Ifyoudon'twantstencilswithavaluelowerthan2tobeaffected,youwoulduse: glStencilFunc(GL_GEQUAL,2,0xFF); Themaskvalueissettoallones(incaseofan8bitstencilbuffer),soitwillnotaffectthetest. TheglStencilOpcallspecifieswhatshouldhappentostencilvaluesdependingontheoutcomeofthestencilanddepthtests.Theparametersare: sfail:Actiontotakeifthestenciltestfails. dpfail:Actiontotakeifthestenciltestissuccessful,butthedepthtestfailed. dppass:Actiontotakeifboththestenciltestanddepthtestspass. Stencilvaluescanbemodifiedinthefollowingways: GL_KEEP:Thecurrentvalueiskept. GL_ZERO:Thestencilvalueissetto0. GL_REPLACE:ThestencilvalueissettothereferencevalueintheglStencilFunccall. GL_INCR:Thestencilvalueisincreasedby1ifitislowerthanthemaximumvalue. GL_INCR_WRAP:SameasGL_INCR,withtheexceptionthatthevalueissetto0ifthemaximumvalueisexceeded. GL_DECR:Thestencilvalueisdecreasedby1ifitishigherthan0. GL_DECR_WRAP:SameasGL_DECR,withtheexceptionthatthevalueissettothemaximumvalueifthecurrentvalueis0(thestencilbufferstoresunsignedintegers). GL_INVERT:Abitwiseinvertisappliedtothevalue. Finally,glStencilMaskcanbeusedtocontrolthebitsthatarewrittentothestencilbufferwhenanoperationisrun.Thedefaultvalueisallones,whichmeansthattheoutcomeofanyoperationisunaffected. If,likeintheexample,youwanttosetallstencilvaluesinarectangularareato1,youwouldusethefollowingcalls: glStencilFunc(GL_ALWAYS,1,0xFF); glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE); glStencilMask(0xFF); Inthiscasetherectangleshouldn'tactuallybedrawntothecolorbuffer,sinceitisonlyusedtodeterminewhichstencilvaluesshouldbeaffected. glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE); glDepthMask(GL_FALSE); TheglColorMaskfunctionallowsyoutospecifywhichdataiswrittentothecolorbufferduringadrawingoperation.Inthiscaseyouwouldwanttodisableallcolorchannels(red,green,blue,alpha).WritingtothedepthbufferneedstobedisabledseparatelyaswellwithglDepthMask,sothatcubedrawingoperationwon'tbeaffectedbyleftoverdepthvaluesoftherectangle.Thisiscleanerthansimplyclearingthedepthbufferagainlater. Usingvaluesindrawingoperations Withtheknowledgeaboutsettingvalues,usingthemfortestingfragmentsindrawingoperationsbecomesverysimple.Allyouneedtodonowisre-enablecoloranddepthwritingifyouhaddisabledthoseearlierandsettingthetestfunctiontodeterminewhichfragmentsaredrawnbasedonthevaluesinthestencilbuffer. glStencilFunc(GL_EQUAL,1,0xFF); Ifyouusethiscalltosetthetestfunction,thestenciltestwillonlypassforpixelswithastencilvalueequalto1.Afragmentwillonlybedrawnifitpassesboththestencilanddepthtest,sosettingtheglStencilOpisnotnecessary.Inthecaseoftheexampleaboveonlythestencilvaluesintherectangularareaweresetto1,soonlythecubefragmentsinthatareawillbedrawn. glStencilMask(0x00); Onesmalldetailthatiseasytooverlookisthatthecubedrawcallcouldstillaffectvaluesinthestencilbuffer.Thisproblemcanbesolvedbysettingthestencilbitmasktoallzeroes,whicheffectivelydisablesstencilwriting. Planarreflections Let'sspiceupthedemowehaverightnowabitbyaddingafloorwithareflectionunderthecube.I'lladdtheverticesforthefloortothesamevertexbufferthecubeiscurrentlyusingtokeepthingssimple: floatvertices[]={ ... -1.0f,-1.0f,-0.5f,0.0f,0.0f,0.0f,0.0f,0.0f, 1.0f,-1.0f,-0.5f,0.0f,0.0f,0.0f,1.0f,0.0f, 1.0f,1.0f,-0.5f,0.0f,0.0f,0.0f,1.0f,1.0f, 1.0f,1.0f,-0.5f,0.0f,0.0f,0.0f,1.0f,1.0f, -1.0f,1.0f,-0.5f,0.0f,0.0f,0.0f,0.0f,1.0f, -1.0f,-1.0f,-0.5f,0.0f,0.0f,0.0f,0.0f,0.0f } Nowaddtheextradrawcalltoyourmainloop: glDrawArrays(GL_TRIANGLES,36,6); Tocreatethereflectionofthecubeitself,itissufficienttodrawitagainbutinvertedontheZ-axis: model=glm::scale( glm::translate(model,glm::vec3(0,0,-1)), glm::vec3(1,1,-1) ); glUniformMatrix4fv(uniModel,1,GL_FALSE,glm::value_ptr(model)); glDrawArrays(GL_TRIANGLES,0,36); I'vesetthecolorofthefloorverticestoblacksothatthefloordoesnotdisplaythetextureimage,soyou'llwanttochangetheclearcolortowhitetobeabletoseeit.I'vealsochangedthecameraparametersabittogetagoodviewofthescene. Twoissuesarenoticeableintherenderedimage: Theflooroccludesthereflectionbecauseofdepthtesting. Thereflectionisvisibleoutsideofthefloor. Thefirstproblemiseasytosolvebytemporarilydisablingwritingtothedepthbufferwhendrawingthefloor: glDepthMask(GL_FALSE); glDrawArrays(GL_TRIANGLES,36,6); glDepthMask(GL_TRUE); Tofixthesecondproblem,itisnecessarytodiscardfragmentsthatfalloutsideofthefloor.Soundslikeit'stimetoseewhatstenciltestingisreallyworth! Itcanbegreatlybeneficialattimeslikethesetomakealittlelistoftherenderingstagesofthescenetogetaproperideaofwhatisgoingon. Drawregularcube. Enablestenciltestingandsettestfunctionandoperationstowriteonestoallselectedstencils. Drawfloor. Setstencilfunctiontopassifstencilvalueequals1. Drawinvertedcube. Disablestenciltesting. Thenewdrawingcodelookslikethis: glEnable(GL_STENCIL_TEST); //Drawfloor glStencilFunc(GL_ALWAYS,1,0xFF);//Setanystencilto1 glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE); glStencilMask(0xFF);//Writetostencilbuffer glDepthMask(GL_FALSE);//Don'twritetodepthbuffer glClear(GL_STENCIL_BUFFER_BIT);//Clearstencilbuffer(0bydefault) glDrawArrays(GL_TRIANGLES,36,6); //Drawcubereflection glStencilFunc(GL_EQUAL,1,0xFF);//Passtestifstencilvalueis1 glStencilMask(0x00);//Don'twriteanythingtostencilbuffer glDepthMask(GL_TRUE);//Writetodepthbuffer model=glm::scale( glm::translate(model,glm::vec3(0,0,-1)), glm::vec3(1,1,-1) ); glUniformMatrix4fv(uniModel,1,GL_FALSE,glm::value_ptr(model)); glDrawArrays(GL_TRIANGLES,0,36); glDisable(GL_STENCIL_TEST); I'veannotatedthecodeabovewithcomments,butthestepsshouldbemostlyclearfromthestencilbuffersection. Nowjustonefinaltouchisrequired,todarkenthereflectedcubealittletomakethefloorlookalittlelesslikeaperfectmirror.I'vechosentocreateauniformforthiscalledoverrideColorinthevertexshader: uniformvec3overrideColor; ... Color=overrideColor*color; Andinthedrawingcodeforthereflectedcube glUniform3f(uniColor,0.3f,0.3f,0.3f); glDrawArrays(GL_TRIANGLES,0,36); glUniform3f(uniColor,1.0f,1.0f,1.0f); whereuniColoristhereturnvalueofaglGetUniformLocationcall. Awesome!Ihopethat,especiallyinchapterslikethese,yougettheideathatworkingwithanAPIaslow-levelasOpenGLcanbealotoffunandposeinterestingchallenges!Asusual,thefinalcodeisavailablehere. Exercises Therearenorealexercisesforthischapter,buttherearealotmoreinterestingeffectsyoucancreatewiththestencilbuffer.I'llleaveresearchingtheimplementationofothereffects,suchasstencilshadowsandobjectoutliningasanexercisetoyou.
延伸文章資訊
- 1Stencil Test - OpenGL Wiki
The stencil buffer is an image that uses a stencil image format. The Default Framebuffer may have...
- 2Stencil Test的应用总结
在OpenGL中,写Stencil Buffer的开启与否是通过函数glStencilMask(GLuint mask)设置的,这个函数的参数mask对应Stencil值的各个bit是否允许写入...
- 3OpenGL Programming/Stencil buffer - Wikibooks, open books ...
When you draw something with OpenGL, you see colors on the screen, but keep in mind that there ar...
- 4OpenGL基礎之Stencil Testing - 台部落
stencil testing發生在fragment shader之後,depth testing之前,它利用stencil-buffer來捨棄一些片元,餘下的會進入depth testing進...
- 5Stencil buffer - Wikipedia
OpenGL