Tutorial 43 - Multipass Shadow Mapping With Point Lights

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

In tutorial 24 we learned the basics of Shadow Mapping - first a rendering pass from the light point of view using the light direction as the viewing vector ... Background Intutorial24welearnedthebasicsofShadowMapping-firstarenderingpassfromthelight pointofviewusingthelightdirectionastheviewingvectorandthenasecondpassfrom thecamerapointofviewusingthedatafromthefirstpassforshadowcalculation.Atthis pointmostprogrammerswillaskthemselves:thisisfinefordirectional/spotlightbutwhat ifIwanttogenerateshadowsfromapointlight?Thereisnospecificlightdirectioninthis case.Solvingthisproblemisthetopicofthistutorial. Thesolutiontothisproblemistorecognizethatapointlightbasicallycastsitslightin alldirections,soratherthanplacearectangularshadowmaptexturesomewherethatwillonly receiveasmallportionofthatlight,wecanplacethelightsourceinamiddleofatexture cube.Wenowhavesixrectangularshadowmapsandthelighthasnowheretoescape.Everylight "beam"hastolandononeofthesesixshadowmapsandwecansamplefromittodoourstandard shadowcalculations.Wehavealreadyseenthecubemapinactionintheskyboxtutorialsowe arealreadyfamiliarwithit. Inpractice,inordertosimulatethenotionofspreadinglightalloverwewilldosixshadow maprenderingpassesfromthelocationofthelightsourcebuteachrenderingpasswilltarget adifferentdirection.Wearegoingtomakethisverysimpleandtargetthefollowingaxis aligneddirections:positive/negativeX,positive/negativeYandpositive/negativeZ.Eventually thecubemapfaceswillcontainthedistanceofallpixelsinthescenethatareclosesttothe lightsource.Bycomparingthisvaluetothedistanceofeachpixeltothelightduringthe lightingpasswecantellwhetherthatpixelisinlightorshadow. Takealookatthefollowingpicture: Ourscenecontainsabluesphereandapointlight(theyellowlightbulb)isstationed nearby.Inthefirstrenderingpassweuseatexturecubeastheframebuffer.Rememberthat atthisstagewedon'tcareabouttheoriginalcameralocationordirection.Weplacethe cameraatthepositionofthepointlightsoitalwayslookslikeitislocatedatthemiddle ofthetexturecube.Intheexampleaboveweseethatthecurrentrenderingdirectionisthe positiveZaxis(intotheyellowface).Atthispointwearebacktothestandardshadow mappingprocesssousingthedepthvaluesintheyellowfacewecangeneratetheproper shadowforthebluesphere(thesedepthvaluesarelocatedintheblackcirclebuttheactual shadowwillberenderedinthesecondpass). Thefollowingpicturedemonstratesthesixcameradirectionsthatwewillusein thefirstrenderingpass: Sincethesamesceneisrenderedsixtimesinthefirstrenderingpasswecallthis MultipassShadowMapping. Sourcewalkthru (shadow_map_fbo.h) classShadowMapFBO { public:   ShadowMapFBO();   ~ShadowMapFBO();   boolInit(unsignedintWindowWidth,unsignedintWindowHeight);   voidBindForWriting(GLenumCubeFace);   voidBindForReading(GLenumTextureUnit); private:   GLuintm_fbo;   GLuintm_shadowMap;   GLuintm_depth; }; Let'sstartthecodewalkthrubyreviewingthechangesinourshadowmapFBO. TheFBOismostlythesamewithtwominorchanges:theBindForWriting()method nowtakesacubefaceenumerator.Sincewearedoingamultipassrendering intothecubemapthisishowwewilltelltheGLwhichcubefacewearegoing torender.Thesecondchangeistheadditionofaseparatedepthbuffer.Previously weusedthem_shadowMapclassmemberastheshadowmapobject(whichisactually adepthbuffer).Nowm_shadowMapisgoingtobeusedasacubemapandwe needadedicateddepthbuffer.Foreachofthesixpassesintothecubemapfaces wewillusethisdepthbuffer(andnaturallywewillclearitbeforeeachpass). (shadow_map_fbo.cpp:46) boolShadowMapFBO::Init(unsignedintWindowWidth,unsignedintWindowHeight) {   //CreatetheFBO   glGenFramebuffers(1,&m_fbo);   //Createthedepthbuffer   glGenTextures(1,&m_depth);   glBindTexture(GL_TEXTURE_2D,m_depth);   glTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT32,WindowWidth,WindowHeight,0,GL_DEPTH_COMPONENT,GL_FLOAT,NULL);   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);   glBindTexture(GL_TEXTURE_2D,0);   //Createthecubemap   glGenTextures(1,&m_shadowMap);   glBindTexture(GL_TEXTURE_CUBE_MAP,m_shadowMap);   glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_MIN_FILTER,GL_LINEAR);   glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_MAG_FILTER,GL_LINEAR);   glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);   glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);   glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_WRAP_R,GL_CLAMP_TO_EDGE);   for(uinti=0;i<6;i++){     glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i,0,GL_R32F,WindowWidth,WindowHeight,0,GL_RED,GL_FLOAT,NULL);   }   glBindFramebuffer(GL_FRAMEBUFFER,m_fbo);   glFramebufferTexture2D(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_TEXTURE_2D,m_depth,0);   //Disablewritestothecolorbuffer   glDrawBuffer(GL_NONE);   //Disablereadsfromthecolorbuffer   glReadBuffer(GL_NONE);   GLenumStatus=glCheckFramebufferStatus(GL_FRAMEBUFFER);   if(Status!=GL_FRAMEBUFFER_COMPLETE){     printf("FBerror,status:0x%x\n",Status);     returnfalse;   }   glBindFramebuffer(GL_FRAMEBUFFER,0);   returnGLCheckError(); } Thisishowweinitializetheshadowmap.Firstwecreateandsetupthedepth buffer.Nothingnewhere.Nextcomesthecubemaptexture.GL_TEXTURE_CUBE_MAP isusedasthetarget.Theinterestingparthereisthewayweinitialize thesixcubefaces.OpenGLprovidesamacroforeachface:GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,etc. Theyhappentobedefinessequentiallywhichmakestheloopabovepossible(seeglew.h fortheremainingmacros;aroundline1319intheversionIhave).Eachface isinitializedwithasingle32bitfloatingpointvalueineachtexel. (tutorial43.cpp:183) virtualvoidRenderSceneCB() {  CalcFPS();  m_scale+=0.05f;  m_pGameCamera->OnRender();  ShadowMapPass();  RenderPass();  RenderFPS();  glutSwapBuffers(); } Thisisthemainrenderscenefunctionandasyoucansee,thereisnochange incomparisontopreviousshadowmappingtutorials.Atthehighlevelwehave thesametwopassesofshadowmapgenerationandrendering. (tutorial43.cpp:200) voidShadowMapPass() {   glCullFace(GL_FRONT);   m_shadowMapEffect.Enable();   PersProjInfoProjInfo;   ProjInfo.FOV=90.0f;   ProjInfo.Height=WINDOW_HEIGHT;   ProjInfo.Width=WINDOW_WIDTH;   ProjInfo.zNear=1.0f;   ProjInfo.zFar=100.0f;   Pipelinep;   p.SetPerspectiveProj(m_persProjInfo);   glClearColor(FLT_MAX,FLT_MAX,FLT_MAX,FLT_MAX);   for(uinti=0;iGetPos());   Pipelinep;   p.SetPerspectiveProj(m_persProjInfo);   p.SetCamera(*m_pGameCamera);   //Renderthequads   m_pGroundTex->Bind(COLOR_TEXTURE_UNIT);   p.Orient(m_quad1Orientation);   m_lightingEffect.SetWorldMatrix(p.GetWorldTrans());   m_lightingEffect.SetWVP(p.GetWVPTrans());   m_quad.Render();   p.Orient(m_quad2Orientation);   m_lightingEffect.SetWorldMatrix(p.GetWorldTrans());   m_lightingEffect.SetWVP(p.GetWVPTrans());   m_quad.Render();   //Renderthemeshes   p.Orient(m_mesh1Orientation);   m_lightingEffect.SetWorldMatrix(p.GetWorldTrans());   m_lightingEffect.SetWVP(p.GetWVPTrans());   m_mesh.Render();   p.Orient(m_mesh2Orientation);   m_lightingEffect.SetWorldMatrix(p.GetWorldTrans());   m_lightingEffect.SetWVP(p.GetWVPTrans());   m_mesh.Render(); } Thisisthefulllightingpass.Everythingisbacktonormal-werender intothedefaultframebuffer,webindthecubemapforreadingandresetthe camerabasedontheviewerposition.ThiscompletesourC++codereview.Now let'stakealookattheshaders. (shadow_map.vs) #version330 layout(location=0)invec3Position; layout(location=1)invec2TexCoord; layout(location=2)invec3Normal; uniformmat4gWVP; uniformmat4gWorld; outvec3WorldPos; voidmain() {   vec4Pos4=vec4(Position,1.0);   gl_Position=gWVP*Pos4;   WorldPos=(gWorld*Pos4).xyz; } Wearegoingtorenderfromthepositionofthepointlightandthecamera iscurrentlyalignedononeoftheaxis.Thevaluethatwillbewritteninto thecubemapisthedistancefromtheobjecttothepointlight.Soweneedtheobject worldpositionintheFSwherethisdistancewillbecalculated. (shadow_map.fs) #version330 invec3WorldPos; uniformvec3gLightWorldPos; outfloatFragColor; voidmain() {   vec3LightToVertex=WorldPos-gLightWorldPos;   floatLightToPixelDistance=length(LightToVertex);   FragColor=LightToPixelDistance; } WenowhavetheworldspacepositionofthepixelintheFSandtheworldspace positionofthepointlightisprovidedasauniform.Wecalculatethevector fromthelighttothepixel,takeitslengthandwriteitout. (lighting.vs) #version330 layout(location=0)invec3Position; layout(location=1)invec2TexCoord; layout(location=2)invec3Normal; outvec2TexCoord0; outvec3Normal0; outvec3WorldPos0; uniformmat4gWVP; uniformmat4gWorld; voidmain() {   gl_Position=gWVP*vec4(Position,1.0);   TexCoord0=TexCoord;   Normal0=(gWorld*vec4(Normal,0.0)).xyz;   WorldPos0=(gWorld*vec4(Position,1.0)).xyz; } ThisistheupdatedlightingVSandwhat'sinterestinghereisthemissingpiece- wenolongerneedtocalculatethelightspacepositionofthevertexaswe didintheoriginalshadowmappingalgorithm.Thiswasrequiredwhenweneeded toplacetheshadowmapalongthelightvectorbutnowweonlyneedthevectorfrom thelighttothepixelinworldspaceinordertosamplefromthecubmap.Everything weneedforthisisheresowearegoodtogo. (lighting.fs) ... uniformsamplerCubegShadowMap; ... floatCalcShadowFactor(vec3LightDirection) {   floatSampledDistance=texture(gShadowMap,LightDirection).r;   floatDistance=length(LightDirection);   if(Distance



請為這篇文章評分?