Normal Mapping - LearnOpenGL

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

The normal map is defined in tangent space, so one way to solve the problem is to calculate a matrix to transform normals from tangent space to a different ... Ifyou'rerunningAdBlock,pleaseconsiderwhitelistingthissiteifyou'dliketosupportLearnOpenGL;andnoworries,Iwon'tbemadifyoudon't:) IntroductionGettingstartedOpenGLCreatingawindowHelloWindowHelloTriangleShadersTexturesTransformationsCoordinateSystemsCameraReviewLightingColorsBasicLightingMaterialsLightingmapsLightcastersMultiplelightsReviewModelLoadingAssimpMeshModelAdvancedOpenGLDepthtestingStenciltestingBlendingFacecullingFramebuffersCubemapsAdvancedDataAdvancedGLSLGeometryShaderInstancingAntiAliasingAdvancedLightingAdvancedLightingGammaCorrectionShadowsShadowMappingPointShadowsNormalMappingParallaxMappingHDRBloomDeferredShadingSSAOPBRTheoryLightingIBLDiffuseirradianceSpecularIBLInPracticeDebuggingTextRendering2DGameBreakoutSettingupRenderingSpritesLevelsCollisionsBallCollisiondetectionCollisionresolutionParticlesPostprocessingPowerupsAudioRendertextFinalthoughtsGuestArticlesHowtopublish2020OITIntroductionWeightedBlendedSkeletalAnimation2021CSMSceneSceneGraphFrustumCullingTessellationHeightmapTessellationDSACoderepositoryTranslationsAbout BTC 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa ETH/ERC20 0x1de59bd9e52521a46309474f8372531533bd7c43 NormalMapping Advanced-Lighting/Normal-Mapping Allofourscenesarefilledwithmeshes,eachconsistingofhundredsormaybethousandsoftriangles.Weboostedtherealismbywrapping2Dtexturesontheseflattriangles,hidingthefactthatthepolygonsarejusttinyflattriangles.Textureshelp,butwhenyoutakeagoodcloselookatthemeshesitisstillquiteeasytoseetheunderlyingflatsurfaces.Mostreal-lifesurfacearen'tflathoweverandexhibitalotof(bumpy)details. Forinstance,takeabricksurface.Abricksurfaceisquitearoughsurfaceandobviouslynotcompletelyflat:itcontainssunkencementstripesandalotofdetailedlittleholesandcracks.Ifweweretoviewsuchabricksurfaceinalitscenetheimmersiongetseasilybroken.Belowwecanseeabricktextureappliedtoaflatsurfacelitbyapointlight. Thelightingdoesn'ttakeanyofthesmallcracksandholesintoaccountandcompletelyignoresthedeepstripesbetweenthebricks;thesurfacelooksperfectlyflat.Wecanpartlyfixtheflatlookbyusingaspecularmaptopretendsomesurfacesarelesslitduetodepthorotherdetails,butthat'smoreofahackthanarealsolution.Whatweneedissomewaytoinformthelightingsystemaboutallthelittledepth-likedetailsofthesurface. Ifwethinkaboutthisfromalight'sperspective:howcomesthesurfaceislitasacompletelyflatsurface?Theansweristhesurface'snormalvector.Fromthelightingtechnique'spointofview,theonlywayitdeterminestheshapeofanobjectisbyitsperpendicularnormalvector.Thebricksurfaceonlyhasasinglenormalvector,andasaresultthesurfaceisuniformlylitbasedonthisnormalvector'sdirection.Whatifwe,insteadofaper-surfacenormalthatisthesameforeachfragment,useaper-fragmentnormalthatisdifferentforeachfragment?Thiswaywecanslightlydeviatethenormalvectorbasedonasurface'slittledetails;thisgivestheillusionthesurfaceisalotmorecomplex: Byusingper-fragmentnormalswecantrickthelightingintobelievingasurfaceconsistsoftinylittleplanes(perpendiculartothenormalvectors)givingthesurfaceanenormousboostindetail.Thistechniquetouseper-fragmentnormalscomparedtoper-surfacenormalsiscallednormalmappingorbumpmapping.Appliedtothebrickplaneitlooksabitlikethis: Asyoucansee,itgivesanenormousboostindetailandforarelativelylowcost.Sinceweonlychangethenormalvectorsperfragmentthereisnoneedtochangethelightingequation.Wenowpassaper-fragmentnormal,insteadofaninterpolatedsurfacenormal,tothelightingalgorithm.Thelightingthendoestherest. Normalmapping Togetnormalmappingtoworkwe'regoingtoneedaper-fragmentnormal.Similartowhatwedidwithdiffuseandspecularmapswecanusea2Dtexturetostoreper-fragmentnormaldata.Thiswaywecansamplea2Dtexturetogetanormalvectorforthatspecificfragment. Whilenormalvectorsaregeometricentitiesandtexturesaregenerallyonlyusedforcolorinformation,storingnormalvectorsinatexturemaynotbeimmediatelyobvious.Ifyouthinkaboutcolorvectorsinatexturetheyarerepresentedasa3Dvectorwithanr,g,andbcomponent.Wecansimilarlystoreanormalvector'sx,yandzcomponentintherespectivecolorcomponents.Normalvectorsrangebetween-1and1sothey'refirstmappedto[0,1]: vec3rgb_normal=normal*0.5+0.5;//transformsfrom[-1,1]to[0,1] WithnormalvectorstransformedtoanRGBcolorcomponentlikethis,wecanstoreaper-fragmentnormalderivedfromtheshapeofasurfaceontoa2Dtexture.Anexamplenormalmapofthebricksurfaceatthestartofthischapterisshownbelow: This(andalmostallnormalmapsyoufindonline)willhaveablue-ishtint.Thisisbecausethenormalsareallcloselypointingoutwardstowardsthepositivez-axis\((0,0,1)\):ablue-ishcolor.Thedeviationsincolorrepresentnormalvectorsthatareslightlyoffsetfromthegeneralpositivezdirection,givingasenseofdepthtothetexture.Forexample,youcanseethatatthetopofeachbrickthecolortendstobemoregreenish,whichmakessenseasthetopsideofabrickwouldhavenormalspointingmoreinthepositiveydirection\((0,1,0)\)whichhappenstobethecolorgreen! Withasimpleplane,lookingatthepositivez-axis,wecantakethisdiffusetextureandthisnormalmaptorendertheimagefromtheprevioussection.Notethatthelinkednormalmapisdifferentfromtheoneshownabove.ThereasonforthisisthatOpenGLreadstexturecoordinateswiththey(orv)coordinatereversedfromhowtexturesaregenerallycreated.Thelinkednormalmapthushasitsy(orgreen)componentinversed(youcanseethegreencolorsarenowpointingdownwards);ifyoufailtotakethisintoaccount,thelightingwillbeincorrect.Loadbothtextures,bindthemtothepropertextureunits,andrenderaplanewiththefollowingchangesinthelightingfragmentshader: uniformsampler2DnormalMap; voidmain() { //obtainnormalfromnormalmapinrange[0,1] normal=texture(normalMap,fs_in.TexCoords).rgb; //transformnormalvectortorange[-1,1] normal=normalize(normal*2.0-1.0); [...] //proceedwithlightingasnormal } HerewereversetheprocessofmappingnormalstoRGBcolorsbyremappingthesamplednormalcolorfrom[0,1]backto[-1,1]andthenusethesamplednormalvectorsfortheupcominglightingcalculations.InthiscaseweusedaBlinn-Phongshader. Byslowlymovingthelightsourceovertimeyoureallygetasenseofdepthusingthenormalmap.Runningthisnormalmappingexamplegivestheexactresultsasshownatthestartofthischapter: Thereisoneissuehoweverthatgreatlylimitsthisuseofnormalmaps.Thenormalmapweusedhadnormalvectorsthatallpointedsomewhatinthepositivezdirection.Thisworkedbecausetheplane'ssurfacenormalwasalsopointinginthepositivezdirection.However,whatwouldhappenifweusedthesamenormalmaponaplanelayingonthegroundwithasurfacenormalvectorpointinginthepositiveydirection? Thelightingdoesn'tlookright!Thishappensbecausethesamplednormalsofthisplanestillroughlypointinthepositivezdirectioneventhoughtheyshouldmostlypointinthepositiveydirection.Asaresult,thelightingthinksthesurface'snormalsarethesameasbeforewhentheplanewaspointingtowardsthepositivezdirection;thelightingisincorrect.Theimagebelowshowswhatthesamplednormalsapproximatelylooklikeonthissurface: Youcanseethatallthenormalspointsomewhatinthepositivezdirectioneventhoughtheyshouldbepointingtowardsthepositiveydirection.Onesolutiontothisproblemistodefineanormalmapforeachpossibledirectionofthesurface;inthecaseofacubewewouldneed6normalmaps.However,withadvancedmeshesthatcanhavemorethanhundredsofpossiblesurfacedirectionsthisbecomesaninfeasibleapproach. Adifferentsolutionexiststhatdoesallthelightinginadifferentcoordinatespace:acoordinatespacewherethenormalmapvectorsalwayspointtowardsthepositivezdirection;allotherlightingvectorsarethentransformedrelativetothispositivezdirection.Thiswaywecanalwaysusethesamenormalmap,regardlessoforientation.Thiscoordinatespaceiscalledtangentspace. Tangentspace Normalvectorsinanormalmapareexpressedintangentspacewherenormalsalwayspointroughlyinthepositivezdirection.Tangentspaceisaspacethat'slocaltothesurfaceofatriangle:thenormalsarerelativetothelocalreferenceframeoftheindividualtriangles.Thinkofitasthelocalspaceofthenormalmap'svectors;they'realldefinedpointinginthepositivezdirectionregardlessofthefinaltransformeddirection.Usingaspecificmatrixwecanthentransformnormalvectorsfromthislocaltangentspacetoworldorviewcoordinates,orientingthemalongthefinalmappedsurface'sdirection. Let'ssaywehavetheincorrectnormalmappedsurfacefromtheprevioussectionlookinginthepositiveydirection.Thenormalmapisdefinedintangentspace,soonewaytosolvetheproblemistocalculateamatrixtotransformnormalsfromtangentspacetoadifferentspacesuchthatthey'realignedwiththesurface'snormaldirection:thenormalvectorsarethenallpointingroughlyinthepositiveydirection.Thegreatthingabouttangentspaceisthatwecancalculatethismatrixforanytypeofsurfacesothatwecanproperlyalignthetangentspace'szdirectiontothesurface'snormaldirection. SuchamatrixiscalledaTBNmatrixwherethelettersdepictaTangent,BitangentandNormalvector.Thesearethevectorsweneedtoconstructthismatrix.Toconstructsuchachange-of-basismatrix,thattransformsatangent-spacevectortoadifferentcoordinatespace,weneedthreeperpendicularvectorsthatarealignedalongthesurfaceofanormalmap:anup,right,andforwardvector;similartowhatwedidinthecamerachapter. Wealreadyknowtheupvector,whichisthesurface'snormalvector.Therightandforwardvectorarethetangentandbitangentvectorrespectively.Thefollowingimageofasurfaceshowsallthreevectorsonasurface: Calculatingthetangentandbitangentvectorsisnotasstraightforwardasthenormalvector.Wecanseefromtheimagethatthedirectionofthenormalmap'stangentandbitangentvectoralignwiththedirectioninwhichwedefineasurface'stexturecoordinates.We'llusethisfacttocalculatetangentandbitangentvectorsforeachsurface.Retrievingthemdoesrequireabitofmath;takealookatthefollowingimage: Fromtheimagewecanseethatthetexturecoordinatedifferencesofanedge\(E_2\)ofatriangle(denotedas\(\DeltaU_2\)and\(\DeltaV_2\))areexpressedinthesamedirectionasthetangentvector\(T\)andbitangentvector\(B\).Becauseofthiswecanwritebothdisplayededges\(E_1\)and\(E_2\)ofthetriangleasalinearcombinationofthetangentvector\(T\)andthebitangentvector\(B\): \[E_1=\DeltaU_1T+\DeltaV_1B\] \[E_2=\DeltaU_2T+\DeltaV_2B\] Whichwecanalsowriteas: \[(E_{1x},E_{1y},E_{1z})=\DeltaU_1(T_x,T_y,T_z)+\DeltaV_1(B_x,B_y,B_z)\] \[(E_{2x},E_{2y},E_{2z})=\DeltaU_2(T_x,T_y,T_z)+\DeltaV_2(B_x,B_y,B_z)\] Wecancalculate\(E\)asthedifferencevectorbetweentwotrianglepositions,and\(\DeltaU\)and\(\DeltaV\)astheirtexturecoordinatedifferences.We'rethenleftwithtwounknowns(tangent\(T\)andbitangent\(B\))andtwoequations.Youmayrememberfromyouralgebraclassesthatthisallowsustosolvefor\(T\)and\(B\). Thelastequationallowsustowriteitinadifferentform:thatofmatrixmultiplication: \[\begin{bmatrix}E_{1x}&E_{1y}&E_{1z}\\E_{2x}&E_{2y}&E_{2z}\end{bmatrix}=\begin{bmatrix}\DeltaU_1&\DeltaV_1\\\DeltaU_2&\DeltaV_2\end{bmatrix}\begin{bmatrix}T_x&T_y&T_z\\B_x&B_y&B_z\end{bmatrix}\] Trytovisualizethematrixmultiplicationsinyourheadandconfirmthatthisisindeedthesameequation.Anadvantageofrewritingtheequationsinmatrixformisthatsolvingfor\(T\)and\(B\)iseasiertounderstand.Ifwemultiplybothsidesoftheequationsbytheinverseofthe\(\DeltaU\DeltaV\)matrixweget: \[\begin{bmatrix}\DeltaU_1&\DeltaV_1\\\DeltaU_2&\DeltaV_2\end{bmatrix}^{-1}\begin{bmatrix}E_{1x}&E_{1y}&E_{1z}\\E_{2x}&E_{2y}&E_{2z}\end{bmatrix}=\begin{bmatrix}T_x&T_y&T_z\\B_x&B_y&B_z\end{bmatrix}\] Thisallowsustosolvefor\(T\)and\(B\).Thisdoesrequireustocalculatetheinverseofthedeltatexturecoordinatematrix.Iwon'tgointothemathematicaldetailsofcalculatingamatrix'inverse,butitroughlytranslatesto1overthedeterminantofthematrix,multipliedbyitsadjugatematrix: \[\begin{bmatrix}T_x&T_y&T_z\\B_x&B_y&B_z\end{bmatrix}=\frac{1}{\DeltaU_1\DeltaV_2-\DeltaU_2\DeltaV_1}\begin{bmatrix}\DeltaV_2&-\DeltaV_1\\-\DeltaU_2&\DeltaU_1\end{bmatrix}\begin{bmatrix}E_{1x}&E_{1y}&E_{1z}\\E_{2x}&E_{2y}&E_{2z}\end{bmatrix}\] Thisfinalequationgivesusaformulaforcalculatingthetangentvector\(T\)andbitangentvector\(B\)fromatriangle'stwoedgesanditstexturecoordinates. Don'tworryifyoudonotfullyunderstandthemathematicsbehindthis.Aslongasyouunderstandthatwecancalculatetangentsandbitangentsfromatriangle'sverticesanditstexturecoordinates(sincetexturecoordinatesareinthesamespaceastangentvectors)you'rehalfwaythere. Manualcalculationoftangentsandbitangents Inthepreviousdemowehadasimplenormalmappedplanefacingthepositivezdirection.Thistimewewanttoimplementnormalmappingusingtangentspacesowecanorientthisplanehoweverwewantandnormalmappingwouldstillwork.Usingthepreviouslydiscussedmathematicswe'regoingtomanuallycalculatethissurface'stangentandbitangentvectors. Let'sassumetheplaneisbuiltupfromthefollowingvectors(with1,2,3and1,3,4asitstwotriangles): //positions glm::vec3pos1(-1.0,1.0,0.0); glm::vec3pos2(-1.0,-1.0,0.0); glm::vec3pos3(1.0,-1.0,0.0); glm::vec3pos4(1.0,1.0,0.0); //texturecoordinates glm::vec2uv1(0.0,1.0); glm::vec2uv2(0.0,0.0); glm::vec2uv3(1.0,0.0); glm::vec2uv4(1.0,1.0); //normalvector glm::vec3nm(0.0,0.0,1.0); Wefirstcalculatethefirsttriangle'sedgesanddeltaUVcoordinates: glm::vec3edge1=pos2-pos1; glm::vec3edge2=pos3-pos1; glm::vec2deltaUV1=uv2-uv1; glm::vec2deltaUV2=uv3-uv1; Withtherequireddataforcalculatingtangentsandbitangentswecanstartfollowingtheequationfromtheprevioussection: floatf=1.0f/(deltaUV1.x*deltaUV2.y-deltaUV2.x*deltaUV1.y); tangent1.x=f*(deltaUV2.y*edge1.x-deltaUV1.y*edge2.x); tangent1.y=f*(deltaUV2.y*edge1.y-deltaUV1.y*edge2.y); tangent1.z=f*(deltaUV2.y*edge1.z-deltaUV1.y*edge2.z); bitangent1.x=f*(-deltaUV2.x*edge1.x+deltaUV1.x*edge2.x); bitangent1.y=f*(-deltaUV2.x*edge1.y+deltaUV1.x*edge2.y); bitangent1.z=f*(-deltaUV2.x*edge1.z+deltaUV1.x*edge2.z); [...]//similarprocedureforcalculatingtangent/bitangentforplane'ssecondtriangle Herewefirstpre-calculatethefractionalpartoftheequationasfandthenforeachvectorcomponentwedothecorrespondingmatrixmultiplicationmultipliedbyf.Ifyoucomparethiscodewiththefinalequationyoucanseeitisadirecttranslation.Becauseatriangleisalwaysaflatshape,weonlyneedtocalculateasingletangent/bitangentpairpertriangleastheywillbethesameforeachofthetriangle'svertices. Theresultingtangentandbitangentvectorshouldhaveavalueof(1,0,0)and(0,1,0)respectivelythattogetherwiththenormal(0,0,1)formsanorthogonalTBNmatrix.Visualizedontheplane,theTBNvectorswouldlooklikethis: Withtangentandbitangentvectorsdefinedpervertexwecanstartimplementingpropernormalmapping. Tangentspacenormalmapping Togetnormalmappingworking,wefirsthavetocreateaTBNmatrixintheshaders.Todothat,wepasstheearliercalculatedtangentandbitangentvectorstothevertexshaderasvertexattributes: #version330core layout(location=0)invec3aPos; layout(location=1)invec3aNormal; layout(location=2)invec2aTexCoords; layout(location=3)invec3aTangent; layout(location=4)invec3aBitangent; Thenwithinthevertexshader'smainfunctionwecreatetheTBNmatrix: voidmain() { [...] vec3T=normalize(vec3(model*vec4(aTangent,0.0))); vec3B=normalize(vec3(model*vec4(aBitangent,0.0))); vec3N=normalize(vec3(model*vec4(aNormal,0.0))); mat3TBN=mat3(T,B,N); } HerewefirsttransformalltheTBNvectorstothecoordinatesystemwe'dliketoworkin,whichinthiscaseisworld-spaceaswemultiplythemwiththemodelmatrix.ThenwecreatetheactualTBNmatrixbydirectlysupplyingmat3'sconstructorwiththerelevantcolumnvectors.Notethatifwewanttobereallyprecise,wewouldmultiplytheTBNvectorswiththenormalmatrixasweonlycareabouttheorientationofthevectors. Technicallythereisnoneedforthebitangentvariableinthevertexshader.AllthreeTBNvectorsareperpendiculartoeachothersowecancalculatethebitangentourselvesinthevertexshaderbytakingthecrossproductoftheTandNvector:vec3B=cross(N,T); SonowthatwehaveaTBNmatrix,howarewegoingtouseit?TherearetwowayswecanuseaTBNmatrixfornormalmapping,andwe'lldemonstratebothofthem: WetaketheTBNmatrixthattransformsanyvectorfromtangenttoworldspace,giveittothefragmentshader,andtransformthesamplednormalfromtangentspacetoworldspaceusingtheTBNmatrix;thenormalistheninthesamespaceastheotherlightingvariables. WetaketheinverseoftheTBNmatrixthattransformsanyvectorfromworldspacetotangentspace,andusethismatrixtotransformnotthenormal,buttheotherrelevantlightingvariablestotangentspace;thenormalisthenagaininthesamespaceastheotherlightingvariables. Let'sreviewthefirstcase.Thenormalvectorwesamplefromthenormalmapisexpressedintangentspacewhereastheotherlightingvectors(lightandviewdirection)areexpressedinworldspace.BypassingtheTBNmatrixtothefragmentshaderwecanmultiplythesampledtangentspacenormalwiththisTBNmatrixtotransformthenormalvectortothesamereferencespaceastheotherlightingvectors.Thisway,allthelightingcalculations(specificallythedotproduct)makesense. SendingtheTBNmatrixtothefragmentshaderiseasy: outVS_OUT{ vec3FragPos; vec2TexCoords; mat3TBN; }vs_out; voidmain() { [...] vs_out.TBN=mat3(T,B,N); } Inthefragmentshaderwesimilarlytakeamat3asaninputvariable: inVS_OUT{ vec3FragPos; vec2TexCoords; mat3TBN; }fs_in; WiththisTBNmatrixwecannowupdatethenormalmappingcodetoincludethetangent-to-worldspacetransformation: normal=texture(normalMap,fs_in.TexCoords).rgb; normal=normal*2.0-1.0; normal=normalize(fs_in.TBN*normal); Becausetheresultingnormalisnowinworldspace,thereisnoneedtochangeanyoftheotherfragmentshadercodeasthelightingcodeassumesthenormalvectortobeinworldspace. Let'salsoreviewthesecondcase,wherewetaketheinverseoftheTBNmatrixtotransformallrelevantworld-spacevectorstothespacethesamplednormalvectorsarein:tangentspace.TheconstructionoftheTBNmatrixremainsthesame,butwefirstinvertthematrixbeforesendingittothefragmentshader: vs_out.TBN=transpose(mat3(T,B,N)); Notethatweusethetransposefunctioninsteadoftheinversefunctionhere.Agreatpropertyoforthogonalmatrices(eachaxisisaperpendicularunitvector)isthatthetransposeofanorthogonalmatrixequalsitsinverse.Thisisagreatpropertyasinverseisexpensiveandatransposeisn't. Withinthefragmentshaderwedonottransformthenormalvector,butwetransformtheotherrelevantvectorstotangentspace,namelythelightDirandviewDirvectors.Thatway,eachvectorisinthesamecoordinatespace:tangentspace. voidmain() { vec3normal=texture(normalMap,fs_in.TexCoords).rgb; normal=normalize(normal*2.0-1.0); vec3lightDir=fs_in.TBN*normalize(lightPos-fs_in.FragPos); vec3viewDir=fs_in.TBN*normalize(viewPos-fs_in.FragPos); [...] } Thesecondapproachlookslikemoreworkandalsorequiresmatrixmultiplicationsinthefragmentshader,sowhywouldwebotherwiththesecondapproach? Well,transformingvectorsfromworldtotangentspacehasanaddedadvantageinthatwecantransformalltherelevantlightingvectorstotangentspaceinthevertexshaderinsteadofinthefragmentshader.Thisworks,becauselightPosandviewPosdon'tupdateeveryfragmentrun,andforfs_in.FragPoswecancalculateitstangent-spacepositioninthevertexshaderandletfragmentinterpolationdoitswork.Thereiseffectivelynoneedtotransformavectortotangentspaceinthefragmentshader,whileitisnecessarywiththefirstapproachassamplednormalvectorsarespecifictoeachfragmentshaderrun. SoinsteadofsendingtheinverseoftheTBNmatrixtothefragmentshader,wesendatangent-spacelightposition,viewposition,andvertexpositiontothefragmentshader.Thissavesusfromhavingtodomatrixmultiplicationsinthefragmentshader.Thisisaniceoptimizationasthevertexshaderrunsconsiderablylessoftenthanthefragmentshader.Thisisalsothereasonwhythisapproachisoftenthepreferredapproach. outVS_OUT{ vec3FragPos; vec2TexCoords; vec3TangentLightPos; vec3TangentViewPos; vec3TangentFragPos; }vs_out; uniformvec3lightPos; uniformvec3viewPos; [...] voidmain() { [...] mat3TBN=transpose(mat3(T,B,N)); vs_out.TangentLightPos=TBN*lightPos; vs_out.TangentViewPos=TBN*viewPos; vs_out.TangentFragPos=TBN*vec3(model*vec4(aPos,1.0)); } Inthefragmentshaderwethenusethesenewinputvariablestocalculatelightingintangentspace.Asthenormalvectorisalreadyintangentspace,thelightingmakessense. Withnormalmappingappliedintangentspace,weshouldgetsimilarresultstowhatwehadatthestartofthischapter.Thistimehowever,wecanorientourplaneinanywaywe'dlikeandthelightingwouldstillbecorrect: glm::mat4model=glm::mat4(1.0f); model=glm::rotate(model,(float)glfwGetTime()*-10.0f,glm::normalize(glm::vec3(1.0,0.0,1.0))); shader.setMat4("model",model); RenderQuad(); Whichindeedlookslikepropernormalmapping: Youcanfindthesourcecodehere. Complexobjects We'vedemonstratedhowwecanusenormalmapping,togetherwithtangentspacetransformations,bymanuallycalculatingthetangentandbitangentvectors.Luckilyforus,havingtomanuallycalculatethesetangentandbitangentvectorsisnotsomethingwedotoooften.Mostofthetimeyouimplementitonceinacustommodelloader,orinourcaseuseamodelloaderusingAssimp. AssimphasaveryusefulconfigurationbitwecansetwhenloadingamodelcalledaiProcess_CalcTangentSpace.WhentheaiProcess_CalcTangentSpacebitissuppliedtoAssimp'sReadFilefunction,Assimpcalculatessmoothtangentandbitangentvectorsforeachoftheloadedvertices,similarlytohowwediditinthischapter. constaiScene*scene=importer.ReadFile( path,aiProcess_Triangulate|aiProcess_FlipUVs|aiProcess_CalcTangentSpace ); WithinAssimpwecanthenretrievethecalculatedtangentsvia: vector.x=mesh->mTangents[i].x; vector.y=mesh->mTangents[i].y; vector.z=mesh->mTangents[i].z; vertex.Tangent=vector; Thenyou'llhavetoupdatethemodelloadertoalsoloadnormalmapsfromatexturedmodel.Thewavefrontobjectformat(.obj)exportsnormalmapsslightlydifferentfromAssimp'sconventionsasaiTextureType_NORMALdoesn'tloadnormalmaps,whileaiTextureType_HEIGHTdoes: vectornormalMaps=loadMaterialTextures(material,aiTextureType_HEIGHT,"texture_normal"); Ofcourse,thisisdifferentforeachtypeofloadedmodelandfileformat. Runningtheapplicationonamodelwithspecularandnormalmaps,usinganupdatedmodelloader,givesthefollowingresult: Asyoucansee,normalmappingbooststhedetailofanobjectbyanincredibleamountwithouttoomuchextracost. Usingnormalmapsisalsoagreatwaytoboostperformance.Beforenormalmapping,youhadtousealargenumberofverticestogetahighnumberofdetailonamesh.Withnormalmapping,wecangetthesamelevelofdetailonameshusingalotlessvertices.TheimagebelowfromPaoloCignonishowsanicecomparisonofbothmethods: Thedetailsonboththehigh-vertexmeshandthelow-vertexmeshwithnormalmappingarealmostindistinguishable.Sonormalmappingdoesn'tonlylooknice,it'sagreattooltoreplacehigh-vertexmesheswithlow-vertexmesheswithoutlosing(toomuch)detail. Onelastthing Thereisonelasttricklefttodiscussthatslightlyimprovesqualitywithouttoomuchextracost. Whentangentvectorsarecalculatedonlargermeshesthatshareaconsiderableamountofvertices,thetangentvectorsaregenerallyaveragedtogiveniceandsmoothresults.AproblemwiththisapproachisthatthethreeTBNvectorscouldendupnon-perpendicular,whichmeanstheresultingTBNmatrixwouldnolongerbeorthogonal.Normalmappingwouldonlybeslightlyoffwithanon-orthogonalTBNmatrix,butit'sstillsomethingwecanimprove. UsingamathematicaltrickcalledtheGram-Schmidtprocess,wecanre-orthogonalizetheTBNvectorssuchthateachvectorisagainperpendiculartotheothervectors.Withinthevertexshaderwewoulddoitlikethis: vec3T=normalize(vec3(model*vec4(aTangent,0.0))); vec3N=normalize(vec3(model*vec4(aNormal,0.0))); //re-orthogonalizeTwithrespecttoN T=normalize(T-dot(T,N)*N); //thenretrieveperpendicularvectorBwiththecrossproductofTandN vec3B=cross(N,T); mat3TBN=mat3(T,B,N) This,albeitbyalittle,generallyimprovesthenormalmappingresultswithalittleextracost.TakealookattheendoftheNormalMappingMathematicsvideointheadditionalresourcesforagreatexplanationofhowthisprocessactuallyworks. Additionalresources Tutorial26:NormalMapping:normalmappingtutorialbyogldev. HowNormalMappingWorks:anicevideotutorialofhownormalmappingworksbyTheBennyBox. NormalMappingMathematics:asimilarvideobyTheBennyBoxaboutthemathematicsbehindnormalmapping. Tutorial13:NormalMapping:normalmappingtutorialbyopengl-tutorial.org. HI



請為這篇文章評分?