Hello Triangle - LearnOpenGL
文章推薦指數: 80 %
In modern OpenGL we are required to define at least a vertex and fragment ... and back of all triangles and the second line tells us to draw them as lines. Ifyou'rerunningAdBlock,pleaseconsiderwhitelistingthissiteifyou'dliketosupportLearnOpenGL;andnoworries,Iwon'tbemadifyoudon't:) IntroductionGettingstartedOpenGLCreatingawindowHelloWindowHelloTriangleShadersTexturesTransformationsCoordinateSystemsCameraReviewLightingColorsBasicLightingMaterialsLightingmapsLightcastersMultiplelightsReviewModelLoadingAssimpMeshModelAdvancedOpenGLDepthtestingStenciltestingBlendingFacecullingFramebuffersCubemapsAdvancedDataAdvancedGLSLGeometryShaderInstancingAntiAliasingAdvancedLightingAdvancedLightingGammaCorrectionShadowsShadowMappingPointShadowsNormalMappingParallaxMappingHDRBloomDeferredShadingSSAOPBRTheoryLightingIBLDiffuseirradianceSpecularIBLInPracticeDebuggingTextRendering2DGameBreakoutSettingupRenderingSpritesLevelsCollisionsBallCollisiondetectionCollisionresolutionParticlesPostprocessingPowerupsAudioRendertextFinalthoughtsGuestArticlesHowtopublish2020OITIntroductionWeightedBlendedSkeletalAnimation2021CSMSceneSceneGraphFrustumCullingTessellationHeightmapTessellationDSA2022ComputeShadersIntroductionPhys.BasedBloomCoderepositoryTranslationsAbout BTC 1CLGKgmBSuYJ1nnvDGAepVTKNNDpUjfpRa ETH/ERC20 0x1de59bd9e52521a46309474f8372531533bd7c43 HelloTriangle Getting-started/Hello-Triangle InOpenGLeverythingisin3Dspace,butthescreenorwindowisa2DarrayofpixelssoalargepartofOpenGL'sworkisabouttransformingall3Dcoordinatesto2Dpixelsthatfitonyourscreen.Theprocessoftransforming3Dcoordinatesto2DpixelsismanagedbythegraphicspipelineofOpenGL.Thegraphicspipelinecanbedividedintotwolargeparts:thefirsttransformsyour3Dcoordinatesinto2Dcoordinatesandthesecondparttransformsthe2Dcoordinatesintoactualcoloredpixels.Inthischapterwe'llbrieflydiscussthegraphicspipelineandhowwecanuseittoouradvantagetocreatefancypixels. Thegraphicspipelinetakesasinputasetof3Dcoordinatesandtransformsthesetocolored2Dpixelsonyourscreen.Thegraphicspipelinecanbedividedintoseveralstepswhereeachsteprequirestheoutputofthepreviousstepasitsinput.Allofthesestepsarehighlyspecialized(theyhaveonespecificfunction)andcaneasilybeexecutedinparallel.Becauseoftheirparallelnature,graphicscardsoftodayhavethousandsofsmallprocessingcorestoquicklyprocessyourdatawithinthegraphicspipeline.TheprocessingcoresrunsmallprogramsontheGPUforeachstepofthepipeline.Thesesmallprogramsarecalledshaders. Someoftheseshadersareconfigurablebythedeveloperwhichallowsustowriteourownshaderstoreplacetheexistingdefaultshaders.Thisgivesusmuchmorefine-grainedcontroloverspecificpartsofthepipelineandbecausetheyrunontheGPU,theycanalsosaveusvaluableCPUtime.ShadersarewrittenintheOpenGLShadingLanguage(GLSL)andwe'lldelvemoreintothatinthenextchapter. Belowyou'llfindanabstractrepresentationofallthestagesofthegraphicspipeline.Notethatthebluesectionsrepresentsectionswherewecaninjectourownshaders. Asyoucansee,thegraphicspipelinecontainsalargenumberofsectionsthateachhandleonespecificpartofconvertingyourvertexdatatoafullyrenderedpixel.Wewillbrieflyexplaineachpartofthepipelineinasimplifiedwaytogiveyouagoodoverviewofhowthepipelineoperates. Asinputtothegraphicspipelinewepassinalistofthree3DcoordinatesthatshouldformatriangleinanarrayherecalledVertexData;thisvertexdataisacollectionofvertices.Avertexisacollectionofdataper3Dcoordinate.Thisvertex'sdataisrepresentedusingvertexattributesthatcancontainanydatawe'dlike,butforsimplicity'ssakelet'sassumethateachvertexconsistsofjusta3Dpositionandsomecolorvalue. InorderforOpenGLtoknowwhattomakeofyourcollectionofcoordinatesandcolorvaluesOpenGLrequiresyoutohintwhatkindofrendertypesyouwanttoformwiththedata.Dowewantthedatarenderedasacollectionofpoints,acollectionoftrianglesorperhapsjustonelongline?ThosehintsarecalledprimitivesandaregiventoOpenGLwhilecallinganyofthedrawingcommands.SomeofthesehintsareGL_POINTS,GL_TRIANGLESandGL_LINE_STRIP. Thefirstpartofthepipelineisthevertexshaderthattakesasinputasinglevertex.Themainpurposeofthevertexshaderistotransform3Dcoordinatesintodifferent3Dcoordinates(moreonthatlater)andthevertexshaderallowsustodosomebasicprocessingonthevertexattributes. Theprimitiveassemblystagetakesasinputallthevertices(orvertexifGL_POINTSischosen)fromthevertexshaderthatformaprimitiveandassemblesallthepoint(s)intheprimitiveshapegiven;inthiscaseatriangle. Theoutputoftheprimitiveassemblystageispassedtothegeometryshader.Thegeometryshadertakesasinputacollectionofverticesthatformaprimitiveandhastheabilitytogenerateothershapesbyemittingnewverticestoformnew(orother)primitive(s).Inthisexamplecase,itgeneratesasecondtriangleoutofthegivenshape. Theoutputofthegeometryshaderisthenpassedontotherasterizationstagewhereitmapstheresultingprimitive(s)tothecorrespondingpixelsonthefinalscreen,resultinginfragmentsforthefragmentshadertouse.Beforethefragmentshadersrun,clippingisperformed.Clippingdiscardsallfragmentsthatareoutsideyourview,increasingperformance. AfragmentinOpenGLisallthedatarequiredforOpenGLtorenderasinglepixel. ThemainpurposeofthefragmentshaderistocalculatethefinalcolorofapixelandthisisusuallythestagewherealltheadvancedOpenGLeffectsoccur.Usuallythefragmentshadercontainsdataaboutthe3Dscenethatitcanusetocalculatethefinalpixelcolor(likelights,shadows,colorofthelightandsoon). Afterallthecorrespondingcolorvalueshavebeendetermined,thefinalobjectwillthenpassthroughonemorestagethatwecallthealphatestandblendingstage.Thisstagechecksthecorrespondingdepth(andstencil)value(we'llgettothoselater)ofthefragmentandusesthosetocheckiftheresultingfragmentisinfrontorbehindotherobjectsandshouldbediscardedaccordingly.Thestagealsochecksforalphavalues(alphavaluesdefinetheopacityofanobject)andblendstheobjectsaccordingly.Soevenifapixeloutputcoloriscalculatedinthefragmentshader,thefinalpixelcolorcouldstillbesomethingentirelydifferentwhenrenderingmultipletriangles. Asyoucansee,thegraphicspipelineisquiteacomplexwholeandcontainsmanyconfigurableparts.However,foralmostallthecasesweonlyhavetoworkwiththevertexandfragmentshader.Thegeometryshaderisoptionalandusuallylefttoitsdefaultshader.Thereisalsothetessellationstageandtransformfeedbackloopthatwehaven'tdepictedhere,butthat'ssomethingforlater. InmodernOpenGLwearerequiredtodefineatleastavertexandfragmentshaderofourown(therearenodefaultvertex/fragmentshadersontheGPU).ForthisreasonitisoftenquitedifficulttostartlearningmodernOpenGLsinceagreatdealofknowledgeisrequiredbeforebeingabletorenderyourfirsttriangle.Onceyoudogettofinallyrenderyourtriangleattheendofthischapteryouwillendupknowingalotmoreaboutgraphicsprogramming. Vertexinput TostartdrawingsomethingwehavetofirstgiveOpenGLsomeinputvertexdata.OpenGLisa3DgraphicslibrarysoallcoordinatesthatwespecifyinOpenGLarein3D(x,yandzcoordinate).OpenGLdoesn'tsimplytransformallyour3Dcoordinatesto2Dpixelsonyourscreen;OpenGLonlyprocesses3Dcoordinateswhenthey'reinaspecificrangebetween-1.0and1.0onall3axes(x,yandz).Allcoordinateswithinthissocallednormalizeddevicecoordinatesrangewillendupvisibleonyourscreen(andallcoordinatesoutsidethisregionwon't). Becausewewanttorenderasingletrianglewewanttospecifyatotalofthreeverticeswitheachvertexhavinga3Dposition.Wedefinetheminnormalizeddevicecoordinates(thevisibleregionofOpenGL)inafloatarray: floatvertices[]={ -0.5f,-0.5f,0.0f, 0.5f,-0.5f,0.0f, 0.0f,0.5f,0.0f }; BecauseOpenGLworksin3Dspacewerendera2Dtrianglewitheachvertexhavingazcoordinateof0.0.Thiswaythedepthofthetriangleremainsthesamemakingitlooklikeit's2D. NormalizedDeviceCoordinates(NDC) Onceyourvertexcoordinateshavebeenprocessedinthevertexshader,theyshouldbeinnormalizeddevicecoordinateswhichisasmallspacewherethex,yandzvaluesvaryfrom-1.0to1.0.Anycoordinatesthatfalloutsidethisrangewillbediscarded/clippedandwon'tbevisibleonyourscreen.Belowyoucanseethetrianglewespecifiedwithinnormalizeddevicecoordinates(ignoringthezaxis): Unlikeusualscreencoordinatesthepositivey-axispointsintheup-directionandthe(0,0)coordinatesareatthecenterofthegraph,insteadoftop-left.Eventuallyyouwantallthe(transformed)coordinatestoendupinthiscoordinatespace,otherwisetheywon'tbevisible. YourNDCcoordinateswillthenbetransformedtoscreen-spacecoordinatesviatheviewporttransformusingthedatayouprovidedwithglViewport.Theresultingscreen-spacecoordinatesarethentransformedtofragmentsasinputstoyourfragmentshader. Withthevertexdatadefinedwe'dliketosenditasinputtothefirstprocessofthegraphicspipeline:thevertexshader.ThisisdonebycreatingmemoryontheGPUwherewestorethevertexdata,configurehowOpenGLshouldinterpretthememoryandspecifyhowtosendthedatatothegraphicscard.Thevertexshaderthenprocessesasmuchverticesaswetellittofromitsmemory. Wemanagethismemoryviasocalledvertexbufferobjects(VBO)thatcanstorealargenumberofverticesintheGPU'smemory.Theadvantageofusingthosebufferobjectsisthatwecansendlargebatchesofdataallatoncetothegraphicscard,andkeepitthereifthere'senoughmemoryleft,withouthavingtosenddataonevertexatatime.SendingdatatothegraphicscardfromtheCPUisrelativelyslow,sowhereverwecanwetrytosendasmuchdataaspossibleatonce.Oncethedataisinthegraphicscard'smemorythevertexshaderhasalmostinstantaccesstotheverticesmakingitextremelyfast AvertexbufferobjectisourfirstoccurrenceofanOpenGLobjectaswe'vediscussedintheOpenGLchapter.JustlikeanyobjectinOpenGL,thisbufferhasauniqueIDcorrespondingtothatbuffer,sowecangenerateonewithabufferIDusingtheglGenBuffersfunction: unsignedintVBO; glGenBuffers(1,&VBO); OpenGLhasmanytypesofbufferobjectsandthebuffertypeofavertexbufferobjectisGL_ARRAY_BUFFER.OpenGLallowsustobindtoseveralbuffersatonceaslongastheyhaveadifferentbuffertype.WecanbindthenewlycreatedbuffertotheGL_ARRAY_BUFFERtargetwiththeglBindBufferfunction: glBindBuffer(GL_ARRAY_BUFFER,VBO); Fromthatpointonanybuffercallswemake(ontheGL_ARRAY_BUFFERtarget)willbeusedtoconfigurethecurrentlyboundbuffer,whichisVBO.Thenwecanmakeacalltothe glBufferDatafunctionthatcopiesthepreviouslydefinedvertexdataintothebuffer'smemory: glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_STATIC_DRAW); glBufferDataisafunctionspecificallytargetedtocopyuser-defineddataintothecurrentlyboundbuffer.Itsfirstargumentisthetypeofthebufferwewanttocopydatainto:thevertexbufferobjectcurrentlyboundtotheGL_ARRAY_BUFFERtarget.Thesecondargumentspecifiesthesizeofthedata(inbytes)wewanttopasstothebuffer;asimplesizeofofthevertexdatasuffices.Thethirdparameteristheactualdatawewanttosend. Thefourthparameterspecifieshowwewantthegraphicscardtomanagethegivendata.Thiscantake3forms: GL_STREAM_DRAW:thedataissetonlyonceandusedbytheGPUatmostafewtimes. GL_STATIC_DRAW:thedataissetonlyonceandusedmanytimes. GL_DYNAMIC_DRAW:thedataischangedalotandusedmanytimes. Thepositiondataofthetriangledoesnotchange,isusedalot,andstaysthesameforeveryrendercallsoitsusagetypeshouldbestbeGL_STATIC_DRAW.If,forinstance,onewouldhaveabufferwithdatathatislikelytochangefrequently,ausagetypeofGL_DYNAMIC_DRAWensuresthegraphicscardwillplacethedatainmemorythatallowsforfasterwrites. AsofnowwestoredthevertexdatawithinmemoryonthegraphicscardasmanagedbyavertexbufferobjectnamedVBO.Nextwewanttocreateavertexandfragmentshaderthatactuallyprocessesthisdata,solet'sstartbuildingthose. Vertexshader Thevertexshaderisoneoftheshadersthatareprogrammablebypeoplelikeus.ModernOpenGLrequiresthatweatleastsetupavertexandfragmentshaderifwewanttodosomerenderingsowewillbrieflyintroduceshadersandconfiguretwoverysimpleshadersfordrawingourfirsttriangle.Inthenextchapterwe'lldiscussshadersinmoredetail. ThefirstthingweneedtodoiswritethevertexshaderintheshaderlanguageGLSL(OpenGLShadingLanguage)andthencompilethisshadersowecanuseitinourapplication.Belowyou'llfindthesourcecodeofaverybasicvertexshaderinGLSL: #version330core layout(location=0)invec3aPos; voidmain() { gl_Position=vec4(aPos.x,aPos.y,aPos.z,1.0); } Asyoucansee,GLSLlookssimilartoC.Eachshaderbeginswithadeclarationofitsversion.SinceOpenGL3.3andhighertheversionnumbersofGLSLmatchtheversionofOpenGL(GLSLversion420correspondstoOpenGLversion4.2forexample).Wealsoexplicitlymentionwe'reusingcoreprofilefunctionality. Nextwedeclarealltheinputvertexattributesinthevertexshaderwiththeinkeyword.Rightnowweonlycareaboutpositiondatasoweonlyneedasinglevertexattribute.GLSLhasavectordatatypethatcontains1to4floatsbasedonitspostfixdigit.Sinceeachvertexhasa3Dcoordinatewecreateavec3inputvariablewiththenameaPos.Wealsospecificallysetthelocationoftheinputvariablevialayout(location=0)andyou'lllaterseethatwhywe'regoingtoneedthatlocation. Vector Ingraphicsprogrammingweusethemathematicalconceptofavectorquiteoften,sinceitneatlyrepresentspositions/directionsinanyspaceandhasusefulmathematicalproperties.AvectorinGLSLhasamaximumsizeof4andeachofitsvaluescanberetrievedviavec.x,vec.y,vec.zandvec.wrespectivelywhereeachofthemrepresentsacoordinateinspace.Notethatthevec.wcomponentisnotusedasapositioninspace(we'redealingwith3D,not4D)butisusedforsomethingcalledperspectivedivision.We'lldiscussvectorsinmuchgreaterdepthinalaterchapter. Tosettheoutputofthevertexshaderwehavetoassignthepositiondatatothepredefinedgl_Positionvariablewhichisavec4behindthescenes.Attheendofthemainfunction,whateverwesetgl_Positiontowillbeusedastheoutputofthevertexshader.Sinceourinputisavectorofsize3wehavetocastthistoavectorofsize4.Wecandothisbyinsertingthevec3valuesinsidetheconstructorofvec4andsetitswcomponentto1.0f(wewillexplainwhyinalaterchapter). Thecurrentvertexshaderisprobablythemostsimplevertexshaderwecanimaginebecausewedidnoprocessingwhatsoeverontheinputdataandsimplyforwardedittotheshader'soutput.InrealapplicationstheinputdataisusuallynotalreadyinnormalizeddevicecoordinatessowefirsthavetotransformtheinputdatatocoordinatesthatfallwithinOpenGL'svisibleregion. Compilingashader WetakethesourcecodeforthevertexshaderandstoreitinaconstCstringatthetopofthecodefilefornow: constchar*vertexShaderSource="#version330core\n" "layout(location=0)invec3aPos;\n" "voidmain()\n" "{\n" "gl_Position=vec4(aPos.x,aPos.y,aPos.z,1.0);\n" "}\0"; InorderforOpenGLtousetheshaderithastodynamicallycompileitatrun-timefromitssourcecode.Thefirstthingweneedtodoiscreateashaderobject,againreferencedbyanID.SowestorethevertexshaderasanunsignedintandcreatetheshaderwithglCreateShader: unsignedintvertexShader; vertexShader=glCreateShader(GL_VERTEX_SHADER); WeprovidethetypeofshaderwewanttocreateasanargumenttoglCreateShader.Sincewe'recreatingavertexshaderwepassinGL_VERTEX_SHADER. Nextweattachtheshadersourcecodetotheshaderobjectandcompiletheshader: glShaderSource(vertexShader,1,&vertexShaderSource,NULL); glCompileShader(vertexShader); TheglShaderSourcefunctiontakestheshaderobjecttocompiletoasitsfirstargument.Thesecondargumentspecifieshowmanystringswe'repassingassourcecode,whichisonlyone.Thethirdparameteristheactualsourcecodeofthevertexshaderandwecanleavethe4thparametertoNULL. YouprobablywanttocheckifcompilationwassuccessfulafterthecalltoglCompileShaderandifnot,whaterrorswerefoundsoyoucanfixthose.Checkingforcompile-timeerrorsisaccomplishedasfollows: intsuccess; charinfoLog[512]; glGetShaderiv(vertexShader,GL_COMPILE_STATUS,&success); Firstwedefineanintegertoindicatesuccessandastoragecontainerfortheerrormessages(ifany).ThenwecheckifcompilationwassuccessfulwithglGetShaderiv.Ifcompilationfailed,weshouldretrievetheerrormessagewithglGetShaderInfoLogandprinttheerrormessage. if(!success) { glGetShaderInfoLog(vertexShader,512,NULL,infoLog); std::cout<
延伸文章資訊
- 1OpenGL 101: Drawing primitives - points, lines and triangles
OpenGL 101: Drawing primitives - points, lines and triangles. Posted on May 13, 2013 by Paul. The...
- 2OpenGL Tutorial 7 - Drawing A Line - YouTube
- 3Sample Modern OpenGL Programs - UCSD Math Department
SimpleDrawModern shows how to draw points, lines, line strips, line loops, and triangles. It incl...
- 4Hello Triangle - LearnOpenGL
In modern OpenGL we are required to define at least a vertex and fragment ... and back of all tri...
- 5Drawing Lines is Hard - Matt DesLauriers - Svbtle
Twitter: @mattdesl Drawing lines might not sound like rocket science, but it's damn difficult to ...