Depth testing - LearnOpenGL
文章推薦指數: 80 %
When depth testing is enabled, OpenGL tests the depth value of a fragment against the content of the depth buffer. OpenGL performs a depth test and if this ... Ifyou'rerunningAdBlock,pleaseconsiderwhitelistingthissiteifyou'dliketosupportLearnOpenGL;andnoworries,Iwon'tbemadifyoudon't:) IntroductionGettingstartedOpenGLCreatingawindowHelloWindowHelloTriangleShadersTexturesTransformationsCoordinateSystemsCameraReviewLightingColorsBasicLightingMaterialsLightingmapsLightcastersMultiplelightsReviewModelLoadingAssimpMeshModelAdvancedOpenGLDepthtestingStenciltestingBlendingFacecullingFramebuffersCubemapsAdvancedDataAdvancedGLSLGeometryShaderInstancingAntiAliasingAdvancedLightingAdvancedLightingGammaCorrectionShadowsShadowMappingPointShadowsNormalMappingParallaxMappingHDRBloomDeferredShadingSSAOPBRTheoryLightingIBLDiffuseirradianceSpecularIBLInPracticeDebuggingTextRendering2DGameBreakoutSettingupRenderingSpritesLevelsCollisionsBallCollisiondetectionCollisionresolutionParticlesPostprocessingPowerupsAudioRendertextFinalthoughtsGuestArticlesHowtopublish2020OITIntroductionWeightedBlendedSkeletalAnimation2021CSMSceneSceneGraphFrustumCullingTessellationHeightmapTessellationDSACoderepositoryTranslationsAbout BTC 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa ETH/ERC20 0x1de59bd9e52521a46309474f8372531533bd7c43 Depthtesting Advanced-OpenGL/Depth-testing Inthecoordinatesystemschapterwe'verendereda3Dcontainerandmadeuseofadepthbuffertopreventtrianglesrenderinginthefrontwhilethey'resupposedtobebehindothertriangles.Inthischapterwe'regoingtoelaborateabitmoreonthosedepthvaluesthedepthbuffer(orz-buffer)storesandhowitactuallydeterminesifafragmentisinfront. Thedepth-bufferisabufferthat,justlikethecolorbuffer(thatstoresallthefragmentcolors:thevisualoutput),storesinformationperfragmentandhasthesamewidthandheightasthecolorbuffer.Thedepthbufferisautomaticallycreatedbythewindowingsystemandstoresitsdepthvaluesas16,24or32bitfloats.Inmostsystemsyou'llseeadepthbufferwithaprecisionof24bits. Whendepthtestingisenabled,OpenGLteststhedepthvalueofafragmentagainstthecontentofthedepthbuffer.OpenGLperformsadepthtestandifthistestpasses,thefragmentisrenderedandthedepthbufferisupdatedwiththenewdepthvalue.Ifthedepthtestfails,thefragmentisdiscarded. Depthtestingisdoneinscreenspaceafterthefragmentshaderhasrun(andafterthestenciltestwhichwe'llgettointhenextchapter).ThescreenspacecoordinatesrelatedirectlytotheviewportdefinedbyOpenGL'sglViewportfunctionandcanbeaccessedviaGLSL'sbuilt-ingl_FragCoordvariableinthefragmentshader.Thexandycomponentsofgl_FragCoordrepresentthefragment'sscreen-spacecoordinates(with(0,0)beingthebottom-leftcorner).Thegl_FragCoordvariablealsocontainsaz-componentwhichcontainsthedepthvalueofthefragment.Thiszvalueisthevaluethatiscomparedtothedepthbuffer'scontent. TodaymostGPUssupportahardwarefeaturecalledearlydepthtesting.Earlydepthtestingallowsthedepthtesttorunbeforethefragmentshaderruns.Wheneveritisclearafragmentisn'tgoingtobevisible(itisbehindotherobjects)wecanprematurelydiscardthefragment. Fragmentshadersareusuallyquiteexpensivesowhereverwecanavoidrunningthemweshould.Arestrictiononthefragmentshaderforearlydepthtestingisthatyoushouldn'twritetothefragment'sdepthvalue.Ifafragmentshaderwouldwritetoitsdepthvalue,earlydepthtestingisimpossible;OpenGLwon'tbeabletofigureoutthedepthvaluebeforehand. DepthtestingisdisabledbydefaultsotoenabledepthtestingweneedtoenableitwiththeGL_DEPTH_TESToption: glEnable(GL_DEPTH_TEST); Onceenabled,OpenGLautomaticallystoresfragmentstheirz-valuesinthedepthbufferiftheypassedthedepthtestanddiscardsfragmentsiftheyfailedthedepthtestaccordingly.IfyouhavedepthtestingenabledyoushouldalsoclearthedepthbufferbeforeeachframeusingGL_DEPTH_BUFFER_BIT;otherwiseyou'restuckwiththedepthvaluesfromlastframe: glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); Therearecertainscenariosimaginablewhereyouwanttoperformthedepthtestonallfragmentsanddiscardthemaccordingly,butnotupdatethedepthbuffer.Basically,you're(temporarily)usingaread-onlydepthbuffer.OpenGLallowsustodisablewritingtothedepthbufferbysettingitsdepthmasktoGL_FALSE: glDepthMask(GL_FALSE); Notethatthisonlyhaseffectifdepthtestingisenabled. Depthtestfunction OpenGLallowsustomodifythecomparisonoperatorsitusesforthedepthtest.ThisallowsustocontrolwhenOpenGLshouldpassordiscardfragmentsandwhentoupdatethedepthbuffer.Wecansetthecomparisonoperator(ordepthfunction)bycallingglDepthFunc: glDepthFunc(GL_LESS); Thefunctionacceptsseveralcomparisonoperatorsthatarelistedinthetablebelow: Function Description GL_ALWAYS Thedepthtestalwayspasses. GL_NEVER Thedepthtestneverpasses. GL_LESS Passesifthefragment'sdepthvalueislessthanthestoreddepthvalue. GL_EQUAL Passesifthefragment'sdepthvalueisequaltothestoreddepthvalue. GL_LEQUAL Passesifthefragment'sdepthvalueislessthanorequaltothestoreddepthvalue. GL_GREATER Passesifthefragment'sdepthvalueisgreaterthanthestoreddepthvalue. GL_NOTEQUAL Passesifthefragment'sdepthvalueisnotequaltothestoreddepthvalue. GL_GEQUAL Passesifthefragment'sdepthvalueisgreaterthanorequaltothestoreddepthvalue. BydefaultthedepthfunctionGL_LESSisusedthatdiscardsallthefragmentsthathaveadepthvaluehigherthanorequaltothecurrentdepthbuffer'svalue. Let'sshowtheeffectthatchangingthedepthfunctionhasonthevisualoutput.We'lluseafreshcodesetupthatdisplaysabasicscenewithtwotexturedcubessittingonatexturedfloorwithnolighting.Youcanfindthesourcecodehere. WithinthesourcecodewechangedthedepthfunctiontoGL_ALWAYS: glEnable(GL_DEPTH_TEST); glDepthFunc(GL_ALWAYS); Thissimulatesthesamebehaviorwe'dgetifwedidn'tenabledepthtesting.Thedepthtestalwayspassessothefragmentsthataredrawnlastarerenderedinfrontofthefragmentsthatweredrawnbefore,eventhoughtheyshould'vebeenatthefront.Sincewe'vedrawnthefloorplanelast,theplane'sfragmentsoverwriteeachofthecontainer'spreviouslywrittenfragments: SettingitallbacktoGL_LESSgivesusthetypeofscenewe'reusedto: Depthvalueprecision Thedepthbuffercontainsdepthvaluesbetween0.0and1.0anditcomparesitscontentwiththez-valuesofalltheobjectsinthesceneasseenfromtheviewer.Thesez-valuesinviewspacecanbeanyvaluebetweentheprojection-frustum'snearandfarplane.Wethusneedsomewaytotransformtheseview-spacez-valuestotherangeof[0,1]andonewayistolinearlytransformthem.Thefollowing(linear)equationtransformsthez-valuetoadepthvaluebetween0.0and1.0: \begin{equation}F_{depth}=\frac{z-near}{far-near}\end{equation} Here\(near\)and\(far\)arethenearandfarvaluesweusedtoprovidetotheprojectionmatrixtosetthevisiblefrustum(seecoordinateSystems).Theequationtakesadepthvalue\(z\)withinthefrustumandtransformsittotherange[0,1].Therelationbetweenthez-valueanditscorrespondingdepthvalueispresentedinthefollowinggraph: Notethatallequationsgiveadepthvaluecloseto0.0whentheobjectisclosebyandadepthvaluecloseto1.0whentheobjectisclosetothefarplane. Inpracticehowever,alineardepthbufferlikethisisalmostneverused.Becauseofprojectionpropertiesanon-lineardepthequationisusedthatisproportionalto1/z.Theresultisthatwegetenormousprecisionwhenzissmallandmuchlessprecisionwhenzisfaraway. Sincethenon-linearfunctionisproportionalto1/z,z-valuesbetween1.0and2.0wouldresultindepthvaluesbetween1.0and0.5whichishalfofthe[0,1]range,givingusenormousprecisionatsmallz-values.Z-valuesbetween50.0and100.0wouldaccountforonly2%ofthe[0,1]range.Suchanequation,thatalsotakesnearandfardistancesintoaccount,isgivenbelow: \begin{equation}F_{depth}=\frac{1/z-1/near}{1/far-1/near}\end{equation} Don'tworryifyoudon'tknowexactlywhatisgoingonwiththisequation.Theimportantthingtorememberisthatthevaluesinthedepthbufferarenotlinearinclip-space(theyarelinearinview-spacebeforetheprojectionmatrixisapplied).Avalueof0.5inthedepthbufferdoesnotmeanthepixel'sz-valueishalfwayinthefrustum;thez-valueofthevertexisactuallyquiteclosetothenearplane!Youcanseethenon-linearrelationbetweenthez-valueandtheresultingdepthbuffer'svalueinthefollowinggraph: Asyoucansee,thedepthvaluesaregreatlydeterminedbythesmallz-valuesgivinguslargedepthprecisiontotheobjectscloseby.Theequationtotransformz-values(fromtheviewer'sperspective)isembeddedwithintheprojectionmatrixsowhenwetransformvertexcoordinatesfromviewtoclip,andthentoscreen-spacethenon-linearequationisapplied. Theeffectofthisnon-linearequationquicklybecomesapparentwhenwetrytovisualizethedepthbuffer. Visualizingthedepthbuffer Weknowthatthez-valueofthebuilt-ingl_FragCoordvectorinthefragmentshadercontainsthedepthvalueofthatparticularfragment.Ifweweretooutputthisdepthvalueofthefragmentasacolorwecoulddisplaythedepthvaluesofallthefragmentsinthescene: voidmain() { FragColor=vec4(vec3(gl_FragCoord.z),1.0); } Ifyou'dthenruntheprogramyou'llprobablynoticethateverythingiswhite,makingitlooklikeallofourdepthvaluesarethemaximumdepthvalueof1.0.Sowhyaren'tanyofthedepthvaluescloserto0.0andthusdarker? Intheprevioussectionwedescribedthatdepthvaluesinscreenspacearenon-lineare.g.theyhaveaveryhighprecisionforsmallz-valuesandalowprecisionforlargez-values.Thedepthvalueofthefragmentincreasesrapidlyoverdistancesoalmostalltheverticeshavevaluescloseto1.0.Ifweweretocarefullymovereallyclosetoanobjectyoumayeventuallyseethecolorsgettingdarker,theirz-valuesbecomingsmaller: Thisclearlyshowsthenon-linearityofthedepthvalue.Objectsclosebyhaveamuchlargereffectonthedepthvaluethanobjectsfaraway.Onlymovingafewinchescanresultinthecolorsgoingfromdarktocompletelywhite. Wecanhowever,transformthenon-lineardepthvaluesofthefragmentbacktoitslinearsibling.Toachievethiswebasicallyneedtoreversetheprocessofprojectionforthedepthvaluesalone.Thismeanswehavetofirstre-transformthedepthvaluesfromtherange[0,1]tonormalizeddevicecoordinatesintherange[-1,1].Thenwewanttoreversethenon-linearequation(equation2)asdoneintheprojectionmatrixandapplythisinversedequationtotheresultingdepthvalue.Theresultisthenalineardepthvalue. FirstwetransformthedepthvaluetoNDCwhichisnottoodifficult: floatndc=depth*2.0-1.0; Wethentaketheresultingndcvalueandapplytheinversetransformationtoretrieveitslineardepthvalue: floatlinearDepth=(2.0*near*far)/(far+near-ndc*(far-near)); Thisequationisderivedfromtheprojectionmatrixfornon-linearizingthedepthvalues,returningdepthvaluesbetweennearandfar.Thismath-heavyarticleexplainstheprojectionmatrixinenormousdetailfortheinterestedreader;italsoshowswheretheequationscomefrom. Thecompletefragmentshaderthattransformsthenon-lineardepthinscreen-spacetoalineardepthvalueisthenasfollows: #version330core outvec4FragColor; floatnear=0.1; floatfar=100.0; floatLinearizeDepth(floatdepth) { floatz=depth*2.0-1.0;//backtoNDC return(2.0*near*far)/(far+near-z*(far-near)); } voidmain() { floatdepth=LinearizeDepth(gl_FragCoord.z)/far;//dividebyfarfordemonstration FragColor=vec4(vec3(depth),1.0); } Becausethelinearizeddepthvaluesrangefromneartofarmostofitsvalueswillbeabove1.0anddisplayedascompletelywhite.Bydividingthelineardepthvaluebyfarinthemainfunctionweconvertthelineardepthvaluetotherange[0,1].Thiswaywecangraduallyseethescenebecomebrighterthecloserthefragmentsaretotheprojectionfrustum'sfarplane,whichworksbetterforvisualizationpurposes. Ifwe'dnowruntheapplicationwegetdepthvaluesthatarelinearoverdistance.Trymovingaroundthescenetoseethedepthvalueschangeinalinearfashion. Thecolorsaremostlyblackbecausethedepthvaluesrangelinearlyfromthenearplane(0.1)tothefarplane(100)whichisstillquitefarawayfromus.Theresultisthatwe'rerelativelyclosetothenearplaneandthereforegetlower(darker)depthvalues. Z-fighting Acommonvisualartifactmayoccurwhentwoplanesortrianglesaresocloselyalignedtoeachotherthatthedepthbufferdoesnothaveenoughprecisiontofigureoutwhichoneofthetwoshapesisinfrontoftheother.Theresultisthatthetwoshapescontinuallyseemtoswitchorderwhichcausesweirdglitchypatterns.Thisiscalledz-fighting,becauseitlooksliketheshapesarefightingoverwhogetsontop. Inthescenewe'vebeenusingsofarthereareafewspotswherez-fightingcanbenoticed.Thecontainerswereplacedattheexactheightofthefloorwhichmeansthebottomplaneofthecontaineriscoplanarwiththefloorplane.Thedepthvaluesofbothplanesarethenthesamesotheresultingdepthtesthasnowayoffiguringoutwhichistherightone. Ifyoumovethecamerainsideoneofthecontainerstheeffectsareclearlyvisible,thebottompartofthecontainerisconstantlyswitchingbetweenthecontainer'splaneandthefloor'splaneinazigzagpattern: Z-fightingisacommonproblemwithdepthbuffersandit'sgenerallymorenoticeablewhenobjectsarefurtheraway(becausethedepthbufferhaslessprecisionatlargerz-values).Z-fightingcan'tbecompletelyprevented,butthereareafewtricksthatwillhelptomitigateorcompletelypreventz-fightinginyourscene. Preventz-fighting Thefirstandmostimportanttrickisneverplaceobjectstooclosetoeachotherinawaythatsomeoftheirtrianglescloselyoverlap.Bycreatingasmalloffsetbetweentwoobjectsyoucancompletelyremovez-fightingbetweenthetwoobjects.Inthecaseofthecontainersandtheplanewecould'veeasilymovedthecontainersslightlyupwardsinthepositiveydirection.Thesmallchangeofthecontainer'spositionswouldprobablynotbenoticeableatallandwouldcompletelyreducethez-fighting.However,thisrequiresmanualinterventionofeachoftheobjectsandthoroughtestingtomakesurenoobjectsinasceneproducez-fighting. Asecondtrickistosetthenearplaneasfaraspossible.Inoneoftheprevioussectionswe'vediscussedthatprecisionisextremelylargewhenclosetothenearplanesoifwemovethenearplaneawayfromtheviewer,we'llhavesignificantlygreaterprecisionovertheentirefrustumrange.However,settingthenearplanetoofarcouldcauseclippingofnearobjectssoitisusuallyamatteroftweakingandexperimentationtofigureoutthebestneardistanceforyourscene. Anothergreattrickatthecostofsomeperformanceistouseahigherprecisiondepthbuffer.Mostdepthbuffershaveaprecisionof24bits,butmostGPUsnowadayssupport32bitdepthbuffers,increasingtheprecisionbyasignificantamount.Soatthecostofsomeperformanceyou'llgetmuchmoreprecisionwithdepthtesting,reducingz-fighting. The3techniqueswe'vediscussedarethemostcommonandeasy-to-implementantiz-fightingtechniques.Therearesomeothertechniquesouttherethatrequirealotmoreworkandstillwon'tcompletelydisablez-fighting.Z-fightingisacommonissue,butifyouusethepropercombinationofthelistedtechniquesyouprobablywon'tneedtodealwithz-fightingthatmuch. HI
延伸文章資訊
- 1Force depth test against a specific depth value, instead of ...
Not the answer you're looking for? Browse other questions tagged opengl glsl opengl-3 depth-testi...
- 2OpenGL學習腳印:深度測試(depth testing) - IT閱讀
這一過程稱之為深度測試(Depth Testing)。在OpenGL中執行深度測試時,我們可以根據需要指定深度值的比較函數,後面會詳細介紹具體使用。
- 3Overlap and Depth Buffering
Like the color buffer, the depth buffer for the main window is created automatically by OpenGL wh...
- 4Depth Test - OpenGL Wiki
- 5Depth testing - LearnOpenGL