Geometry Shader - LearnOpenGL

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

This input layout qualifier can take any of the following primitive values: points : when drawing GL_POINTS primitives ( 1 ). lines : when drawing GL_LINES or ... Ifyou'rerunningAdBlock,pleaseconsiderwhitelistingthissiteifyou'dliketosupportLearnOpenGL;andnoworries,Iwon'tbemadifyoudon't:) IntroductionGettingstartedOpenGLCreatingawindowHelloWindowHelloTriangleShadersTexturesTransformationsCoordinateSystemsCameraReviewLightingColorsBasicLightingMaterialsLightingmapsLightcastersMultiplelightsReviewModelLoadingAssimpMeshModelAdvancedOpenGLDepthtestingStenciltestingBlendingFacecullingFramebuffersCubemapsAdvancedDataAdvancedGLSLGeometryShaderInstancingAntiAliasingAdvancedLightingAdvancedLightingGammaCorrectionShadowsShadowMappingPointShadowsNormalMappingParallaxMappingHDRBloomDeferredShadingSSAOPBRTheoryLightingIBLDiffuseirradianceSpecularIBLInPracticeDebuggingTextRendering2DGameBreakoutSettingupRenderingSpritesLevelsCollisionsBallCollisiondetectionCollisionresolutionParticlesPostprocessingPowerupsAudioRendertextFinalthoughtsGuestArticlesHowtopublish2020OITIntroductionWeightedBlendedSkeletalAnimation2021CSMSceneSceneGraphFrustumCullingTessellationHeightmapTessellationDSA2022ComputeShadersIntroductionPhys.BasedBloomCoderepositoryTranslationsAbout BTC 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa ETH/ERC20 0x1de59bd9e52521a46309474f8372531533bd7c43 GeometryShader Advanced-OpenGL/Geometry-Shader Betweenthevertexandthefragmentshaderthereisanoptionalshaderstagecalledthegeometryshader.Ageometryshadertakesasinputasetofverticesthatformasingleprimitivee.g.apointoratriangle.Thegeometryshadercanthentransformtheseverticesasitseesfitbeforesendingthemtothenextshaderstage.Whatmakesthegeometryshaderinterestingisthatitisabletoconverttheoriginalprimitive(setofvertices)tocompletelydifferentprimitives,possiblygeneratingmoreverticesthanwereinitiallygiven. We'regoingtothrowyourightintothedeepbyshowingyouanexampleofageometryshader: #version330core layout(points)in; layout(line_strip,max_vertices=2)out; voidmain(){ gl_Position=gl_in[0].gl_Position+vec4(-0.1,0.0,0.0,0.0); EmitVertex(); gl_Position=gl_in[0].gl_Position+vec4(0.1,0.0,0.0,0.0); EmitVertex(); EndPrimitive(); } Atthestartofageometryshaderweneedtodeclarethetypeofprimitiveinputwe'rereceivingfromthevertexshader.Wedothisbydeclaringalayoutspecifierinfrontoftheinkeyword.Thisinputlayoutqualifiercantakeanyofthefollowingprimitivevalues: points:whendrawingGL_POINTSprimitives(1). lines:whendrawingGL_LINESorGL_LINE_STRIP(2). lines_adjacency:GL_LINES_ADJACENCYorGL_LINE_STRIP_ADJACENCY(4). triangles:GL_TRIANGLES,GL_TRIANGLE_STRIPorGL_TRIANGLE_FAN(3). triangles_adjacency:GL_TRIANGLES_ADJACENCYorGL_TRIANGLE_STRIP_ADJACENCY(6). Thesearealmostalltherenderingprimitiveswe'reabletogivetorenderingcallslikeglDrawArrays.Ifwe'dchosentodrawverticesasGL_TRIANGLESweshouldsettheinputqualifiertotriangles.Thenumberwithintheparenthesisrepresentstheminimalnumberofverticesasingleprimitivecontains. Wealsoneedtospecifyaprimitivetypethatthegeometryshaderwilloutputandwedothisviaalayoutspecifierinfrontoftheoutkeyword.Liketheinputlayoutqualifier,theoutputlayoutqualifiercantakeseveralprimitivevalues: points line_strip triangle_strip Withjustthese3outputspecifierswecancreatealmostanyshapewewantfromtheinputprimitives.Togenerateasingletriangleforexamplewe'dspecifytriangle_stripastheoutputandoutput3vertices. Thegeometryshaderalsoexpectsustosetamaximumnumberofverticesitoutputs(ifyouexceedthisnumber,OpenGLwon'tdrawtheextravertices)whichwecanalsodowithinthelayoutqualifieroftheoutkeyword.Inthisparticularcasewe'regoingtooutputaline_stripwithamaximumnumberof2vertices. Incaseyou'rewonderingwhatalinestripis:alinestripbindstogetherasetofpointstoformonecontinuouslinebetweenthemwithaminimumof2points.Eachextrapointresultsinanewlinebetweenthenewpointandthepreviouspointasyoucanseeinthefollowingimagewith5pointvertices: Togeneratemeaningfulresultsweneedsomewaytoretrievetheoutputfromthepreviousshaderstage.GLSLgivesusabuilt-invariablecalledgl_inthatinternally(probably)lookssomethinglikethis: ingl_Vertex { vec4gl_Position; floatgl_PointSize; floatgl_ClipDistance[]; }gl_in[]; Hereitisdeclaredasaninterfaceblock(asdiscussedinthepreviouschapter)thatcontainsafewinterestingvariablesofwhichthemostinterestingoneisgl_Positionthatcontainsthevectorwesetasthevertexshader'soutput. Notethatitisdeclaredasanarray,becausemostrenderprimitivescontainmorethan1vertex.Thegeometryshaderreceivesallverticesofaprimitiveasitsinput. Usingthevertexdatafromthevertexshaderstagewecangeneratenewdatawith2geometryshaderfunctionscalledEmitVertexandEndPrimitive.Thegeometryshaderexpectsyoutogenerate/outputatleastoneoftheprimitivesyouspecifiedasoutput.Inourcasewewanttoatleastgenerateonelinestripprimitive. #version330core layout(points)in; layout(line_strip,max_vertices=2)out; voidmain(){ gl_Position=gl_in[0].gl_Position+vec4(-0.1,0.0,0.0,0.0); EmitVertex(); gl_Position=gl_in[0].gl_Position+vec4(0.1,0.0,0.0,0.0); EmitVertex(); EndPrimitive(); } EachtimewecallEmitVertex,thevectorcurrentlysettogl_Positionisaddedtotheoutputprimitive.WheneverEndPrimitiveiscalled,allemittedverticesforthisprimitivearecombinedintothespecifiedoutputrenderprimitive.ByrepeatedlycallingEndPrimitive,afteroneormoreEmitVertexcalls,multipleprimitivescanbegenerated.ThisparticularcaseemitstwoverticesthatweretranslatedbyasmalloffsetfromtheoriginalvertexpositionandthencallsEndPrimitive,combiningthetwoverticesintoasinglelinestripof2vertices. Nowthatyou(sortof)knowhowgeometryshadersworkyoucanprobablyguesswhatthisgeometryshaderdoes.Thisgeometryshadertakesapointprimitiveasitsinputandcreatesahorizontallineprimitivewiththeinputpointatitscenter.Ifweweretorenderthisitlookssomethinglikethis: Notveryimpressiveyet,butit'sinterestingtoconsiderthatthisoutputwasgeneratedusingjustthefollowingrendercall: glDrawArrays(GL_POINTS,0,4); Whilethisisarelativelysimpleexample,itdoesshowyouhowwecanusegeometryshadersto(dynamically)generatenewshapesonthefly.Laterinthischapterwe'lldiscussafewinterestingeffectsthatwecancreateusinggeometryshaders,butfornowwe'regoingtostartwithasimpleexample. Usinggeometryshaders Todemonstratetheuseofageometryshaderwe'regoingtorenderareallysimplescenewherewedraw4pointsonthez-planeinnormalizeddevicecoordinates.Thecoordinatesofthepointsare: floatpoints[]={ -0.5f,0.5f,//top-left 0.5f,0.5f,//top-right 0.5f,-0.5f,//bottom-right -0.5f,-0.5f//bottom-left }; Thevertexshaderneedstodrawthepointsonthez-planesowe'llcreateabasicvertexshader: #version330core layout(location=0)invec2aPos; voidmain() { gl_Position=vec4(aPos.x,aPos.y,0.0,1.0); } Andwe'lloutputthecolorgreenforallpointswhichwecodedirectlyinthefragmentshader: #version330core outvec4FragColor; voidmain() { FragColor=vec4(0.0,1.0,0.0,1.0); } GenerateaVAOandaVBOforthepoints'vertexdataandthendrawthemviaglDrawArrays: shader.use(); glBindVertexArray(VAO); glDrawArrays(GL_POINTS,0,4); Theresultisadarkscenewith4(difficulttosee)greenpoints: Butdidn'twealreadylearntodoallthis?Yes,andnowwe'regoingtospicethislittlesceneupbyaddinggeometryshadermagictothescene. Forlearningpurposeswe'refirstgoingtocreatewhatiscalledapass-throughgeometryshaderthattakesapointprimitiveasitsinputandpassesittothenextshaderunmodified: #version330core layout(points)in; layout(points,max_vertices=1)out; voidmain(){ gl_Position=gl_in[0].gl_Position; EmitVertex(); EndPrimitive(); } Bynowthisgeometryshadershouldbefairlyeasytounderstand.Itsimplyemitstheunmodifiedvertexpositionitreceivedasinputandgeneratesapointprimitive. Ageometryshaderneedstobecompiledandlinkedtoaprogramjustlikethevertexandfragmentshader,butthistimewe'llcreatetheshaderusingGL_GEOMETRY_SHADERastheshadertype: geometryShader=glCreateShader(GL_GEOMETRY_SHADER); glShaderSource(geometryShader,1,&gShaderCode,NULL); glCompileShader(geometryShader); [...] glAttachShader(program,geometryShader); glLinkProgram(program); Theshadercompilationcodeisthesameasthevertexandfragmentshaders.Besuretocheckforcompileorlinkingerrors! Ifyou'dnowcompileandrunyoushouldbelookingataresultthatlooksabitlikethis: It'sexactlythesameaswithoutthegeometryshader!It'sabitdull,I'lladmitthat,butthefactthatwewerestillabletodrawthepointsmeansthatthegeometryshaderworks,sonowit'stimeforthemorefunkystuff! Let'sbuildhouses Drawingpointsandlinesisn'tthatinterestingsowe'regoingtogetalittlecreativebyusingthegeometryshadertodrawahouseforusatthelocationofeachpoint.Wecanaccomplishthisbysettingtheoutputofthegeometryshadertotriangle_stripanddrawatotalofthreetriangles:twoforthesquarehouseandonefortheroof. AtrianglestripinOpenGLisamoreefficientwaytodrawtriangleswithfewervertices.Afterthefirsttriangleisdrawn,eachsubsequentvertexgeneratesanothertrianglenexttothefirsttriangle:every3adjacentverticeswillformatriangle.Ifwehaveatotalof6verticesthatformatrianglestripwe'dgetthefollowingtriangles:(1,2,3),(2,3,4),(3,4,5)and(4,5,6);formingatotalof4triangles.Atrianglestripneedsatleast3verticesandwillgenerateN-2triangles;with6verticeswecreated6-2=4triangles.Thefollowingimageillustratesthis: Usingatrianglestripastheoutputofthegeometryshaderwecaneasilycreatethehouseshapewe'reafterbygenerating3adjacenttrianglesinthecorrectorder.Thefollowingimageshowsinwhatorderweneedtodrawwhatverticestogetthetrianglesweneedwiththebluedotbeingtheinputpoint: Thistranslatestothefollowinggeometryshader: #version330core layout(points)in; layout(triangle_strip,max_vertices=5)out; voidbuild_house(vec4position) { gl_Position=position+vec4(-0.2,-0.2,0.0,0.0);//1:bottom-left EmitVertex(); gl_Position=position+vec4(0.2,-0.2,0.0,0.0);//2:bottom-right EmitVertex(); gl_Position=position+vec4(-0.2,0.2,0.0,0.0);//3:top-left EmitVertex(); gl_Position=position+vec4(0.2,0.2,0.0,0.0);//4:top-right EmitVertex(); gl_Position=position+vec4(0.0,0.4,0.0,0.0);//5:top EmitVertex(); EndPrimitive(); } voidmain(){ build_house(gl_in[0].gl_Position); } Thisgeometryshadergenerates5vertices,witheachvertexbeingthepoint'spositionplusanoffsettoformonelargetrianglestrip.Theresultingprimitiveisthenrasterizedandthefragmentshaderrunsontheentiretrianglestrip,resultinginagreenhouseforeachpointwe'verendered: Youcanseethateachhouseindeedconsistsof3triangles-alldrawnusingasinglepointinspace.Thegreenhousesdolookabitboringthough,solet'slivenitupabitbygivingeachhouseauniquecolor.Todothiswe'regoingtoaddanextravertexattributeinthevertexshaderwithcolorinformationpervertexanddirectittothegeometryshaderthatfurtherforwardsittothefragmentshader. Theupdatedvertexdataisgivenbelow: floatpoints[]={ -0.5f,0.5f,1.0f,0.0f,0.0f,//top-left 0.5f,0.5f,0.0f,1.0f,0.0f,//top-right 0.5f,-0.5f,0.0f,0.0f,1.0f,//bottom-right -0.5f,-0.5f,1.0f,1.0f,0.0f//bottom-left }; Thenweupdatethevertexshadertoforwardthecolorattributetothegeometryshaderusinganinterfaceblock: #version330core layout(location=0)invec2aPos; layout(location=1)invec3aColor; outVS_OUT{ vec3color; }vs_out; voidmain() { gl_Position=vec4(aPos.x,aPos.y,0.0,1.0); vs_out.color=aColor; } Thenwealsoneedtodeclarethesameinterfaceblock(withadifferentinterfacename)inthegeometryshader: inVS_OUT{ vec3color; }gs_in[]; Becausethegeometryshaderactsonasetofverticesasitsinput,itsinputdatafromthevertexshaderisalwaysrepresentedasarraysofvertexdataeventhoughweonlyhaveasinglevertexrightnow. Wedon'tnecessarilyhavetouseinterfaceblockstotransferdatatothegeometryshader.Wecouldhavealsowrittenitas: invec3outColor[]; Thisworksifthevertexshaderforwardedthecolorvectorasoutvec3outColor.However,interfaceblocksareeasiertoworkwithinshaderslikethegeometryshader.Inpractice,geometryshaderinputscangetquitelargeandgroupingtheminonelargeinterfaceblockarraymakesalotmoresense. Weshouldalsodeclareanoutputcolorvectorforthenextfragmentshaderstage: outvec3fColor; Becausethefragmentshaderexpectsonlyasingle(interpolated)coloritdoesn'tmakesensetoforwardmultiplecolors.ThefColorvectoristhusnotanarray,butasinglevector.Whenemittingavertex,thatvertexwillstorethelaststoredvalueinfColorasthatvertex'soutputvalue.Forthehouses,wecanfillfColoroncewiththecolorfromthevertexshaderbeforethefirstvertexisemittedtocolortheentirehouse: fColor=gs_in[0].color;//gs_in[0]sincethere'sonlyoneinputvertex gl_Position=position+vec4(-0.2,-0.2,0.0,0.0);//1:bottom-left EmitVertex(); gl_Position=position+vec4(0.2,-0.2,0.0,0.0);//2:bottom-right EmitVertex(); gl_Position=position+vec4(-0.2,0.2,0.0,0.0);//3:top-left EmitVertex(); gl_Position=position+vec4(0.2,0.2,0.0,0.0);//4:top-right EmitVertex(); gl_Position=position+vec4(0.0,0.4,0.0,0.0);//5:top EmitVertex(); EndPrimitive(); AlltheemittedverticeswillhavethelaststoredvalueinfColorembeddedintotheirdata,whichisequaltotheinputvertex'scoloraswedefinedinitsattributes.Allthehouseswillnowhaveacoloroftheirown: Justforfunwecouldalsopretendit'swinterandgivetheirroofsalittlesnowbygivingthelastvertexacolorofitsown: fColor=gs_in[0].color; gl_Position=position+vec4(-0.2,-0.2,0.0,0.0);//1:bottom-left EmitVertex(); gl_Position=position+vec4(0.2,-0.2,0.0,0.0);//2:bottom-right EmitVertex(); gl_Position=position+vec4(-0.2,0.2,0.0,0.0);//3:top-left EmitVertex(); gl_Position=position+vec4(0.2,0.2,0.0,0.0);//4:top-right EmitVertex(); gl_Position=position+vec4(0.0,0.4,0.0,0.0);//5:top fColor=vec3(1.0,1.0,1.0); EmitVertex(); EndPrimitive(); Theresultnowlookssomethinglikethis: YoucancompareyoursourcecodewiththeOpenGLcodehere. Youcanseethatwithgeometryshadersyoucangetprettycreative,evenwiththesimplestprimitives.Becausetheshapesaregenerateddynamicallyontheultra-fasthardwareofyourGPUthiscanbealotmorepowerfulthandefiningtheseshapesyourselfwithinvertexbuffers.Geometryshadersareagreattoolforsimple(often-repeating)shapes,likecubesinavoxelworldorgrassleavesonalargeoutdoorfield. Explodingobjects Whiledrawinghousesisfunandall,it'snotsomethingwe'regoingtousethatmuch.That'swhywe'renowgoingtotakeituponenotchandexplodeobjects!Thatissomethingwe'realsoprobablynotgoingtousethatmucheither,butit'sdefinitelyfuntodo! Whenwesayexplodinganobjectwe'renotactuallygoingtoblowupourpreciousbundledsetsofvertices,butwe'regoingtomoveeachtrianglealongthedirectionoftheirnormalvectoroverasmallperiodoftime.Theeffectisthattheentireobject'strianglesseemtoexplode.Theeffectofexplodingtrianglesonthebackpackmodellooksabitlikethis: Thegreatthingaboutsuchageometryshadereffectisthatitworksonallobjects,regardlessoftheircomplexity. Becausewe'regoingtotranslateeachvertexintothedirectionofthetriangle'snormalvectorwefirstneedtocalculatethisnormalvector.Whatweneedtodoiscalculateavectorthatisperpendiculartothesurfaceofatriangle,usingjustthe3verticeswehaveaccessto.Youmayrememberfromthetransformationschapterthatwecanretrieveavectorperpendiculartotwoothervectorsusingthecrossproduct.Ifweweretoretrievetwovectorsaandbthatareparalleltothesurfaceofatrianglewecanretrieveitsnormalvectorbydoingacrossproductonthosevectors.Thefollowinggeometryshaderfunctiondoesexactlythistoretrievethenormalvectorusing3inputvertexcoordinates: vec3GetNormal() { vec3a=vec3(gl_in[0].gl_Position)-vec3(gl_in[1].gl_Position); vec3b=vec3(gl_in[2].gl_Position)-vec3(gl_in[1].gl_Position); returnnormalize(cross(a,b)); } Hereweretrievetwovectorsaandbthatareparalleltothesurfaceofthetriangleusingvectorsubtraction.Subtractingtwovectorsfromeachotherresultsinavectorthatisthedifferenceofthetwovectors.Sinceall3pointslieonthetriangleplane,subtractinganyofitsvectorsfromeachotherresultsinavectorparalleltotheplane.Donotethatifweswitchedaandbinthecrossfunctionwe'dgetanormalvectorthatpointsintheoppositedirection-orderisimportanthere! Nowthatweknowhowtocalculateanormalvectorwecancreateanexplodefunctionthattakesthisnormalvectoralongwithavertexpositionvector.Thefunctionreturnsanewvectorthattranslatesthepositionvectoralongthedirectionofthenormalvector: vec4explode(vec4position,vec3normal) { floatmagnitude=2.0; vec3direction=normal*((sin(time)+1.0)/2.0)*magnitude; returnposition+vec4(direction,0.0); } Thefunctionitselfshouldn'tbetoocomplicated.Thesinfunctionreceivesatimeuniformvariableasitsargumentthat,basedonthetime,returnsavaluebetween-1.0and1.0.Becausewedon'twanttoimplodetheobjectwetransformthesinvaluetothe[0,1]range.Theresultingvalueisthenusedtoscalethenormalvectorandtheresultingdirectionvectorisaddedtothepositionvector. Thecompletegeometryshaderfortheexplodeeffect,whiledrawingamodelloadedusingourmodelloader,looksabitlikethis: #version330core layout(triangles)in; layout(triangle_strip,max_vertices=3)out; inVS_OUT{ vec2texCoords; }gs_in[]; outvec2TexCoords; uniformfloattime; vec4explode(vec4position,vec3normal){...} vec3GetNormal(){...} voidmain(){ vec3normal=GetNormal(); gl_Position=explode(gl_in[0].gl_Position,normal); TexCoords=gs_in[0].texCoords; EmitVertex(); gl_Position=explode(gl_in[1].gl_Position,normal); TexCoords=gs_in[1].texCoords; EmitVertex(); gl_Position=explode(gl_in[2].gl_Position,normal); TexCoords=gs_in[2].texCoords; EmitVertex(); EndPrimitive(); } Notethatwe'realsooutputtingtheappropriatetexturecoordinatesbeforeemittingavertex. Alsodon'tforgettoactuallysetthetimeuniforminyourOpenGLcode: shader.setFloat("time",glfwGetTime()); Theresultisa3Dmodelthatseemstocontinuallyexplodeitsverticesovertimeafterwhichitreturnstonormalagain.Althoughnotexactlysuperuseful,itdoesshowyouamoreadvanceduseofthegeometryshader.Youcancompareyoursourcecodewiththecompletesourcecodehere. Visualizingnormalvectors Toshakethingsupwe'regoingtonowdiscussanexampleofusingthegeometryshaderthatisactuallyuseful:visualizingthenormalvectorsofanyobject.Whenprogramminglightingshadersyouwilleventuallyrunintoweirdvisualoutputsofwhichthecauseishardtodetermine.Acommoncauseoflightingerrorsisincorrectnormalvectors.Eithercausedbyincorrectlyloadingvertexdata,improperlyspecifyingthemasvertexattributes,orbyincorrectlymanagingthemintheshaders.Whatwewantissomewaytodetectifthenormalvectorswesuppliedarecorrect.Agreatwaytodetermineifyournormalvectorsarecorrectisbyvisualizingthem,anditjustsohappensthatthegeometryshaderisanextremelyusefultoolforthispurpose. Theideaisasfollows:wefirstdrawthesceneasnormalwithoutageometryshaderandthenwedrawthesceneasecondtime,butthistimeonlydisplayingnormalvectorsthatwegenerateviaageometryshader.Thegeometryshadertakesasinputatriangleprimitiveandgenerates3linesfromtheminthedirectionsoftheirnormal-onenormalvectorforeachvertex.Incodeit'lllooksomethinglikethis: shader.use(); DrawScene(); normalDisplayShader.use(); DrawScene(); Thistimewe'recreatingageometryshaderthatusesthevertexnormalssuppliedbythemodelinsteadofgeneratingitourself.Toaccommodateforscalingandrotations(duetotheviewandmodelmatrix)we'lltransformthenormalswithanormalmatrix.Thegeometryshaderreceivesitspositionvectorsasview-spacecoordinatessoweshouldalsotransformthenormalvectorstothesamespace.Thiscanallbedoneinthevertexshader: #version330core layout(location=0)invec3aPos; layout(location=1)invec3aNormal; outVS_OUT{ vec3normal; }vs_out; uniformmat4view; uniformmat4model; voidmain() { gl_Position=view*model*vec4(aPos,1.0); mat3normalMatrix=mat3(transpose(inverse(view*model))); vs_out.normal=normalize(vec3(vec4(normalMatrix*aNormal,0.0))); } Thetransformedview-spacenormalvectoristhenpassedtothenextshaderstageviaaninterfaceblock.Thegeometryshaderthentakeseachvertex(withapositionandanormalvector)anddrawsanormalvectorfromeachpositionvector: #version330core layout(triangles)in; layout(line_strip,max_vertices=6)out; inVS_OUT{ vec3normal; }gs_in[]; constfloatMAGNITUDE=0.4; uniformmat4projection; voidGenerateLine(intindex) { gl_Position=projection*gl_in[index].gl_Position; EmitVertex(); gl_Position=projection*(gl_in[index].gl_Position+ vec4(gs_in[index].normal,0.0)*MAGNITUDE); EmitVertex(); EndPrimitive(); } voidmain() { GenerateLine(0);//firstvertexnormal GenerateLine(1);//secondvertexnormal GenerateLine(2);//thirdvertexnormal } Thecontentsofgeometryshadersliketheseshouldbeself-explanatorybynow.Notethatwe'remultiplyingthenormalvectorbyaMAGNITUDEvectortorestrainthesizeofthedisplayednormalvectors(otherwisethey'dbeabittoolarge). Sincevisualizingnormalsaremostlyusedfordebuggingpurposeswecanjustdisplaythemasmono-coloredlines(orsuper-fancylinesifyoufeellikeit)withthehelpofthefragmentshader: #version330core outvec4FragColor; voidmain() { FragColor=vec4(1.0,1.0,0.0,1.0); } Nowrenderingyourmodelwithnormalshadersfirstandthenwiththespecialnormal-visualizingshaderyou'llseesomethinglikethis: Apartfromthefactthatourbackpacknowlooksabithairy,itgivesusareallyusefulmethodfordeterminingifthenormalvectorsofamodelareindeedcorrect.Youcanimaginethatgeometryshaderslikethiscouldalsobeusedforaddingfurtoobjects. YoucanfindtheOpenGL'ssourcecodehere. HI



請為這篇文章評分?