Tutorial 16 : Shadow mapping

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

As such, rendering the shadow map is done with an orthographic projection matrix. An orthographic matrix is just ... Here are two way to do this in GLSL. Basicshadowmap Renderingtheshadowmap SettinguptherendertargetandtheMVPmatrix Theshaders Result Usingtheshadowmap Basicshader Result-Shadowacne Problems Shadowacne PeterPanning Aliasing PCF PoissonSampling StratifiedPoissonSampling Goingfurther Earlybailing Spotlights Pointlights Combinationofseverallights Automaticlightfrustum Exponentialshadowmaps Light-spaceperspectiveShadowMaps Cascadedshadowmaps Conclusion InTutorial15welearnthowtocreatelightmaps,whichencompassesstaticlighting.Whileitproducesveryniceshadows,itdoesn’tdealwithanimatedmodels. Shadowmapsarethecurrent(asof2016)waytomakedynamicshadows.Thegreatthingaboutthemisthatit’sfairlyeasytogettowork.Thebadthingisthatit’sterriblydifficulttogettoworkright. Inthistutorial,we’llfirstintroducethebasicalgorithm,seeitsshortcomings,andthenimplementsometechniquestogetbetterresults.Sinceattimeofwriting(2012)shadowmapsarestillaheavilyresearchedtopic,we’llgiveyousomedirectionstofurtherimproveyourownshadowmap,dependingonyourneeds. Basicshadowmap Thebasicshadowmapalgorithmconsistsintwopasses.First,thesceneisrenderedfromthepointofviewofthelight.Onlythedepthofeachfragmentiscomputed.Next,thesceneisrenderedasusual,butwithanextratesttoseeitthecurrentfragmentisintheshadow. The“beingintheshadow”testisactuallyquitesimple.Ifthecurrentsampleisfurtherfromthelightthantheshadowmapatthesamepoint,thismeansthatthescenecontainsanobjectthatisclosertothelight.Inotherwords,thecurrentfragmentisintheshadow. Thefollowingimagemighthelpyouunderstandtheprinciple: Renderingtheshadowmap Inthistutorial,we’llonlyconsiderdirectionallights-lightsthataresofarawaythatallthelightrayscanbeconsideredparallel.Assuch,renderingtheshadowmapisdonewithanorthographicprojectionmatrix.Anorthographicmatrixisjustlikeausualperspectiveprojectionmatrix,exceptthatnoperspectiveistakenintoaccount-anobjectwilllookthesamewhetherit’sfarornearthecamera. SettinguptherendertargetandtheMVPmatrix SinceTutorial14,youknowhowtorenderthesceneintoatextureinordertoaccessitlaterfromashader. Hereweusea1024x102416-bitdepthtexturetocontaintheshadowmap.16bitsareusuallyenoughforashadowmap.Feelfreetoexperimentwiththesevalues.Notethatweuseadepthtexture,notadepthrenderbuffer,sincewe’llneedtosampleitlater. //Theframebuffer,whichregroups0,1,ormoretextures,and0or1depthbuffer. GLuintFramebufferName=0; glGenFramebuffers(1,&FramebufferName); glBindFramebuffer(GL_FRAMEBUFFER,FramebufferName); //Depthtexture.Slowerthanadepthbuffer,butyoucansampleitlaterinyourshader GLuintdepthTexture; glGenTextures(1,&depthTexture); glBindTexture(GL_TEXTURE_2D,depthTexture); glTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT16,1024,1024,0,GL_DEPTH_COMPONENT,GL_FLOAT,0); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); glFramebufferTexture(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,depthTexture,0); glDrawBuffer(GL_NONE);//Nocolorbufferisdrawnto. //Alwayscheckthatourframebufferisok if(glCheckFramebufferStatus(GL_FRAMEBUFFER)!=GL_FRAMEBUFFER_COMPLETE) returnfalse; TheMVPmatrixusedtorenderthescenefromthelight’spointofviewiscomputedasfollows: TheProjectionmatrixisanorthographicmatrixwhichwillencompasseverythingintheaxis-alignedbox(-10,10),(-10,10),(-10,20)ontheX,YandZaxesrespectively.Thesevaluesaremadesothatourentire*visible*sceneisalwaysvisible;moreonthisintheGoingFurthersection. TheViewmatrixrotatestheworldsothatincameraspace,thelightdirectionis-Z(wouldyouliketore-readTutorial3?) TheModelmatrixiswhateveryouwant. glm::vec3lightInvDir=glm::vec3(0.5f,2,2); //ComputetheMVPmatrixfromthelight'spointofview glm::mat4depthProjectionMatrix=glm::ortho(-10,10,-10,10,-10,20); glm::mat4depthViewMatrix=glm::lookAt(lightInvDir,glm::vec3(0,0,0),glm::vec3(0,1,0)); glm::mat4depthModelMatrix=glm::mat4(1.0); glm::mat4depthMVP=depthProjectionMatrix*depthViewMatrix*depthModelMatrix; //Sendourtransformationtothecurrentlyboundshader, //inthe"MVP"uniform glUniformMatrix4fv(depthMatrixID,1,GL_FALSE,&depthMVP[0][0]) Theshaders Theshadersusedduringthispassareverysimple.Thevertexshaderisapass-throughshaderwhichsimplycomputethevertex’positioninhomogeneouscoordinates: #version330core //Inputvertexdata,differentforallexecutionsofthisshader. layout(location=0)invec3vertexPosition_modelspace; //Valuesthatstayconstantforthewholemesh. uniformmat4depthMVP; voidmain(){ gl_Position=depthMVP*vec4(vertexPosition_modelspace,1); } Thefragmentshaderisjustassimple:itsimplywritesthedepthofthefragmentatlocation0(i.e.inourdepthtexture). #version330core //Ouputdata layout(location=0)outfloatfragmentdepth; voidmain(){ //Notreallyneeded,OpenGLdoesitanyway fragmentdepth=gl_FragCoord.z; } Renderingashadowmapisusuallymorethantwiceasfastasthenormalrender,becauseonlylowprecisiondepthiswritten,insteadofboththedepthandthecolor;MemorybandwidthisoftenthebiggestperformanceissueonGPUs. Result Theresultingtexturelookslikethis: Adarkcolourmeansasmallz;hence,theupper-rightcornerofthewallisnearthecamera.Attheopposite,whitemeansz=1(inhomogeneouscoordinates),sothisisveryfar. Usingtheshadowmap Basicshader Nowwegobacktoourusualshader.Foreachfragmentthatwecompute,wemusttestwhetheritis“behind”theshadowmapornot. Todothis,weneedtocomputethecurrentfragment’spositioninthesamespacethattheoneweusedwhencreatingtheshadowmap.SoweneedtotransformitoncewiththeusualMVPmatrix,andanothertimewiththedepthMVPmatrix. Thereisalittletrick,though.Multiplyingthevertex’positionbydepthMVPwillgivehomogeneouscoordinates,whicharein[-1,1];buttexturesamplingmustbedonein[0,1]. Forinstance,afragmentinthemiddleofthescreenwillbein(0,0)inhomogeneouscoordinates;butsinceitwillhavetosamplethemiddleofthetexture,theUVswillhavetobe(0.5,0.5). Thiscanbefixedbytweakingthefetchcoordinatesdirectlyinthefragmentshaderbutit’smoreefficienttomultiplythehomogeneouscoordinatesbythefollowingmatrix,whichsimplydividescoordinatesby2(thediagonal:[-1,1]->[-0.5,0.5])andtranslatesthem(thelowerrow:[-0.5,0.5]->[0,1]). glm::mat4biasMatrix( 0.5,0.0,0.0,0.0, 0.0,0.5,0.0,0.0, 0.0,0.0,0.5,0.0, 0.5,0.5,0.5,1.0 ); glm::mat4depthBiasMVP=biasMatrix*depthMVP; Wecannowwriteourvertexshader.It’sthesameasbefore,butweoutput2positionsinsteadof1: gl_Positionisthepositionofthevertexasseenfromthecurrentcamera ShadowCoordisthepositionofthevertexasseenfromthelastcamera(thelight) //Outputpositionofthevertex,inclipspace:MVP*position gl_Position=MVP*vec4(vertexPosition_modelspace,1); //Same,butwiththelight'sviewmatrix ShadowCoord=DepthBiasMVP*vec4(vertexPosition_modelspace,1); Thefragmentshaderisthenverysimple: texture(shadowMap,ShadowCoord.xy).zisthedistancebetweenthelightandthenearestoccluder ShadowCoord.zisthedistancebetweenthelightandthecurrentfragment …soifthecurrentfragmentisfurtherthanthenearestoccluder,thismeansweareintheshadow(ofsaidnearestoccluder): floatvisibility=1.0; if(texture(shadowMap,ShadowCoord.xy).zdrawonlyback-facingtriangles Andwhenrenderingthescene,rendernormally(backfaceculling) glCullFace(GL_BACK);//Cullback-facingtriangles->drawonlyfront-facingtriangles Thismethodisusedinthecode,inadditiontothebias. PeterPanning Wehavenoshadowacneanymore,butwestillhavethiswrongshadingoftheground,makingthewalltolookasifit’sflying(hencetheterm“PeterPanning”).Infact,addingthebiasmadeitworse. Thisoneisveryeasytofix:simplyavoidthingeometry.Thishastwoadvantages: First,itsolvesPeterPanning:itthegeometryismoredeepthanyourbias,you’reallset. Second,youcanturnonbackfacecullingwhenrenderingthelightmap,becausenow,thereisapolygonofthewallwhichisfacingthelight,whichwilloccludetheotherside,whichwouldn’tberenderedwithbackfaceculling. Thedrawbackisthatyouhavemoretrianglestorender(twotimesperframe!) Aliasing Evenwiththesetwotricks,you’llnoticethatthereisstillaliasingontheborderoftheshadow.Inotherwords,onepixeliswhite,andthenextisblack,withoutasmoothtransitioninbetween. PCF Theeasiestwaytoimprovethisistochangetheshadowmap’ssamplertypetosampler2DShadow.Theconsequenceisthatwhenyousampletheshadowmaponce,thehardwarewillinfactalsosampletheneighboringtexels,dothecomparisonforallofthem,andreturnafloatin[0,1]withabilinearfilteringofthecomparisonresults. Forinstance,0.5meansthat2samplesareintheshadow,and2samplesareinthelight. Notethatit’snotthesamethanasinglesamplingofafiltereddepthmap!Acomparisonalwaysreturnstrueorfalse;PCFgivesainterpolationof4“trueorfalse”. Asyoucansee,shadowbordersaresmooth,butshadowmap’stexelsarestillvisible. PoissonSampling AneasywaytodealwiththisistosampletheshadowmapNtimesinsteadofonce.UsedincombinationwithPCF,thiscangiveverygoodresults,evenwithasmallN.Here’sthecodefor4samples: for(inti=0;i<4;i++){ if(texture(shadowMap,ShadowCoord.xy+poissonDisk[i]/700.0).z(glm::radians(45.0f),1.0f,2.0f,50.0f); glm::mat4depthViewMatrix=glm::lookAt(lightPos,lightPos-lightInvDir,glm::vec3(0,1,0)); samething,butwithaperspectivefrustuminsteadofanorthographicfrustum.Usetexture2Dprojtoaccountforperspective-divide(seefootnotesintutorial4-Matrices) Thesecondstepistotakeintoaccounttheperspectiveintheshader.(seefootnotesintutorial4-Matrices.Inanutshell,aperspectiveprojectionmatrixactuallydoesn’tdoanyperspectiveatall.Thisisdonebythehardware,bydividingtheprojectedcoordinatesbyw.Here,weemulatethetransformationintheshader,sowehavetodotheperspective-divideourselves.Bytheway,anorthographicmatrixalwaysgenerateshomogeneousvectorswithw=1,whichiswhytheydon’tproduceanyperspective) HerearetwowaytodothisinGLSL.Thesecondusesthebuilt-intextureProjfunction,butbothmethodsproduceexactlythesameresult. if(texture(shadowMap,(ShadowCoord.xy/ShadowCoord.w)).z



請為這篇文章評分?