Normal Mapping - LearnOpenGL
文章推薦指數: 80 %
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:
vector
延伸文章資訊
- 1Tutorial 13 : Normal Mapping
The basic idea of normal mapping is to give normals similar variations. Normal textures. A “norma...
- 2Normal Mapping - LearnOpenGL
The normal map is defined in tangent space, so one way to solve the problem is to calculate a mat...
- 3Implementing Normal Mapping using OpenGL/GLSL - Stack ...
That normal map is in tangent-space, but you are treating it as object-space. You need a bitangen...
- 4OpenGL 2.1 - GLSL normal mapping - 3D C/C++ tutorials
The normal map used in this tutorial is a Direct3D normal map and before it can be mapped on tria...
- 5第十三课:法线贴图
镜面纹理(Specular texture); 用立即模式(immediate mode)进行调试; 利用颜色进行调试; 利用变量名进行调试 ... 今天的内容是法线贴图(normal mapp...