Advanced OpenGL in Python with PyGame and PyOpenGL

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

PyOpenGL is a standardized bridge between OpenGL and Python. PyGame is a standardized library for making games with Python. In this article ... SALogotypeArticlesLearnWorkwithUsSigninSignupPythonJavaScriptJavaHomeArticlesAdvancedOpenGLinPythonwithPyGameandPyOpenGLVladimirBatoćaninIntroduction Followingthepreviousarticle,UnderstandingOpenGLthroughPythonwherewe'vesetthefoundationforfurtherlearning,wecanjumpintoOpenGLusingPyGameandPyOpenGL. PyOpenGListhestandardizedlibraryusedasabridgebetweenPythonandtheOpenGLAPIs,andPyGameisastandardizedlibraryusedformakinggamesinPython.Itoffersbuilt-inhandygraphicalandaudiolibrariesandwe'llbeusingittorendertheresultmoreeasilyattheendofthearticle. Asmentionedinthepreviousarticle,OpenGLisveryoldsoyouwon'tfindmanytutorialsonlineonhowtoproperlyuseitandunderstanditbecauseallofthetopdogsarealreadyknee-deepinnewtechnologies. Inthisarticle,we'lljumpintoseveralfundamentaltopicsyou'llneedtoknow: InitializingaProjectUsingPyGame DrawingObjects IterativeAnimation UtilizingTransformationMatrices MultipleTransformationExecution ImplementationExample InitializingaProjectUsingPyGame Firstoff,weneedtoinstallPyGameandPyOpenGLifyouhaven'talready: $python3-mpipinstall-Upygame--user $python3-mpipinstallPyOpenGLPyOpenGL_accelerate Note:YoucanfindamoredetailedinstallationinthepreviousOpenGLarticle. Ifyouhaveproblemsconcerningtheinstallation,PyGame's"Getting Started"sectionmightbeagoodplacetovisit. Sincethere'snopointinunloading3booksworthofgraphicstheoryonyou,we'llbeusingthePyGamelibrarytogiveusaheadstart.Itwillessentiallyjustshortentheprocessfromprojectinitializationtoactualmodelingandanimating. Tostartoff,weneedtoimporteverythingnecessaryfrombothOpenGLandPyGame: importpygameaspg frompygame.localsimport* fromOpenGL.GLimport* fromOpenGL.GLUimport* Next,wegettotheinitialization: pg.init() windowSize=(1920,1080) pg.display.set_mode(display,DOUBLEBUF|OPENGL) Whiletheinitializationisonlythreelinesofcode,eachdeservesatleastasimpleexplanation: pg.init():InitializationofallthePyGamemodules-thisfunctionisagodsend windowSize=(1920,1080):Definingafixedwindowsize pg.display.set_mode(display,DOUBLEBUF|OPENGL):Here,wespecifythatwe'llbeusingOpenGLwithdoublebuffering Doublebufferingmeansthattherearetwoimagesatanygiventime-onethatwecanseeandonethatwecantransformasweseefit.Wegettoseetheactualchangecausedbythetransformationswhenthetwobuffersswap. Sincewehaveourviewportsetup,nextweneedtospecifywhatwe'llbeseeing,orratherwherethe"camera"willbeplaced,andhowfarandwideitcansee. Thisisknownasthefrustum-whichisjustacutoffpyramidthatvisuallyrepresentsthecamera'ssight(whatitcanandcan'tsee). Afrustumisdefinedby4keyparameters: TheFOV(FieldofView):Angleindegrees TheAspectRatio:Definedastheratioofthewidthandheight ThezcoordinateofthenearClippingPlane:Theminimumdrawdistance ThezcoordinateofthefarClippingPlane:Themaximumdrawdistance So,let'sgoaheadandimplementthecamerawiththeseparametersinmind,usingOpenGLCcode: voidgluPerspective(GLdoublefovy,GLdoubleaspect,GLdoublezNear,GLdoublezFar); gluPerspective(60,(display[0]/display[1]),0.1,100.0) Tobetterunderstandhowafrustumworks,here'sareferencepicture: Nearandfarplanesareusedforbetterperformance.Realistically,renderinganythingoutsideourfieldofvisionisawasteofhardwareperformancethatcouldbeusedrenderingsomethingthatwecanactuallysee. Soeverythingthattheplayercan'tseeisimplicitlystoredinmemory,eventhoughitisn'tvisuallypresent.Here'sagreatvideoofhowrenderingonlywithinthefrustumlookslike. DrawingObjects Afterthissetup,Iimaginewe'reaskingourselvesthesamequestion: Wellthisisallfineanddandy,buthowdoImakeaSuperStarDestroyer? Well...withdots.EverymodelinOpenGLobjectisstoredasasetofverticesandasetoftheirrelations(whichverticesareconnected).SotheoreticallyifyouknewthepositionofeverysingledotthatisusedtodrawaSuperStarDestroyer,youcouldverywelldrawone! ThereareafewwayswecanmodelobjectsinOpenGL: Drawingusingvertices,anddependingonhowOpenGLinterpretsthesevertices,wecandrawwith: points:asinliteralpointsthatarenotconnectedinanyway lines:everypairofverticesconstructsaconnectedline triangles:everythreeverticesmakeatriangle quadrilateral:everyfourverticesmakeaquadrilateral polygon:yougetthepoint manymore... DrawingusingthebuiltinshapesandobjectsthatwerepainstakinglymodeledbyOpenGLcontributors Importingfullymodeledobjects So,todrawacubeforexample,wefirstneedtodefineitsvertices: cubeVertices=((1,1,1),(1,1,-1),(1,-1,-1),(1,-1,1),(-1,1,1),(-1,-1,-1),(-1,-1,1),(-1,1,-1)) Then,weneedtodefinehowthey'reallconnected.Ifwewanttomakeawirecube,weneedtodefinethecube'sedges: cubeEdges=((0,1),(0,3),(0,4),(1,2),(1,7),(2,5),(2,3),(3,6),(4,6),(4,7),(5,6),(5,7)) Thisisprettyintuitive-thepoint0hasanedgewith1,3,and4.Thepoint1hasanedgewithpoints3,5,and7,andsoon. Andifwewanttomakeasolidcube,thenweneedtodefinethecube'squadrilaterals: cubeQuads=((0,3,6,4),(2,5,6,3),(1,2,5,7),(1,0,4,7),(7,4,6,5),(2,3,0,1)) Thisisalsointuitive-tomakeaquadrilateralonthetopsideofthecube,we'dwantto"color"everythingin-betweenthepoints0,3,6,and4. Keepinmindthere'sanactualreasonwelabeltheverticesasindexesofthearraythey'redefinedin.Thismakeswritingcodethatconnectsthemveryeasy. Thefollowingfunctionisusedtodrawawiredcube: FreeeBook:GitEssentialsCheckoutourhands-on,practicalguidetolearningGit,withbest-practices,industry-acceptedstandards,andincludedcheatsheet.StopGooglingGitcommandsandactuallylearnit!DownloadtheeBook defwireCube(): glBegin(GL_LINES) forcubeEdgeincubeEdges: forcubeVertexincubeEdge: glVertex3fv(cubeVertices[cubeVertex]) glEnd() glBegin()isafunctionthatindicateswe'lldefiningtheverticesofaprimitiveinthecodebelow.Whenwe'redonedefiningtheprimitive,weusethefunctionglEnd(). GL_LINESisamacrothatindicateswe'llbedrawinglines. glVertex3fv()isafunctionthatdefinesavertexinspace,thereareafewversionsofthisfunction,soforthesakeofclaritylet'slookathowthenamesareconstructed: glVertex:afunctionthatdefinesavertex glVertex3:afunctionthatdefinesavertexusing3coordinates glVertex3f:afunctionthatdefinesavertexusing3coordinatesoftypeGLfloat glVertex3fv:afunctionthatdefinesavertexusing3coordinatesoftypeGLfloatwhichareputinsideavector(tuple)(thealternativewouldbeglVertex3flwhichusesalistofargumentsinsteadofavector) Followingsimilarlogic,thefollowingfunctionisusedtodrawasolidcube: defsolidCube(): glBegin(GL_QUADS) forcubeQuadincubeQuads: forcubeVertexincubeQuad: glVertex3fv(cubeVertices[cubeVertex]) glEnd() IterativeAnimation Forourprogramtobe"killable"weneedtoinsertthefollowingcodesnippet: foreventinpg.event.get(): ifevent.type==pg.QUIT: pg.quit() quit() It'sbasicallyjustalistenerthatscrollsthroughPyGame'sevents,andifitdetectsthatweclickedthe"killwindow"button,itquitstheapplication. We'llcovermoreofPyGame'seventsinafuturearticle-thisonewasintroducedrightawaybecauseitwouldbequiteuncomfortableforusersandyourselvestohavetofireupthetaskmanagereverytimetheywanttoquittheapplication. Inthisexample,we'llbeusingdoublebuffering,whichjustmeansthatwe'llbeusingtwobuffers(youcanthinkofthemascanvasesfordrawing)whichwillswapinfixedintervalsandgivetheillusionofmotion. Knowingthis,ourcodehastohavethefollowingpattern: handleEvents() glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) doTransformationsAndDrawing() pg.display.flip() pg.time.wait(1) glClear:Functionthatclearsthespecifiedbuffers(canvases),inthiscase,thecolorbuffer(whichcontainscolorinformationfordrawingthegeneratedobjects)anddepthbuffer(abufferwhichstoresin-front-oforin-back-ofrelationsofallthegeneratedobjects). pg.display.flip():Functionthatupdatedthewindowwiththeactivebuffercontents pg.time.wait(1):Functionthatpausestheprogramforaperiodoftime glClearhastobeusedbecauseifwedon'tuseit,we'llbejustpaintingoveranalreadypaintedcanvas,whichinthiscase,isourscreenandwe'regoingtoendupwithamess. Next,ifwewanttocontinuouslyupdateourscreen,justlikeananimation,wehavetoputallourcodeinsideawhileloopinwhichwe: Handleevents(inthiscase,justquitting) Clearthecoloranddepthbufferssothattheycanbedrawnonagain Transformanddrawobjects Updatethescreen GOTO1. Thecodeoughttolooksomethinglikethis: whileTrue: handleEvents() glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) doTransformationsAndDrawing() pg.display.flip() pg.time.wait(1) UtilizingTransformationMatrices Inthepreviousarticle,weexplainedhow,intheory,weneedtoconstructatransformationthathasareferralpoint. OpenGLworksthesameway,ascanbeseeninthefollowingcode: glTranslatef(1,1,1) glRotatef(30,0,0,1) glTranslatef(-1,-1,-1) Inthisexample,wedidaz-axisrotationinthexy-planewiththecenterofrotationbeing(1,1,1)by30degrees. Let'shavealittlerefresherifthesetermssoundabitconfusing: z-axisrotationmeansthatwe'rerotatingaroundthez-axis Thisjustmeanswe'reapproximatinga2Dplanewitha3Dspace,thiswholetransformationisbasicallylikedoinganormalrotationaroundareferralpointin2Dspace. Wegetthexy-planebysquashinganentire3Dspaceintoaplanethathasz=0(weeliminatethezparameterineveryway) Centerofrotationisavertexaroundwhichwewillberotatingagivenobject(thedefaultcenterofrotationistheoriginvertex(0,0,0)) Butthere'sacatch-OpenGLunderstandsthecodeabovebyconstantlyrememberingandmodifyingoneglobaltransformationmatrix. SowhenyouwritesomethinginOpenGL,whatyou'resayingis: #Thispartofthecodeisnottranslated #transformationmatrix=E(neutral) glTranslatef(1,1,1) #transformationmatrix=TxE #ALLOBJECTSFROMNOWONARETRANSLATEDBY(1,1,1) Asyoumightimagine,thisposesahugeproblem,becausesometimeswewanttoutilizeatransformationonasingleobject,notonthewholesourcecode.Thisisaverycommonreasonforbugsinlow-levelOpenGL. TocombatthisproblematicfeatureofOpenGL,we'representedwithpushingandpoppingtransformationmatrices-glPushMatrix()andglPopMatrix(): #TransformationmatrixisT1beforethisblockofcode glPushMatrix() glTranslatef(1,0,0) generateObject()#Thisobjectistranslated glPopMatrix() generateSecondObject()#Thisobjectisn'ttranslated TheseworkinasimpleLast-in-First-Out(LIFO)principle.Whenwewishtoperformatranslationtoamatrix,wefirstduplicateitandthenpushitontopofthestackofthetransformationmatrices. Inotherwords,itisolatesallthetransformationswe'reperforminginthisblockbycreatingalocalmatrixthatwecanscrapafterwe'redone. Oncetheobjectistranslated,wepopthetransformationmatrixfromthestack,leavingtherestofthematricesuntouched. MultipleTransformationExecution InOpenGL,aspreviouslymentioned,transformationsareaddedtotheactivetransformationmatrixthat'sontopofstackoftransformationmatrices. Thismeansthatthetransformationsareexecutedinreverseorder.Forexample: #########Firstexample########## glTranslatef(-1,0,0) glRotatef(30,0,0,1) drawObject1() ################################## ########SecondExample######### glRotatef(30,0,0,1) glTranslatef(-1,0,0) drawObject2() ################################# Inthisexample,Object1isfirstrotated,thentranslated,andObject2isfirsttranslated,andthenrotated.Thelasttwoconceptswon'tbeusedintheimplementationexample,butwillbepracticallyusedinthenextarticleintheseries. ImplementationExample Thecodebelowdrawsasolidcubeonthescreenandcontinuouslyrotatesitby1degreearoundthe(1,1,1)vector.AnditcanbeveryeasilymodifiedtodrawawirecubebyswappingoutthecubeQuadswiththecubeEdges: importpygameaspg frompygame.localsimport* fromOpenGL.GLimport* fromOpenGL.GLUimport* cubeVertices=((1,1,1),(1,1,-1),(1,-1,-1),(1,-1,1),(-1,1,1),(-1,-1,-1),(-1,-1,1),(-1,1,-1)) cubeEdges=((0,1),(0,3),(0,4),(1,2),(1,7),(2,5),(2,3),(3,6),(4,6),(4,7),(5,6),(5,7)) cubeQuads=((0,3,6,4),(2,5,6,3),(1,2,5,7),(1,0,4,7),(7,4,6,5),(2,3,0,1)) defwireCube(): glBegin(GL_LINES) forcubeEdgeincubeEdges: forcubeVertexincubeEdge: glVertex3fv(cubeVertices[cubeVertex]) glEnd() defsolidCube(): glBegin(GL_QUADS) forcubeQuadincubeQuads: forcubeVertexincubeQuad: glVertex3fv(cubeVertices[cubeVertex]) glEnd() defmain(): pg.init() display=(1680,1050) pg.display.set_mode(display,DOUBLEBUF|OPENGL) gluPerspective(45,(display[0]/display[1]),0.1,50.0) glTranslatef(0.0,0.0,-5) whileTrue: foreventinpg.event.get(): ifevent.type==pg.QUIT: pg.quit() quit() glRotatef(1,1,1,1) glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) solidCube() #wireCube() pg.display.flip() pg.time.wait(10) if__name__=="__main__": main() Runningthispieceofcode,aPyGamewindowwillpopup,renderingthecubeanimation: Conclusion ThereisalotmoretolearnaboutOpenGL-lighting,textures,advancedsurfacemodeling,compositemodularanimation,andmuchmore. Butfretnot,allofthiswillbeexplainedinthefollowingarticlesteachingthepublicaboutOpenGLtheproperway,fromthegroundup. Anddon'tworry,inthenextarticle,we'llactuallydrawsomethingsemi-decent. #python#opengl#pygame#pyopenglLastUpdated:February5th,2020Wasthisarticlehelpful?Youmightalsolike...UnderstandingOpenGLthroughPythonBriefIntroductiontoOpenGLinPythonwithPyOpenGLHandlingUnixSignalsinPythonDon'tUseFlatten()-GlobalPoolingforCNNswithTensorFlowandKerasTheBestMachineLearningLibrariesinPythonImproveyourdevskills!Gettutorials,guides,anddevjobsinyourinbox.EmailaddressSignUpNospamever.Unsubscribeatanytime.ReadourPrivacyPolicy.VladimirBatoćaninAuthorInthisarticleIntroductionInitializingaProjectUsingPyGameDrawingObjectsIterativeAnimationUtilizingTransformationMatricesMultipleTransformationExecutionImplementationExampleConclusionProjectReal-TimeRoadSignDetectionwithYOLOv5#python#machinelearning#computervision#pytorchIfyoudrive-there'sachanceyouenjoycruisingdowntheroad.Aresponsibledriverpaysattentiontotheroadsigns,andadjuststheir...DavidLandupDetailsProjectDataVisualizationinPython:TheCollatzConjecture#python#matplotlib#datavisualizationTheCollatzConjectureisanotoriousconjectureinmathematics.Aconjectureisaconclusionbasedonexistingevidence-however,aconjecturecannotbeproven....DetailsTwitterGitHubFacebook©2013-2022StackAbuse.Allrightsreserved.DisclosurePrivacyTermsDonotsharemyPersonalInformation.



請為這篇文章評分?