3D Játékok készítése OpenGL környezetben Szirmay-Kalos László Irányítástechnika és Informatika Tanszék Budapesti Műszaki és Gazdaságtudományi Egyetem email: szirmay@iit.bme.hu Web: http://www.iit.bme.hu/~szirmay Demo 1: A cél sgame/spaceshootgame 3D játékok Enemy AI Virtuális valóság Field objects Self képszintézis interakció vezérlés Virtuális világ Játékok feladatai Képszintézis az avatár nézőpontjából Az avatár vezérlése a beviteli eszközökkel (keyboard, mouse) Az intelligens virtuális objektumok vezérlése (AI) A fizikai világ szimulációja I/O könyvtárak Windows + GLUT input képszintézis szimuláció Virtuális világmodell OpenGL
Input/Output menedzsment Operációs rendszer Windows Grafikus hardver inicializáció callback regisztráció GLUT callbacks OpenGL main DisplayFunc KeyboadFunc SpecialFunc IdleFunc applikáció R,G,B - R,G,B - Z Initiacializáció #include <GL/gl.h> #include <GL/glu.h> #include <GL/glut.h> void main(int argc, char* argv[]) { glutinit(&argc, argv); glutinitwindowsize(600, 600); // ablak kijelölés glutinitdisplaymode(glut_rgb GLUT_DOUBLE GLUT_DEPTH); glutcreatewindow( "Space Game" ); glutdisplayfunc( DisplayFunc ); // callback regisztráció glutidlefunc( IdleFunc ); glutkeyboardfunc( KeyboardFunc ); glutspecialfunc( SpecialKeyboardFunc ); glutmainloop(); Demo 2: Szimpla és dupla bufferelés single:sgame/files/sgamesing.exe double: sgame/files/sgame.exe draw Dupla bufferelés frame buffer 1. frame buffer 2. glclear(gl_color_buffer_bit); drawing glutswapbuffers( ); display monitor Képszintézis OpenGL-lel Világ + mozgás Nézeti transzformáció 2. 1. Perspektív transzformáció x y z A transzformáció lánc Tmodellview Tmodell Tview Tperspective eltolás, forgatás nagyítás szempozíció Nézeti irány Függőleges X Y Z Látószög Képoldal arány Első-hátsó vágás Tviewport XP YP 628 1325 1325 628 vágás Láthatóság: z-buffer megjelenítés
Tárgyak geometriai definíciója glbegin(gl_triangles); Texturing x13,y13,z13 x12,y12,z12 glcolor3f( 1, 1, 0 ); glvertex3f( x11, y11, z11 ); glvertex3f( x12, y12, z12 ); glvertex3f( x13, y13, z13 ); x11,y11,z11 glcolor4f(r,g,b,a) glcolor3f( 0, 1, 0 ); glvertex3f( x21, y21, z21 ); glvertex3f( x22, y22, z22 ); glvertex3f( x23, y23, z23 ); glend(); (u1, v1) Textúra leképzés (u2, v2) (u3, v3) x2,y2,z2 x3,y3,z3 x1,y1,z1 glenable(gl_texture_2d); glbindtexture(gl_texture_2d, texture_id); // which texture glbegin(gl_triangles); gltexcoord2f(u1, v1); glvertex3f(x1, y1, z1); gltexcoord2f(u2, v2); glvertex3f(x2, y2, z2); gltexcoord2f(u3, v3); glvertex3f(x3, y3, z3); glend(); gldisable(gl_texture_2d); x Felületek mint háromszöghálók: Tesszelláció 1. Keressük meg a felület paraméteres egyenletét x(u,v) = x0 + r cos 2πu sin πv y(u,v) = y0 + r sin 2πu sin πv z(u,v) = z0 + r cos πv u,v [0,1] 2. Háromszögesítsük a paraméterteret z θ ϕ y Kvadratikus felületek: Gömb // definition GLUquadricObj * quadric = glunewquadric( ); gluquadrictexture(quadric, GL_TRUE); // draw glbindtexture(gl_texture_2d, texture_id); glusphere(quadric, R, 16, 10); GLUT idővezérlés float time; void IdleFunc( ) { // idle call back float old_time = time; time = glutget( GLUT_ELAPSED_TIME ); float dt = time - old_time; AnimateIt( dt ); DrawIt( );
Forog a Föld: animate, draw void AnimateIt( float dt ) { angle += rot_speed * dt; if (angle > 360.0) angle -= 360.0; void DrawIt( ) { glclearcolor(0, 0, 0, 0); // R,G,B,A glclear(gl_color_buffer_bit GL_DEPTH_BUFFER_BIT); glpushmatrix( ); glrotatef( angle, 0, 0, 1 ); glbindtexture(gl_texture_2d, earth_texture); glusphere( quadric, 1.0, 16, 10 ); glpopmatrix( ); glutswapbuffers( ); Demo 3: Forog a föld sgame/earth/earth1.exe dist rot_angle rev_angle A Föld kering a Nap körül void AnimateIt( float dt ) { rot_angle += rot_speed * dt; rev_angle += rev_speed * dt; void DrawIt( ) { glpushmatrix( ); glrotatef(rev_angle, 0, 0, 1); gltranslatef(dist, 0, 0 ); glrotatef(rot_angle, 0, 0, 1); glbindtexture(gl_texture_2d, earth_texture); glusphere(quadric, 1, 16, 10); glpopmatrix( ); glutswapbuffers( ); Demo 3: A Föld kering a Nap körül sgame/earth/earth.exe -SS,-SS,-SS SS,SS,SS Az űr void DrawIt( ) { glbindtexture(gl_texture_2d, space_texture); glbegin(gl_quads); gltexcoord2f(0, 0); glvertex3f(-ss, -SS, -SS); gltexcoord2f(0, 1); glvertex3i(-ss, SS, -SS); gltexcoord2f(1, 1); glvertex3i( SS, SS, -SS); gltexcoord2f(1, 0); glvertex3i( SS, -SS, -SS);... glend(); Játékok képszintézis vezérlés AnimateIt(dt), DrawIt() avatár interakció ControlIt(dt) Virtuális világ
Játékobjektumok ControlIt: gondolkodik és a lehetséges vezérléséket alkalmazza (pl. rakéták) InteractIt: Társalog másokkal, figyeli a többieket AnimateIt: A következő helyre és orientációba megy DrawIt: Felrajzolja magát a képernyőre Szimulációs hurok (Játék hurok) dt void IdleFunc( ) { // idle call back float old_time = time; time = glutget( GLUT_ELAPSED_TIME ); float dt = time - old_time; -> ProcessInput( ); world -> Control( dt ); world -> Animate( dt ); glclearcolor(0, 0, 0, 0); glclear(gl_color_buffer_bit GL_DEPTH_BUFFER_BIT); -> SetCameraTransform(); world -> Draw(); glutswapbuffers( ); world A virtuális világ Objektumok dinamikusak (öldöklés) Különböző objektumtípusok (heterogén kollekció) Láncolt lista (fák) ship1 ship2 space sun bullet explosion Join: új elem hozzávétele Az űrhajó Komplex geometria négyszögháló Komplex textúra Fizikai animáció erők (gravitáció, rakéták) ütközések Viselkedés (AI) A rakéták vezérlése Ütközés elkerülés, avatártól menekülés, avatár üldözése Az űrhajó felépítése: Maya Kihúzás
Megint kihúzás És megint kihúzás Az utolsó kihúzás = 1/4 Σ Subdivision surfaces = 1/4 Σ + 1/4 Σ = 1/2 + 1/16 Σ + 1/16 Σ Aláosztásos simítás: 1 szint Aláosztásos simítás: 2. szint
Textúrázás Paraméterezés: Maya (0,0) (1,1) modell textúra A textúrázott űrhajó Animate: : Newton mozgástörvényei m force position velocity void Ship :: AnimateIt( float dt ) { acceleration = force/m; velocity += acceleration * dt; position += velocity * dt; void Ship :: DrawIt() { glpushmatrix( ); gltranslatef(position.x, position.y, position.z); glbegin( GL_QUADS );... ; glend( ); glpopmatrix();
Orientáció beállítása void Ship :: DrawIt() { glpushmatrix( ); gltranslatef(position.x, position.y, position.z); modell_head world_head = velocity.unitvector(); Vector modell_head( 0, 0, 1 ); Vector world_head = velocity.unitvector(); Vector rotate_axis = modell_head % world_head; float cos_rotate_angle = world_head * modell_head; glrotatef( acos(cos_rotate_angle)* 180 / M_PI, rotate_axis.x,rotate_axis.y,rotate_axis.z); glbegin( GL_QUADS );... ; glend( ); glpopmatrix( ); Ship :: ControlIt void Ship :: ControlIt( float dt ) { force = Vector(0, 0, 0); Interact( world ); void Ship::InteractIt( GameObject * object ) world ship1 ship2 space sun bullet explosion Ship: InteractIt void Ship :: InteractIt( GameObject * object ) { if ( object->gettype( ) == PLANET ) { Ship: InteractIt if ( object->gettype( ) == AVATAR ) { bullet F = f m M r 2 aiming angle F = f m M r 2 if ( object->gettype( ) == PLANET ) { Planet * planet = (Planet *)object; Vector dist = planet->position - position; float r = dist.length(); force += dist * f * mass * planet->mass /r/r/r; if ( r < planet->radius() * 2 ) force += (position - planet->position)/r; if ( r < planet->radius()+boundingradius() ) Kill(); Ship: InteractIt bullet Ütközésdetektálás lassú objektumok között if ( object->gettype( ) == AVATAR ) { Avatar * = (Avatar *)object; Vector goal = -> position - position; force += goal * 0.05; aim cosaim = velocity.unitvector()*goal.unitvector(); if (cosaim > 0.9) { world-> Join( new Bullet(position, velocity)); given t Probléma, ha az objektum gyors dist = obj1.position - obj2.position min = obj1.boundingradius() + obj2.boundingradius() if (dist.length() < min) Collision! t t + t
Nagyon komplex geometria Hasonló kinézet minden irányból Könnyebb a képét használni transparent Lövedék Ütközésdetektálás = gyors mozgás Plakátok: Billboards Egyetlen félig átlátszó textúra egy téglalapon QUAD x y z QUAD pos Tmodell Tview Tperspective pozíció orientáció kamera pozíció kamera orientáció pos X Y Z Bullet :: DrawIt void Bullet :: DrawIt() { A modell transzformáció forgatási részével a nézeti transzformáció forgatási részét kompenzáljuk glenable(gl_blend); // transparency glblendfunc(gl_src_alpha, GL_ONE); glbegin(gl_quads); gltexcoord2f(0, 0); glvertex2f(-size, -size); gltexcoord2f(1, 0); glvertex2f(size, -size); gltexcoord2f(1, 1); glvertex2f(size, size); gltexcoord2f(0, 1); glvertex2f(-size, size); glend(); gldisable(gl_blend); velocity Gyors ütközésdetektálás: ray-tracing rel_velocity = velocity - vel2 ray: position + rel_velocity t If (ray intersects bounding sphere first AND tintersect < dt) Collision! hit_object = world->intersect(position,velocity,t); world position ship1 vel2 ship2 space sun bullet explosion Nagyon komplex geometria Hasonló kinézet minden irányból Plakátgyűjtemény Részecske rendszer Robbanás Részecske rendszerek Véletlen Kezdeti értékek position: velocity: acceleration: lifetime age: size, dsize: weight, dweight: color, dcolor: Globális erőtér (szél fújja a füstöt) position += velocity * dt velocity += acceleration * dt acceleration = force / weight age += dt; if (age > lifetime) Kill(); size += dsize * dt; weight += dweight * dt color += dcolor * dt
Robbanás paraméterei var Rand(mean, var) mean position = center; // initially focused lifetime = Rand( 2.0, 1.0 ); size = 0.001; // initially small dsize = Rand( 0.5, 0.25 ) / lifetime; velocity = Rand( Vector(0, 0, 0), 0.4 ); // symmetric acceleration = Rand( Vector(0, 0, 0), 0.4 ); Avatár A viselkedését a klaviatúra vezérli: ProcessInput A helye és iránya viszi a kamerát SetCameraTransform Olyan mint egy űrhajó, de nem rajzoljuk Control: gravitáció, lövedék ütközés // from yellow opaque animate to reddish transparent color = Rand( Color(1, 0.5, 0, 1), Color(0, 0.5, 0, 0) ); dcolor = Color(0, -0.25, 0, -1) / lifetime; KeyboardFunc SpecialFunc Klaviatúra kezelés input IsSpace, IsLeft, IsRight, IsUp, IsDown Avatar :: ProcessInput Avatar :: ProcessInput( GLUTWindow * input ) { if ( input->isspace( ) )// shoot world -> Join(new Bullet(position, velocity)); Vector head = velocity.unitvector(); IdleFunc: GameLoop virtual world if ( input->isup() ) force += up * (-1); if ( input->isdown() ) force += up; if ( input->isleft() ) force += up % head; if ( input->isright() ) force += head % up; GLUTWindow GameEngine DisplayFunc IdleFunc KeyPress GameObject position, velocity, acceleration ControlIt(float dt ) AnimateIt(float dt) InteractIt( GameObject * o) DrawIt( ) IntersectIt(Ray r, float& t) world Member Control, Animate, Draw Interact, Intersect, Join next Játékmotor 500 C++ sor Texture Load( char * fname) Particle Avatar Self ProcessInput ControlIt InteractIt BillBoard Bullet ControlIt Űrjáték Space DrawIt TexturedObject Planet DrawIt AnimateIt Ship DrawIt InteractIt ControlIt GameEngine Avatar ProcessInput() SetCameraTransform() TexturedObject BillBoard DrawIt() ParticleSystem Emit(int n) 350 C++ sor SpaceGame
Egy földi lövöldözős játék Terepek ég terep karakterek magasságmező textúra Terep ütközésdetektálás if (height(x,y) > z) Ütközés! Terepen járás: Pozíció(x, y) = (x, y, height(x,y) + lábmagasság) z x,y Ellenség Bonyolult, animált geometia Clip-ek = kulcskeretek Áll, fut, támad, haldoklik, szenved, + mesh animáció Textúrák (animált) Mesterséges intelligencia Ütközés: befoglaló gömb Futás clip: 1.. kulcskeret Futás clip: : 2. kulcskeret
Futás clip: : 3. kulcskeret Futás clip: : 4. kulcskeret Futás clip: : 5. kulcskeret Futás clip: : 6. kulcskeret t= 0 Mesh animáció: Idő: t Két közrefogó keyframe AI gép Mozgásvezérlés AI állapot Idő: t t= 1 Lináris interpoláció csúcspontonként Csúcspont pozíciók Clip = start, stop frame Keyframe animáció Csúcspont pozíciók kulcskeretek
Mesterséges intelligencia Dont Care Dist < 4 && Avatar_angle > 60 Chase Dist < 4 && Avatar_angle < 40 Dist > 6 Dist > 1 Escape Avatar_angle < 20 Dist < 1 && Aim_angle < 10 Attack Textúrázás Ütközés golyóval Dying Aim_angle Avatar_angle Avatar