OpenGL Shadow Mapping Tutorial - Paul's Projects

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

The shadow map is a snapshot of the light's viewport, which is a 2d projection of the light's clip space. In order to perform the texture projection, we will ... Home MetaballsII SonicPlayer VMachine OpenGLTutorials ShadowMapping SimpleBumpMapping OpenGLProjects Direct3DProjects MegaReader ContactMe ShadowMapping ShadowmappingwasintroducedbyLanceWilliamsin1978,inapaperentitled"Castingcurvedshadowsoncurvedsurfaces". Ithasbeenextensivelyusedsince,bothinofflinerenderingandrealtimegraphics. ShadowmappingisusedbyPixar'sRendermanandwasusedonmajorfilmssuchas"ToyStory". Shadowmappingisjustoneofmanydifferentwaysofproducingshadowsinyourgraphicsapplications,eachwithitsownadvantagesanddisadvantages. Inthecaseofshadowmapping,theseinclude: Advantages: Noknowledgeorprocessingofthescenegeometryisrequired,sinceshadowmappingisanimagespacetechnique,workingautomaticallywithobjectscreatedoralteredontheGPU. Onlyasingletextureisrequiredtoholdshadowinginformationforeachlight;thestencilbufferisnotused. Avoidsthehighfillrequirementofshadowvolumes. Disadvantages: Aliasing,especiallywhenusingsmallshadowmaps. Thescenegeometrymustberenderedonceperlightinordertogeneratetheshadowmapforaspotlight,andmoretimesforanomnidirectionalpointlight. Thistutorialwillfocusonbasicshadowmappingforasinglespotlight,butthereareplentyofpapersabouthowtoextendandimprovethetechnique. Theory-Shadowmappingasadepthtest Considerasimplescenelitbyasinglepointlight,withhardshadows. Howdoesagivenpointinthesceneknowwhetheritislit,orinshadow? Putsimply,apointinthesceneislitifthereisnothingblockingastraightlinepathbetweenthelightandthatpoint. Thekeystepinunderstandingshadowmappingisthatthesepointsareexactlythosewhichwouldbevisible(i.e.notoccluded)toaviewerplacedatthelightsource. Wealreadyhaveatechniquetoseewhatisvisibletoagivenviewer,anduseitwhendrawingalmostanysceneusing3dhardware. Thattechniqueisz-buffering. So,thepointswhichwouldpassthedepthtestifwewererenderingthescenefromthelight'spointofviewarepreciselythosewhichshouldnotbeinshadow. Ifwedrawthescenefromthelight'spointofview,wecansavethevaluesfromthedepthbuffer. Then,wedrawthescenefromthecamera'spointofview,andusethesaveddepthbufferasatexturewhichisprojectedfromthelight'sposition. Atagivenpoint,wecanthencomparethevaluefromthetextureprojectedontothepointtothedistancefromthepointtothelight,andhencecalculatewhichpointsshouldbeinshadow. LettingthevalueinthesaveddepthtexturebeD,andthedistancefromthepointtothelightbeR,wehave: R = D Therewasnothingoccludingthispointwhendrawingfromthelightsource,sothispointisunshadowed. R > D Theremusthavebeenanobjectinfrontofthispointwhenlookingfromthelight'sposition. Thispointisthusinshadow. Application HowdowegoaboutperformingtheaboveusingOpenGL? Thetechniquerequiresatleast2passes,buttokeepeachpasssimple,wewilluse3. Firstly,wedrawthescenefromthelight'spointofview. ThisisacheivedbyusinggluLookAttolookfromthelight'spositionatthecentreofthescene. Thesceneisthendrawnasnormal,andthedepthbufferread. Allthecalculationsfortheshadowingareperformedattheprecisionofthedepthbuffer. Usinganequalitytotestforanunshadowedpointislikelytoproducemanyincorrectresults,duetoalackofprecision. Thisisthesamereasonasthatbehind"Donotcomparefloatswith==". So,whendrawingthescenefromthelight'spointofview,weinstructOpenGLtocullfrontfaces. Thusthebackfacesofourobjectsaredrawnintotheshadowmap. Hencethedepthvaluesstoredintheshadowmaparegreaterthanthedepthofthefaceswhichcanbeseenbythelight. BymarkingasunshadowedpointsforwhichD>=R,allsurfacesvisibletothelightwillbeunshadowed. Therewillnowbeaprecisionproblemwiththebackfaces(withrespecttothelight),butsincetheseareshadowedbydefinition,theresultofthecomparisondoesnotmatter. Thistechniquewillonlyworkifallobjectsareclosed. Ifyouhaveopenobjectsinyourscene,itispossibleinsteadtousepolygonoffsettoincreasethedepthvaluesstoredinthedepthbuffer. Forsimplicity,wewilldrawthisfirstpasstothestandardbackbuffer. Thismeansthatourwindowmustbelargeenoughtofittheshadowmaptexturewithinit,andthewindowmustnotbeoccludedbyothers. Theserestrictionscanbebypassedbyusinganoff-screenpbufferwhengeneratingtheshadowmap. Theothertwopassesaredrawnfromthecamera'spointofview. Firstly,wedrawtheentirescenewithadimlight,asitwouldbeshownifshadowed. Intheory,thispassshoulddrawthesceneusingonlyambientlight. However,inorderthatthecurvedsurfacesinshadowdonotappearunnaturallyflat,weuseadimdiffuselightsource. Thethirdpassiswheretheshadowcomparisonmentionedaboveoccurs. Thiscomparisonissovitaltoshadowmapping,itisactuallypossibletogetthehardwaretoperformthecomparisonperpixel,usingtheARBapprovedextension,ARB_shadow. Wesetupthetextureunitsothatthecomparisonwillaffectthealphavalueaswellasthecolorcomponents. Anyfragmentswhich"fail"thecomparison(R>D)willgenerateanalphavalueof0,andanywhichpasswillhavealphaof1. Byusingthealphatest,wecandiscardanyfragmentswhichshouldbeshadowed. Now,usingabrightlightwithspecularenabledwecandrawthelitpartsofthescene. Usingalinearfilteronthedepthtexturewillfilterthevaluesproducedaftertheshadowcomparison. Thisiscalled"PercentageCloserFiltering"(orPCF)andwillproduceslightlysoftshadowedges. Ifweallowtheloweralphavaluestopassthealphatesthowever,thelitfragments,modulatedbytheshadowmap,mayactuallybedarkerthantheshadowedpixelalreadywithintheframebuffer. Thisproducesadarkborderaroundtheshadowedregions. So,inthisdemo,thealphatestisusedtodiscardallbutfullylitregions. Thedarkbordercouldbeeliminatedbyusingadifferent,morecomplicatedmethodtocombinethe2passes. Inmymainshadowmappingproject,MAXblendingisusedtocombinetheresults. However,tokeepthistutorialassimpleaspossible,PCFhasnotbeenused. Projectivetexturing Howdoweprojectthelight'sdepthbuffer,encodedinatexture,ontothescene'sgeometrywhenrenderedfromthecamera'spointofview? Firstly,let'slookatthecoordinatespacesandmatricesinvolvedinthisdemo: Theshadowmapisasnapshotofthelight'sviewport,whichisa2dprojectionofthelight'sclipspace. Inordertoperformthetextureprojection,wewilluseOpenGL'sEYE_LINEARtexturecoordinategeneration,whichgeneratestexturecoordinatesforavertexbaseduponitseye-spaceposition. Weneedtomapthesegeneratedtexturecoordinatestoonesappropriateforaddressingtheshadowmap,usingthetexturematrix. Thetexturematrixthusneedstoperformtheoperationsymbolisedbythegreenarrowabove. Thebestwaytodothisistouse: where: Tisthetexturematrix Plisthelight'sprojectionmatrix Vlisthelight'sviewmatrix Vcisthecamera'sviewmatrix RememberingthatOpenGLappliesamatrixMtoatexturecoordinatesetTasMT,thiswilltransformthecamera'seyespacecoordinatesintothelight'sclipspacebygoingthroughworldspaceandthelight'seyespace. Thisavoidsobjectspaceandtheuseofanymodelmatrices,andhencedoesnotneedtoberecalculatedforeachmodelwearedrawing. Thereisonefinaloperationwhichneedstobeperformedonthetexturecoordinatesoncetheyareinthelight'sclipspace. Aftertheperspectivedivide,theclipspaceX,YandZcoordinatesareintherange-1to1(written[-1,1]). ThetexturemapisaddressedbyXandYcoordinatesin[0,1],andthedepthvaluestoredinitisalsoin[0,1]. Weneedtogenerateasimplematrixtomap[-1,1]to[0,1]foreachofX,YandZcoordinates,andpre-multiplyourtexturematrixTbyit. Wecanactuallyperformthisprojectionavoidinguseofthetexturematrixaltogether. ThiscanbeacheivedasweactuallyspecifyamatrixwhenweenableEYE_LINEARtexgen. Typicalcodetoenablethetexturecoordinategenerationforasinglecoordinateis: glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR); glTexGenfv(GL_S,GL_EYE_PLANE,VECTOR4D(1.0f,0.0f,0.0f,0.0f)); glEnable(GL_TEXTURE_GEN_S); Ifwelookattheeyeplanesforallfourtexturecoordinatestogether,theyformthe4x4identitymatrix. Texturecoordinatesaregeneratedbaseduponthis"texgen"matrix,andarethenmanipulatedusingthetexturematrix. Wecangainasmallspeed-upbyignoringthetexturematrixandplacingwhatwewoulduseforthetexturematrixdirectlyintotheeyeplanes. Finally,themostexpensivepartofsettinguptheprojectioniscalculatingtheinverseofVc. OpenGLwillevendothatforus! Whentheeyeplanesarespecified,theGLwillautomaticallypost-multiplythemwiththeinverseofthecurrentmodelviewmatrix. Allwehavetodoisensurethatatthistime,themodelviewmatrixcontainsthecamera'sviewmatrix. Theinverseofthiswillthenbemultipliedontoourtexgenmatrix. So,thefinalcodetosetupthetextureprojection,includingtheseoptimisations,is: //Calculatetexturematrixforprojection //Thismatrixtakesusfromeyespacetothelight'sclipspace //Itispostmultipliedbytheinverseofthecurrentviewmatrixwhenspecifyingtexgen staticMATRIX4X4biasMatrix (0.5f,0.0f,0.0f,0.0f, 0.0f,0.5f,0.0f,0.0f, 0.0f,0.0f,0.5f,0.0f, 0.5f,0.5f,0.5f,1.0f); MATRIX4X4textureMatrix=biasMatrix*lightProjectionMatrix*lightViewMatrix; //Setuptexturecoordinategeneration. glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR); glTexGenfv(GL_S,GL_EYE_PLANE,textureMatrix.GetRow(0)); glEnable(GL_TEXTURE_GEN_S); glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR); glTexGenfv(GL_T,GL_EYE_PLANE,textureMatrix.GetRow(1)); glEnable(GL_TEXTURE_GEN_T); glTexGeni(GL_R,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR); glTexGenfv(GL_R,GL_EYE_PLANE,textureMatrix.GetRow(2)); glEnable(GL_TEXTURE_GEN_R); glTexGeni(GL_Q,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR); glTexGenfv(GL_Q,GL_EYE_PLANE,textureMatrix.GetRow(3)); glEnable(GL_TEXTURE_GEN_Q); Extensionsused TheonlytwoextensionswewilluseinthisprojectareARB_depth_textureandARB_shadow. ARB_depth_textureprovidesanewtextureformattoholdthedepthbuffer. ByusingDEPTH_COMPONENTasboththeformatandinternalformatofatexture,wegetasingle-channeltextureofthesameprecisionasthedepthbuffer. Forexample,ifwehavea24bitdepthbuffer,aDEPTH_COMPONENTtexturewillhaveasingle24bitchannel,perfectforstoringvaluesreadfromthedepthbuffer. AswithRGBtextures,wecanuseCopyTex[Sub]Image2Dtocopydatafromtheframebufferintothetexture. Whenweareusingadepthtexture,thisinformationwillautomaticallybecopiedfromthedepthbufferratherthanthecolorbuffer. Thiscard-localcopysavesusfromhavingtoreadthedepthbufferintosystemmemoryandthensavethisasatexture. ARB_shadowprovidestheautomaticshadowcomparisonmentionedabove. Ratherthanwritepossiblylargeamountsofcodetocreateandinitialisefunctionpointersfortheextensions,wewillusetheextensionloadinglibrary"GLee",byBenWoodhouse. Thiswilldotheworkforus,andcanbedownloadedfromhttp://elf-stone.com/glee.php. Thelatestversionisalsoincludedinthesourcedownloadattheendofthistutorial. Code Theamountofcoderequiredforthistutorialisactuallyquitesmall.Thereasonsforthisareprimarily: Thereisnoneedtomanuallygenerateanyspecificgeometry,sincetheshadowmappingalgorithmdoesnotrequiresilhouetteedgedeterminationoranyadditionalvertexpropertiessuchastangentvectors. AllgeometrycanbedrawnusingglutSolidSphereandsimilarcommands. Themajorityoftheworkrequiredisdoneinhardware. Theshadowmappingcomparisonneedsonlyafewlinestoenableit,thenitwillbeperformedautomatically. InmyOpenGLShadowMappingdemoontheprojectspage,shadowmappingat8bitprecisionisdone"manually"forhardwarewhichdoesnotsupporttheARB_shadowextension. Thisrequiresquitealotmorework. The8bitprecisionalsomeansthatmanyartifactscanappearonallbutthesmallestspotlightfrusta. Nowthathardwareshadowmappingsupportisrelativelywidelyavailable,withitseaseofuseandhighprecision,thereisverylittlecallforthemanualapproach. Sincethecodeforshadowmappingisquiteconcise,IhaveincludedacoupleofsimpleclassesfrommyusualOpenGLprojects. TheTIMERclassimplementsasimpletimerusingtimeGetTime. Thetimercanbepausedandunpaused,andsimplyreturnsthenumberofmillisecondssinceitwaslastreset. Weusethistoanimatethesceneataconstantspeed,independentoftheframerate. TheFPS_COUNTERclassimplementsasimpleframespersecondcountersoyoucanseehowwellthedemocodeisrunning. Eachframe,thiscounterisinternallyincremented,andeachsecondthenumberofframesdisplayedisupdated. TheDrawScenefunction,foundinscene.cpp,drawsthescenewhichwewanttodisplay. Ittakesafloatingpointangleasaparameter,whichisusedtorotatethespheres. voidDrawScene(floatangle) { Firstwecreate3unsignedintegerstoholdtheidentifiersforthedisplaylists. Onedisplaylistisusedforeachpartofthescene. Sincethevariablesaredeclaredstatic,theywillretaintheirvaluebetweencallstothefunction.     //Displaylistsforobjects     staticGLuintspheresList=0,torusList=0,baseList=0; Ifthevariable"spheresList"iszero,weuseglGenListstosaveanewdisplaylistidentifierintospheresList. Thiswillbenon-zero. Hencethecodebetweenthebracesisexecutedonlyonthefirstcallofthisfunction. ThisfillsthedisplaylistwiththeOpenGLcommandstogenerate4spheres.     //Createsphereslistifnecessary     if(!spheresList)     {         spheresList=glGenLists(1);         glNewList(spheresList,GL_COMPILE);         {             glColor3f(0.0f,1.0f,0.0f);             glPushMatrix();             glTranslatef(0.45f,1.0f,0.45f);             glutSolidSphere(0.2,24,24);             glTranslatef(-0.9f,0.0f,0.0f);             glutSolidSphere(0.2,24,24);             glTranslatef(0.0f,0.0f,-0.9f);             glutSolidSphere(0.2,24,24);             glTranslatef(0.9f,0.0f,0.0f);             glutSolidSphere(0.2,24,24);             glPopMatrix();         }         glEndList();     } Wesimilarlygenerateadisplaylistforatorusandaflatbase.     //Createtorusifnecessary     if(!torusList)     {         torusList=glGenLists(1);         glNewList(torusList,GL_COMPILE);         {             glColor3f(1.0f,0.0f,0.0f);             glPushMatrix();             glTranslatef(0.0f,0.5f,0.0f);             glRotatef(90.0f,1.0f,0.0f,0.0f);             glutSolidTorus(0.2,0.5,24,48);             glPopMatrix();         }         glEndList();     }     //Createbaseifnecessary     if(!baseList)     {         baseList=glGenLists(1);         glNewList(baseList,GL_COMPILE);         {             glColor3f(0.0f,0.0f,1.0f);             glPushMatrix();             glScalef(1.0f,0.05f,1.0f);             glutSolidCube(3.0f);             glPopMatrix();         }         glEndList();     } Nowwedrawthescenebycallingthedisplaylists,rotatingthespheresby"angle". Eachtimeafterthefirstthatthisfunctioniscalled,thisistheonlypartwhichwillbeexecuted.     //Drawobjects     glCallList(baseList);     glCallList(torusList);     glPushMatrix();     glRotatef(angle,0.0f,1.0f,0.0f);     glCallList(spheresList);     glPopMatrix(); } Nowlet'slookatthemainsourcefile,wherealloftheinterestingcodelives. Firstupistoincludethenecessaryheaders,including"GLee.h",theheaderfortheextensionloadinglibrary. #defineWIN32_LEAN_AND_MEAN #include #include #include"GLee/GLee.h"//GLheaderfile,includingextensions #include #include"Maths/Maths.h" #include"TIMER.h" #include"FPS_COUNTER.h" #include"scene.h" #include"main.h" Nowcreateourglobalobjects,thetimerandframespersecondcounter. //Timerusedforframerateindependentmovement TIMERtimer; //Framespersecondcounter FPS_COUNTERfpsCounter; Nowwecreateafewglobalvariables. Thecameraandlightpositionsarefixedatthevaluesgivenhere. Wealsofixtheshadowmapsizeat512x512andcreatespacetoholdtheshadowmaptextureidentifier. Wealsocreatespacetoholdtheprojectionandviewmatricesforthecameraandforthelight. //Camera&lightpositions VECTOR3DcameraPosition(-2.5f,3.5f,-2.5f); VECTOR3DlightPosition(2.0f,3.0f,-2.0f); //Sizeofshadowmap constintshadowMapSize=512; //Textures GLuintshadowMapTexture; //windowsize intwindowWidth,windowHeight; //Matrices MATRIX4X4lightProjectionMatrix,lightViewMatrix; MATRIX4X4cameraProjectionMatrix,cameraViewMatrix; The"Init"functioniscalledtoinitialisethedemo. //Calledforinitiation boolInit(void) { FirstweusetheGLeelibrarytocheckthattheARB_depth_textureandARB_shadowextensionsaresupported.     //Checkfornecessaryextensions     if(!GLEE_ARB_depth_texture||!GLEE_ARB_shadow)     {         printf("IrequireARB_depth_textureandARB_shadowextensionsn\n");         returnfalse;     } Nowwesettheinitialstatesforthemodelviewmatrix,shadinganddepthtesting. Wealsoenableback-facecullingforasmallspeedboost,andsinceweuseglScalewhendrawingthescene,weenableGL_NORMALIZE.     //Loadidentitymodelview     glMatrixMode(GL_MODELVIEW);     glLoadIdentity();     //Shadingstates     glShadeModel(GL_SMOOTH);     glClearColor(0.0f,0.0f,0.0f,0.0f);     glColor4f(1.0f,1.0f,1.0f,1.0f);     glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);     //Depthstates     glClearDepth(1.0f);     glDepthFunc(GL_LEQUAL);     glEnable(GL_DEPTH_TEST);     glEnable(GL_CULL_FACE);     //WeuseglScalewhendrawingthescene     glEnable(GL_NORMALIZE); Thenextstepistocreatetheshadowmaptexture. ItisasquaretextureofsizeshadowMapSize,andhasaformatof"DEPTH_COMPONENT". Wedonotwanttoinitialisethetexturedatawithanything,sowesetthepixelspointertoNULL.     //Createtheshadowmaptexture     glGenTextures(1,&shadowMapTexture);     glBindTexture(GL_TEXTURE_2D,shadowMapTexture);     glTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT,shadowMapSize,shadowMapSize,0,         GL_DEPTH_COMPONENT,GL_UNSIGNED_BYTE,NULL);     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); Wewantaneasywaytochangethediffuseandambientmaterialcoloroftheobjectsinthescene,soweuseglColorMaterialsothatchangingthecolorwillchangethismaterial. Wefixallsurfacestohaveawhitespecularreflectancecolorandaspecularexponentof16.     //Usethecolorastheambientanddiffusematerial     glColorMaterial(GL_FRONT,GL_AMBIENT_AND_DIFFUSE);     glEnable(GL_COLOR_MATERIAL);     //Whitespecularmaterialcolor,shininess16     glMaterialfv(GL_FRONT,GL_SPECULAR,white);     glMaterialf(GL_FRONT,GL_SHININESS,16.0f); Thematricesforthecameraandthelightaresetuphereandsavedintotheglobalvariables. Firstly,wesavethecurrentmodelviewmatrix. Then,foreachmatrixwewanttosetup,wefirstlyloadidentity,thencalltherelevantOpenGLfunctionstocreatethematrixonthemodelviewstack. Thisisthenreadbackintotheglobalmatrixvariable. Finally,themodelviewmatrixisrestored. Notethatwearecreatingallmatrices,includingtheprojectionones,onthemodelviewstack. ThisiswhyGetFloatvalwaysreadsthemodelviewmatrix. Thelightandcamerahavedifferenctprojectionmatrices. Inordertomakethemostoftheprecisionwehaveavailable,thelight'snearandfarplanesareplacedasclosetogetheraspossible. Also,thelighthasanaspectratioof1,soitsfrustumisatruncatedsquarepyramid.     //Calculate&savematrices     glPushMatrix();     glLoadIdentity();     gluPerspective(45.0f,(float)windowWidth/windowHeight,1.0f,100.0f);     glGetFloatv(GL_MODELVIEW_MATRIX,cameraProjectionMatrix);     glLoadIdentity();     gluLookAt(cameraPosition.x,cameraPosition.y,cameraPosition.z,     0.0f,0.0f,0.0f,     0.0f,1.0f,0.0f);     glGetFloatv(GL_MODELVIEW_MATRIX,cameraViewMatrix);     glLoadIdentity();     gluPerspective(45.0f,1.0f,2.0f,8.0f);     glGetFloatv(GL_MODELVIEW_MATRIX,lightProjectionMatrix);     glLoadIdentity();     gluLookAt(lightPosition.x,lightPosition.y,lightPosition.z,     0.0f,0.0f,0.0f,     0.0f,1.0f,0.0f);     glGetFloatv(GL_MODELVIEW_MATRIX,lightViewMatrix);     glPopMatrix(); Finally,weresetthetimerandreturntrue.     //Resettimer     timer.Reset();     returntrue; } Displayiscalledtodrawaframe. //Calledtodrawscene voidDisplay(void) { Firstwecalculatetheangleofthespheres. Byusingthetimer,therateofrotationwillbeindependentoftheframerate.     //angleofspheresinscene.Calculatefromtime     floatangle=timer.GetTime()/10; Forthefirstpass,wedrawthescenefromthelight'spointofview. Clearthecoloranddepthbuffersandsetthematricestothoseforthelight. Useaviewportofthesamesizeastheshadowmap.     //Firstpass-fromlight'spointofview     glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);     glMatrixMode(GL_PROJECTION);     glLoadMatrixf(lightProjectionMatrix);     glMatrixMode(GL_MODELVIEW);     glLoadMatrixf(lightViewMatrix);     //Useviewportthesamesizeastheshadowmap     glViewport(0,0,shadowMapSize,shadowMapSize); HereweinstructOpenGLtocullfrontfaces,sothebackfacesaredrawnintotheshadowmap. Thisdealswiththeissueoffiniteprecisionasexplainedabove. Wealsodisablecolorwritesanduseflatshading,sinceweareonlyinterestedinthecontentsofthedepthbuffer.     //Drawbackfacesintotheshadowmap     glCullFace(GL_FRONT);     //Disablecolorwrites,anduseflatshadingforspeed     glShadeModel(GL_FLAT);     glColorMask(0,0,0,0); Wearenowreadytodrawthescene.     //Drawthescene     DrawScene(angle); CopyTexSubImage2Disusedtocopythecontentsoftheframebufferintoatexture. Firstwebindtheshadowmaptexture,thencopytheviewportintothetexture. SincewehaveboundaDEPTH_COMPONENTtexture,thedatareadwillautomaticallycomefromthedepthbuffer.     //Readthedepthbufferintotheshadowmaptexture     glBindTexture(GL_TEXTURE_2D,shadowMapTexture);     glCopyTexSubImage2D(GL_TEXTURE_2D,0,0,0,0,0,shadowMapSize,shadowMapSize); Finallyforthispass,restorethestateswehavechanged.     //restorestates     glCullFace(GL_BACK);     glShadeModel(GL_SMOOTH);     glColorMask(1,1,1,1); Inthesecondpass,wedrawthescenefromthecamera'spointofview,withthelightsettothebrightnessoftheshadowedareas. Firstly,clearthedepthbuffer. Thereisnoneedtoclearthecolorbuffersinceithasnotyetbeenwrittento. Then,setupthematricestodrawfromthecamera'spointofview,anduseaviewportwhichcoversthewholewindow.     //2ndpass-Drawfromcamera'spointofview     glClear(GL_DEPTH_BUFFER_BIT);     glMatrixMode(GL_PROJECTION);     glLoadMatrixf(cameraProjectionMatrix);     glMatrixMode(GL_MODELVIEW);     glLoadMatrixf(cameraViewMatrix);     glViewport(0,0,windowWidth,windowHeight); Thelightissetupasnecessaryfortheunshadowedregions. Inparticular,weuseadimdiffusebrightnessandazerospecularbrightness.     //Usedimlighttorepresentshadowedareas     glLightfv(GL_LIGHT1,GL_POSITION,VECTOR4D(lightPosition));     glLightfv(GL_LIGHT1,GL_AMBIENT,white*0.2f);     glLightfv(GL_LIGHT1,GL_DIFFUSE,white*0.2f);     glLightfv(GL_LIGHT1,GL_SPECULAR,black);     glEnable(GL_LIGHT1);     glEnable(GL_LIGHTING); DrawScene(angle); Thethirdpassiswheretheactualshadowcalculationstakeplace. Ifafragmentpassestheshadowtest(i.e.isunshadowed)thenwewantittobelitbrightly,overwritingthedimpixelfromthepreviouspass. So,enableabrightlight,withfullspecularbrightness.     //3rdpass     //Drawwithbrightlight     glLightfv(GL_LIGHT1,GL_DIFFUSE,white);     glLightfv(GL_LIGHT1,GL_SPECULAR,white); Here,wecalculatethetexgenmatrixwhichwewilluseforprojectingtheshadowmapontothesceneandenabletexturecoordinategeneration,asdescribedabove.     //Calculatetexturematrixforprojection     //Thismatrixtakesusfromeyespacetothelight'sclipspace     //Itispostmultipliedbytheinverseofthecurrentviewmatrixwhenspecifyingtexgen     staticMATRIX4X4biasMatrix(0.5f,0.0f,0.0f,0.0f,     0.0f,0.5f,0.0f,0.0f,     0.0f,0.0f,0.5f,0.0f,     0.5f,0.5f,0.5f,1.0f);//biasfrom[-1,1]to[0,1]     MATRIX4X4textureMatrix=biasMatrix*lightProjectionMatrix*lightViewMatrix;     //Setuptexturecoordinategeneration.     glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR);     glTexGenfv(GL_S,GL_EYE_PLANE,textureMatrix.GetRow(0));     glEnable(GL_TEXTURE_GEN_S);     glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR);     glTexGenfv(GL_T,GL_EYE_PLANE,textureMatrix.GetRow(1));     glEnable(GL_TEXTURE_GEN_T);     glTexGeni(GL_R,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR);     glTexGenfv(GL_R,GL_EYE_PLANE,textureMatrix.GetRow(2));     glEnable(GL_TEXTURE_GEN_R);     glTexGeni(GL_Q,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR);     glTexGenfv(GL_Q,GL_EYE_PLANE,textureMatrix.GetRow(3));     glEnable(GL_TEXTURE_GEN_Q); Nowwebindandenabletheshadowmaptexture,andsetuptheautomaticshadowcomparison. Firstweenablethecomparison,thentelltheGLtogeneratea"true"resultifrislessthanorequaltothevaluestoredinthetexture. Theshadowcomparisonproduceseithera0or1perfragmentforaresult. WeinstructtheGLtoreplicatethistoall4colorchannels,i.e.togenerateanintensityresult.     //Bind&enableshadowmaptexture     glBindTexture(GL_TEXTURE_2D,shadowMapTexture);     glEnable(GL_TEXTURE_2D);     //Enableshadowcomparison     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_COMPARE_MODE_ARB,GL_COMPARE_R_TO_TEXTURE);     //Shadowcomparisonshouldbetrue(ienotinshadow)ifr<=texture     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_COMPARE_FUNC_ARB,GL_LEQUAL);     //ShadowcomparisonshouldgenerateanINTENSITYresult     glTexParameteri(GL_TEXTURE_2D,GL_DEPTH_TEXTURE_MODE_ARB,GL_INTENSITY); Iftheshadowcomparisonpasses,analphavalueof1willbegenerated. So,weusethealphatesttodiscardallfragmentswithalphalessthan0.99. Thisway,fragmentswhichfailtheshadowtestwillnotbedisplayed,soallowingthedarkerresultfromthepreviouspasstoshow.     //Setalphatesttodiscardfalsecomparisons     glAlphaFunc(GL_GEQUAL,0.99f);     glEnable(GL_ALPHA_TEST); Thesceneisthendrawnforthethirdandfinaltime,thenanychangedstatesarereset.     DrawScene(angle);     //Disabletexturesandtexgen     glDisable(GL_TEXTURE_2D);     glDisable(GL_TEXTURE_GEN_S);     glDisable(GL_TEXTURE_GEN_T);     glDisable(GL_TEXTURE_GEN_R);     glDisable(GL_TEXTURE_GEN_Q);     //Restoreotherstates     glDisable(GL_LIGHTING);     glDisable(GL_ALPHA_TEST); Inordertoseehowwellthedemoisrunningonyourcomputer,wewilldisplayaframespersecondcounterinthetoplefthandcornerofthewindow. Inordertodothis,wefirstcallFPS_COUNTER::Updateinordertocalculatetheframespersecond.     //Updateframespersecondcounter     fpsCounter.Update(); sprintfisusedtoconverttheframespersecondfigurefromafloatingpointvaluetoastring.     //Printfps     staticcharfpsString[32];     sprintf(fpsString,"%.2f",fpsCounter.GetFps()); Next,theprojectionandmodelviewmatricesaresetupforasimpleorthographicprojection. TheoldmatricesaresavedontothestackbyusingglPushMatrix.     //Setmatricesforortho     glMatrixMode(GL_PROJECTION);     glPushMatrix();     glLoadIdentity();     gluOrtho2D(-1.0f,1.0f,-1.0f,1.0f);     glMatrixMode(GL_MODELVIEW);     glPushMatrix();     glLoadIdentity(); Nowwecanprintthetextacharacteratatimeusingoneofthefunctionsprovidedbytheglutlibrary.     //Printtext     glRasterPos2f(-1.0f,0.9f);     for(unsignedinti=0;i



請為這篇文章評分?