Point Shadows - LearnOpenGL

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

The technique is mostly similar to directional shadow mapping: we generate a depth map from the light's perspective(s), sample the depth map based on the ... Ifyou'rerunningAdBlock,pleaseconsiderwhitelistingthissiteifyou'dliketosupportLearnOpenGL;andnoworries,Iwon'tbemadifyoudon't:) IntroductionGettingstartedOpenGLCreatingawindowHelloWindowHelloTriangleShadersTexturesTransformationsCoordinateSystemsCameraReviewLightingColorsBasicLightingMaterialsLightingmapsLightcastersMultiplelightsReviewModelLoadingAssimpMeshModelAdvancedOpenGLDepthtestingStenciltestingBlendingFacecullingFramebuffersCubemapsAdvancedDataAdvancedGLSLGeometryShaderInstancingAntiAliasingAdvancedLightingAdvancedLightingGammaCorrectionShadowsShadowMappingPointShadowsNormalMappingParallaxMappingHDRBloomDeferredShadingSSAOPBRTheoryLightingIBLDiffuseirradianceSpecularIBLInPracticeDebuggingTextRendering2DGameBreakoutSettingupRenderingSpritesLevelsCollisionsBallCollisiondetectionCollisionresolutionParticlesPostprocessingPowerupsAudioRendertextFinalthoughtsGuestArticlesHowtopublish2020OITIntroductionWeightedBlendedSkeletalAnimation2021CSMSceneSceneGraphFrustumCullingTessellationHeightmapTessellationDSACoderepositoryTranslationsAbout BTC 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa ETH/ERC20 0x1de59bd9e52521a46309474f8372531533bd7c43 PointShadows Advanced-Lighting/Shadows/Point-Shadows Inthelastchapterwelearnedtocreatedynamicshadowswithshadowmapping.Itworksgreat,butit'smostlysuitedfordirectional(orspot)lightsastheshadowsaregeneratedonlyinthedirectionofthelightsource.Itisthereforealsoknownasdirectionalshadowmappingasthedepth(orshadow)mapisgeneratedfromonlythedirectionthelightislookingat. Whatthischapterwillfocusonisthegenerationofdynamicshadowsinallsurroundingdirections.Thetechniquewe'reusingisperfectforpointlightsasarealpointlightwouldcastshadowsinalldirections.Thistechniqueisknownaspoint(light)shadowsormoreformerlyasomnidirectionalshadowmaps. Thischapterbuildsuponthepreviousshadowmappingchaptersounlessyou'refamiliarwithtraditionalshadowmappingitisadvisedtoreadtheshadowmappingchapterfirst. Thetechniqueismostlysimilartodirectionalshadowmapping:wegenerateadepthmapfromthelight'sperspective(s),samplethedepthmapbasedonthecurrentfragmentposition,andcompareeachfragmentwiththestoreddepthvaluetoseewhetheritisinshadow.Themaindifferencebetweendirectionalshadowmappingandomnidirectionalshadowmappingisthedepthmapweuse. Thedepthmapweneedrequiresrenderingascenefromallsurroundingdirectionsofapointlightandassuchanormal2Ddepthmapwon'twork;whatifweweretouseacubemapinstead?Becauseacubemapcanstorefullenvironmentdatawithonly6faces,itispossibletorendertheentirescenetoeachofthefacesofacubemapandsampletheseasthepointlight'ssurroundingdepthvalues. Thegenerateddepthcubemapisthenpassedtothelightingfragmentshaderthatsamplesthecubemapwithadirectionvectortoobtaintheclosestdepth(fromthelight'sperspective)atthatfragment.Mostofthecomplicatedstuffwe'vealreadydiscussedintheshadowmappingchapter.Whatmakesthistechniqueabitmoredifficultisthedepthcubemapgeneration. Generatingthedepthcubemap Tocreateacubemapofalight'ssurroundingdepthvalueswehavetorenderthescene6times:onceforeachface.One(quiteobvious)waytodothis,isrenderthescene6timeswith6differentviewmatrices,eachtimeattachingadifferentcubemapfacetotheframebufferobject.Thiswouldlooksomethinglikethis: for(unsignedinti=0;i<6;i++) { GLenumface=GL_TEXTURE_CUBE_MAP_POSITIVE_X+i; glFramebufferTexture2D(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,face,depthCubemap,0); BindViewMatrix(lightViewMatrices[i]); RenderScene(); } Thiscanbequiteexpensivethoughasalotofrendercallsarenecessaryforthissingledepthmap.Inthischapterwe'regoingtouseanalternative(moreorganized)approachusingalittletrickinthegeometryshaderthatallowsustobuildthedepthcubemapwithjustasinglerenderpass. First,we'llneedtocreateacubemap: unsignedintdepthCubemap; glGenTextures(1,&depthCubemap); Andassigneachofthesinglecubemapfacesa2Ddepth-valuedtextureimage: constunsignedintSHADOW_WIDTH=1024,SHADOW_HEIGHT=1024; glBindTexture(GL_TEXTURE_CUBE_MAP,depthCubemap); for(unsignedinti=0;i<6;++i) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i,0,GL_DEPTH_COMPONENT, SHADOW_WIDTH,SHADOW_HEIGHT,0,GL_DEPTH_COMPONENT,GL_FLOAT,NULL); Anddon'tforgettosetthetextureparameters: glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_MAG_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_MIN_FILTER,GL_NEAREST); 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); Normallywe'dattachasinglefaceofacubemaptexturetotheframebufferobjectandrenderthescene6times,eachtimeswitchingthedepthbuffertargetoftheframebuffertoadifferentcubemapface.Sincewe'regoingtouseageometryshader,thatallowsustorendertoallfacesinasinglepass,wecandirectlyattachthecubemapasaframebuffer'sdepthattachmentwithglFramebufferTexture: glBindFramebuffer(GL_FRAMEBUFFER,depthMapFBO); glFramebufferTexture(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,depthCubemap,0); glDrawBuffer(GL_NONE); glReadBuffer(GL_NONE); glBindFramebuffer(GL_FRAMEBUFFER,0); Again,notethecalltoglDrawBufferandglReadBuffer:weonlycareaboutdepthvalueswhengeneratingadepthcubemapsowehavetoexplicitlytellOpenGLthisframebufferobjectdoesnotrendertoacolorbuffer. Withomnidirectionalshadowmapswehavetworenderpasses:first,wegeneratethedepthcubemapandsecond,weusethedepthcubemapinthenormalrenderpasstoaddshadowstothescene.Thisprocesslooksabitlikethis: //1.firstrendertodepthcubemap glViewport(0,0,SHADOW_WIDTH,SHADOW_HEIGHT); glBindFramebuffer(GL_FRAMEBUFFER,depthMapFBO); glClear(GL_DEPTH_BUFFER_BIT); ConfigureShaderAndMatrices(); RenderScene(); glBindFramebuffer(GL_FRAMEBUFFER,0); //2.thenrendersceneasnormalwithshadowmapping(usingdepthcubemap) glViewport(0,0,SCR_WIDTH,SCR_HEIGHT); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); ConfigureShaderAndMatrices(); glBindTexture(GL_TEXTURE_CUBE_MAP,depthCubemap); RenderScene(); Theprocessisexactlythesameaswithdefaultshadowmapping,althoughthistimewerendertoanduseacubemapdepthtexturecomparedtoa2Ddepthtexture. Lightspacetransform Withtheframebufferandcubemapset,weneedsomewaytotransformallthescene'sgeometrytotherelevantlightspacesinall6directionsofthelight.Justliketheshadowmappingchapterwe'regoingtoneedalightspacetransformationmatrix\(T\),butthistimeoneforeachface. Eachlightspacetransformationmatrixcontainsbothaprojectionandaviewmatrix.Fortheprojectionmatrixwe'regoingtouseaperspectiveprojectionmatrix;thelightsourcerepresentsapointinspacesoperspectiveprojectionmakesmostsense.Eachlightspacetransformationmatrixusesthesameprojectionmatrix: floataspect=(float)SHADOW_WIDTH/(float)SHADOW_HEIGHT; floatnear=1.0f; floatfar=25.0f; glm::mat4shadowProj=glm::perspective(glm::radians(90.0f),aspect,near,far); Importanttonotehereisthefieldofviewparameterofglm::perspectivethatwesetto90degrees.Bysettingthisto90degreeswemakesuretheviewingfieldisexactlylargeenoughtofillasinglefaceofthecubemapsuchthatallfacesaligncorrectlytoeachotherattheedges. Astheprojectionmatrixdoesnotchangeperdirectionwecanre-useitforeachofthe6transformationmatrices.Wedoneedadifferentviewmatrixperdirection.Withglm::lookAtwecreate6viewdirections,eachlookingatonefacedirectionofthecubemapintheorder:right,left,top,bottom,nearandfar. std::vector<:mat4>shadowTransforms; shadowTransforms.push_back(shadowProj* glm::lookAt(lightPos,lightPos+glm::vec3(1.0,0.0,0.0),glm::vec3(0.0,-1.0,0.0)); shadowTransforms.push_back(shadowProj* glm::lookAt(lightPos,lightPos+glm::vec3(-1.0,0.0,0.0),glm::vec3(0.0,-1.0,0.0)); shadowTransforms.push_back(shadowProj* glm::lookAt(lightPos,lightPos+glm::vec3(0.0,1.0,0.0),glm::vec3(0.0,0.0,1.0)); shadowTransforms.push_back(shadowProj* glm::lookAt(lightPos,lightPos+glm::vec3(0.0,-1.0,0.0),glm::vec3(0.0,0.0,-1.0)); shadowTransforms.push_back(shadowProj* glm::lookAt(lightPos,lightPos+glm::vec3(0.0,0.0,1.0),glm::vec3(0.0,-1.0,0.0)); shadowTransforms.push_back(shadowProj* glm::lookAt(lightPos,lightPos+glm::vec3(0.0,0.0,-1.0),glm::vec3(0.0,-1.0,0.0)); Herewecreate6viewmatricesandmultiplythemwiththeprojectionmatrixtogetatotalof6differentlightspacetransformationmatrices.Thetargetparameterofglm::lookAteachlooksintothedirectionofasinglecubemapface. Thesetransformationmatricesaresenttotheshadersthatrenderthedepthintothecubemap. Depthshaders Torenderdepthvaluestoadepthcubemapwe'regoingtoneedatotalofthreeshaders:avertexandfragmentshader,andageometryshaderinbetween. Thegeometryshaderwillbetheshaderresponsiblefortransformingallworld-spaceverticestothe6differentlightspaces.Therefore,thevertexshadersimplytransformsverticestoworld-spaceanddirectsthemtothegeometryshader: #version330core layout(location=0)invec3aPos; uniformmat4model; voidmain() { gl_Position=model*vec4(aPos,1.0); } Thegeometryshaderwilltakeasinput3triangleverticesandauniformarrayoflightspacetransformationmatrices.Thegeometryshaderisresponsiblefortransformingtheverticestothelightspaces;thisisalsowhereitgetsinteresting. Thegeometryshaderhasabuilt-invariablecalledgl_Layerthatspecifieswhichcubemapfacetoemitaprimitiveto.Whenleftalone,thegeometryshaderjustsendsitsprimitivesfurtherdownthepipelineasusual,butwhenweupdatethisvariablewecancontroltowhichcubemapfacewerendertoforeachprimitive.Thisofcourseonlyworkswhenwehaveacubemaptextureattachedtotheactiveframebuffer. #version330core layout(triangles)in; layout(triangle_strip,max_vertices=18)out; uniformmat4shadowMatrices[6]; outvec4FragPos;//FragPosfromGS(outputperemitvertex) voidmain() { for(intface=0;face<6;++face) { gl_Layer=face;//built-invariablethatspecifiestowhichfacewerender. for(inti=0;i<3;++i)//foreachtrianglevertex { FragPos=gl_in[i].gl_Position; gl_Position=shadowMatrices[face]*FragPos; EmitVertex(); } EndPrimitive(); } } Thisgeometryshaderisrelativelystraightforward.Wetakeasinputatriangle,andoutputatotalof6triangles(6*3equals18vertices).Inthemainfunctionweiterateover6cubemapfaceswherewespecifyeachfaceastheoutputfacebystoringthefaceintegerintogl_Layer.Wethengeneratetheoutputtrianglesbytransformingeachworld-spaceinputvertextotherelevantlightspacebymultiplyingFragPoswiththeface'slight-spacetransformationmatrix.NotethatwealsosenttheresultingFragPosvariabletothefragmentshaderthatwe'llneedtocalculateadepthvalue. InthelastchapterweusedanemptyfragmentshaderandletOpenGLfigureoutthedepthvaluesofthedepthmap.Thistimewe'regoingtocalculateourown(linear)depthasthelineardistancebetweeneachclosestfragmentpositionandthelightsource'sposition.Calculatingourowndepthvaluesmakesthelatershadowcalculationsabitmoreintuitive. #version330core invec4FragPos; uniformvec3lightPos; uniformfloatfar_plane; voidmain() { //getdistancebetweenfragmentandlightsource floatlightDistance=length(FragPos.xyz-lightPos); //mapto[0;1]rangebydividingbyfar_plane lightDistance=lightDistance/far_plane; //writethisasmodifieddepth gl_FragDepth=lightDistance; } ThefragmentshadertakesasinputtheFragPosfromthegeometryshader,thelight'spositionvector,andthefrustum'sfarplanevalue.Herewetakethedistancebetweenthefragmentandthelightsource,mapittothe[0,1]rangeandwriteitasthefragment'sdepthvalue. Renderingthescenewiththeseshadersandthecubemap-attachedframebufferobjectactiveshouldgiveyouacompletelyfilleddepthcubemapforthesecondpass'sshadowcalculations. Omnidirectionalshadowmaps Witheverythingsetupitistimetorendertheactualomnidirectionalshadows.Theprocedureissimilartothedirectionalshadowmappingchapter,althoughthistimewebindacubemaptextureinsteadofa2Dtextureandalsopassthelightprojection'sfarplanevariabletotheshaders. glViewport(0,0,SCR_WIDTH,SCR_HEIGHT); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); shader.use(); //...senduniformstoshader(includinglight'sfar_planevalue) glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_CUBE_MAP,depthCubemap); //...bindothertextures RenderScene(); HeretherenderScenefunctionrendersafewcubesinalargecuberoomscatteredaroundalightsourceatthecenterofthescene. Thevertexandfragmentshaderaremostlysimilartotheoriginalshadowmappingshaders:thedifferencebeingthatthefragmentshadernolongerrequiresafragmentpositioninlightspaceaswecannowsamplethedepthvalueswithadirectionvector. Becauseofthis,thevertexshaderdoesn'tneedstotransformitspositionvectorstolightspacesowecanremovetheFragPosLightSpacevariable: #version330core layout(location=0)invec3aPos; layout(location=1)invec3aNormal; layout(location=2)invec2aTexCoords; outvec2TexCoords; outVS_OUT{ vec3FragPos; vec3Normal; vec2TexCoords; }vs_out; uniformmat4projection; uniformmat4view; uniformmat4model; voidmain() { vs_out.FragPos=vec3(model*vec4(aPos,1.0)); vs_out.Normal=transpose(inverse(mat3(model)))*aNormal; vs_out.TexCoords=aTexCoords; gl_Position=projection*view*model*vec4(aPos,1.0); } Thefragmentshader'sBlinn-Phonglightingcodeisexactlythesameaswehadbeforewithashadowmultiplicationattheend: #version330core outvec4FragColor; inVS_OUT{ vec3FragPos; vec3Normal; vec2TexCoords; }fs_in; uniformsampler2DdiffuseTexture; uniformsamplerCubedepthMap; uniformvec3lightPos; uniformvec3viewPos; uniformfloatfar_plane; floatShadowCalculation(vec3fragPos) { [...] } voidmain() { vec3color=texture(diffuseTexture,fs_in.TexCoords).rgb; vec3normal=normalize(fs_in.Normal); vec3lightColor=vec3(0.3); //ambient vec3ambient=0.3*color; //diffuse vec3lightDir=normalize(lightPos-fs_in.FragPos); floatdiff=max(dot(lightDir,normal),0.0); vec3diffuse=diff*lightColor; //specular vec3viewDir=normalize(viewPos-fs_in.FragPos); vec3reflectDir=reflect(-lightDir,normal); floatspec=0.0; vec3halfwayDir=normalize(lightDir+viewDir); spec=pow(max(dot(normal,halfwayDir),0.0),64.0); vec3specular=spec*lightColor; //calculateshadow floatshadow=ShadowCalculation(fs_in.FragPos); vec3lighting=(ambient+(1.0-shadow)*(diffuse+specular))*color; FragColor=vec4(lighting,1.0); } Thereareafewsubtledifferences:thelightingcodeisthesame,butwenowhaveasamplerCubeuniformandtheShadowCalculationfunctiontakesthecurrentfragment'spositionasitsargumentinsteadofthefragmentpositioninlightspace.Wenowalsoincludethelightfrustum'sfar_planevaluethatwe'lllaterneed. ThebiggestdifferenceisinthecontentoftheShadowCalculationfunctionthatnowsamplesdepthvaluesfromacubemapinsteadofa2Dtexture.Let'sdiscussitscontentstepbystep. Thefirstthingwehavetodoisretrievethedepthofthecubemap.Youmayrememberfromthecubemapsectionofthischapterthatwestoredthedepthasthelineardistancebetweenthefragmentandthelightposition;we'retakingasimilarapproachhere: floatShadowCalculation(vec3fragPos) { vec3fragToLight=fragPos-lightPos; floatclosestDepth=texture(depthMap,fragToLight).r; } Herewetakethedifferencevectorbetweenthefragment'spositionandthelight'spositionandusethatvectorasadirectionvectortosamplethecubemap.Thedirectionvectordoesn'tneedtobeaunitvectortosamplefromacubemapsothere'snoneedtonormalizeit.TheresultingclosestDepthvalueisthenormalizeddepthvaluebetweenthelightsourceanditsclosestvisiblefragment. TheclosestDepthvalueiscurrentlyintherange[0,1]sowefirsttransformitbackto[0,far_plane]bymultiplyingitwithfar_plane. closestDepth*=far_plane; Nextweretrievethedepthvaluebetweenthecurrentfragmentandthelightsource,whichwecaneasilyobtainbytakingthelengthoffragToLightduetohowwecalculateddepthvaluesinthecubemap: floatcurrentDepth=length(fragToLight); Thisreturnsadepthvalueinthesame(orlarger)rangeasclosestDepth. Nowwecancomparebothdepthvaluestoseewhichiscloserthantheotheranddeterminewhetherthecurrentfragmentisinshadow.Wealsoincludeashadowbiassowedon'tgetshadowacneasdiscussedinthepreviouschapter. floatbias=0.05; floatshadow=currentDepth-bias>closestDepth?1.0:0.0; ThecompleteShadowCalculationthenbecomes: floatShadowCalculation(vec3fragPos) { //getvectorbetweenfragmentpositionandlightposition vec3fragToLight=fragPos-lightPos; //usethelighttofragmentvectortosamplefromthedepthmap floatclosestDepth=texture(depthMap,fragToLight).r; //itiscurrentlyinlinearrangebetween[0,1].Re-transformbacktooriginalvalue closestDepth*=far_plane; //nowgetcurrentlineardepthasthelengthbetweenthefragmentandlightposition floatcurrentDepth=length(fragToLight); //nowtestforshadows floatbias=0.05; floatshadow=currentDepth-bias>closestDepth?1.0:0.0; returnshadow; } Withtheseshaderswealreadygetprettygoodshadowsandthistimeinallsurroundingdirectionsfromapointlight.Withapointlightpositionedatthecenterofasimplesceneit'lllookabitlikethis: Youcanfindthesourcecodeofthisdemohere. Visualizingcubemapdepthbuffer Ifyou'resomewhatlikemeyouprobablydidn'tgetthisrightonthefirsttrysoitmakessensetodosomedebugging,withoneoftheobviouschecksbeingvalidatingwhetherthedepthmapwasbuiltcorrectly.AsimpletricktovisualizethedepthbufferistotaketheclosestDepthvariableintheShadowCalculationfunctionanddisplaythatvariableas: FragColor=vec4(vec3(closestDepth/far_plane),1.0); Theresultisagrayedoutscenewhereeachcolorrepresentsthelineardepthvaluesofthescene: Youcanalsoseetheto-beshadowedregionsontheoutsidewall.Ifitlookssomewhatsimilar,youknowthedepthcubemapwasproperlygenerated. PCF Sinceomnidirectionalshadowmapsarebasedonthesameprinciplesoftraditionalshadowmappingitalsohasthesameresolutiondependentartifacts.Ifyouzoomincloseenoughyoucanagainseejaggededges.Percentage-closerfilteringorPCFallowsustosmoothoutthesejaggededgesbyfilteringmultiplesamplesaroundthefragmentpositionandaveragetheresults. IfwetakethesamesimplePCFfilterofthepreviouschapterandaddathirddimensionweget: floatshadow=0.0; floatbias=0.05; floatsamples=4.0; floatoffset=0.1; for(floatx=-offset;xclosestDepth) shadow+=1.0; } } } shadow/=(samples*samples*samples); Thecodeisn'tthatdifferentfromthetraditionalshadowmappingcode.Wecalculateandaddtextureoffsetsdynamicallyforeachaxisbasedonafixednumberofsamples.Foreachsamplewerepeattheoriginalshadowprocessontheoffsettedsampledirectionandaveragetheresultsattheend. Theshadowsnowlookmoresoftandsmoothandgivemoreplausibleresults. However,withsamplessetto4.0wetakeatotalof64sampleseachfragmentwhichisalot! Asmostofthesesamplesareredundantinthattheysampleclosetotheoriginaldirectionvectoritmaymakemoresensetoonlysampleinperpendiculardirectionsofthesampledirectionvector.Howeverasthereisno(easy)waytofigureoutwhichsub-directionsareredundantthisbecomesdifficult.Onetrickwecanuseistotakeanarrayofoffsetdirectionsthatareallroughlyseparablee.g.eachofthempointsincompletelydifferentdirections.Thiswillsignificantlyreducethenumberofsub-directionsthatareclosetogether.Belowwehavesuchanarrayofamaximumof20offsetdirections: vec3sampleOffsetDirections[20]=vec3[] ( vec3(1,1,1),vec3(1,-1,1),vec3(-1,-1,1),vec3(-1,1,1), vec3(1,1,-1),vec3(1,-1,-1),vec3(-1,-1,-1),vec3(-1,1,-1), vec3(1,1,0),vec3(1,-1,0),vec3(-1,-1,0),vec3(-1,1,0), vec3(1,0,1),vec3(-1,0,1),vec3(1,0,-1),vec3(-1,0,-1), vec3(0,1,1),vec3(0,-1,1),vec3(0,-1,-1),vec3(0,1,-1) ); FromthiswecanadaptthePCFalgorithmtotakeafixedamountofsamplesfromsampleOffsetDirectionsandusethesetosamplethecubemap.Theadvantagehereisthatweneedalotlesssamplestogetvisuallysimilarresults. floatshadow=0.0; floatbias=0.15; intsamples=20; floatviewDistance=length(viewPos-fragPos); floatdiskRadius=0.05; for(inti=0;iclosestDepth) shadow+=1.0; } shadow/=float(samples); Hereweaddmultipleoffsets,scaledbysomediskRadius,aroundtheoriginalfragToLightdirectionvectortosamplefromthecubemap. AnotherinterestingtrickwecanapplyhereisthatwecanchangediskRadiusbasedonthedistanceoftheviewertothefragment,makingtheshadowssofterwhenfarawayandsharperwhencloseby. floatdiskRadius=(1.0+(viewDistance/far_plane))/25.0; TheresultsoftheupdatedPCFalgorithmgivesjustasgood,ifnotbetter,resultsofsoftshadows: Ofcourse,thebiasweaddtoeachsampleishighlybasedoncontextandwillalwaysrequiretweakingbasedonthesceneyou'reworkingwith.Playaroundwithallthevaluesandseehowtheyaffectthescene. Youcanfindthefinalcodehere:here. Ishouldmentionthatusinggeometryshaderstogenerateadepthmapisn'tnecessarilyfasterthanrenderingthescene6timesforeachface.Usingageometryshaderlikethishasitsownperformancepenaltiesthatmayoutweightheperformancegainofusingoneinthefirstplace.Thisofcoursedependsonthetypeofenvironment,thespecificvideocarddrivers,andplentyofotherfactors.Soifyoureallycareaboutpushingthemostoutofyoursystem,makesuretoprofilebothmethodsandselectthemoreefficientoneforyourscene. Additionalresources ShadowMappingforpointlightsourcesinOpenGL:omnidirectionalshadowmappingtutorialbysunandblackcat. MultipassShadowMappingWithPointLights:omnidirectionalshadowmappingtutorialbyogldev. Omni-directionalShadows:anicesetofslidesaboutomnidirectionalshadowmappingbyPeterHouska. HI



請為這篇文章評分?