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
3D játékok Enemy AI Self Field objects
Virtuális valóság képszintézis interakció vezérlés avatar 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
Játékok ControlIt(dt) AnimateIt(dt), DrawIt() képszintézis interakció vezérlés avatár ProcessInput(), SetCameraTransform() Virtuális világ
Játékobjektumok ControlIt: Alkalmazza a virtuális világ törvényeit erre az objektumra + gondolkodik” és a lehetséges vezérléséket alkalmazza (pl. rakéták) InteractIt: Társalog másokkal, tájékoztat az állapotáról AnimateIt: A következő helyre és orientációba megy DrawIt: Felrajzolja magát a képernyőre
Szimulációs hurok (Game loop) dt void IdleFunc( ) { // idle call back float old_time = time; time = glutGet( GLUT_ELAPSED_TIME ); float dt = time - old_time; avatar -> ProcessInput( ); world -> Control( dt ); world -> Animate( dt ); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); avatar -> SetCameraTransform(); world -> Draw(); glutSwapBuffers( ); }
A virtuális világ (Scene graph) Objektumok dinamikusak (öldöklés) Különböző objektumtípusok (heterogén kollekció) Láncolt lista (fák) world avatar ship1 ship2 space sun bullet explosion Join: új elem hozzávétele KillIt: egy elem eltávolítása
Bolygó Geometria: gömb Textúra Fizikai vagy képletanimáció
Gömbfelület mint háromszög háló: Tesszelláció 1. Paraméteres egyenlet: x = x0 + r cos 2u sin v y = y0 + r sin 2u sin v z = z0 + r cos v u,v [0,1] 2. Paramétertér háromszögesítése + behelyettesítés
GLU kvadratikus felület // definition GLUquadricObj * quadric = gluNewQuadric( ); gluQuadricTexture(quadric, GL_TRUE); // draw gluSphere(quadric, R, 16, 10);
Forog a Föld: Animate, Draw void Planet :: AnimateIt( float dt ) { rot_angle += rot_speed * dt; if (angle > 360.0) rot_angle -= 360.0; } void Planet :: DrawIt( ) { glBindTexture(GL_TEXTURE_2D, earth_texture); glPushMatrix( ); glRotatef( rot_angle, 0, 0, 1 ); gluSphere( quadric, radius, 16, 10 ); glPopMatrix( );
A Föld kering a Nap körül void Planet::AnimateIt(float dt){ rot_angle += rot_speed * dt; rev_angle += rev_speed * dt; } void Planet::DrawIt( ) { glBindTexture(GL_TEXTURE_2D, earth_texture); glPushMatrix( ); glRotatef(rev_angle, 0, 0, 1); glTranslatef(dist, 0, 0 ); glRotatef(rot_angle, 0, 0, 1); gluSphere(quadric, 1, 16, 10); glPopMatrix( ); rot_angle rev_angle dist
Az űr void Space :: DrawIt( ) { glBindTexture(GL_TEXTURE_2D, SS,SS,SS Az űr void Space :: DrawIt( ) { glBindTexture(GL_TEXTURE_2D, space_texture); glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex3i(-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(); } -SS,-SS,-SS
Az űrhajó Komplex geometria Komplex textúra Fizikai animáció 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
Űrhajó geometria
Poligon modellezés: 1. extruding
Poligon modellezés: 2. extruding
Poligon modellezés: 4. és 5. extruding
Poligon modellezés: 6. extruding
Subdivision simítás: 1 szint
Subdivision simítás: 2. szint
Textúra függvény definíciója (1,1) (0,0)
Textúra függvény definíciója
Textúrázott űrhajó
Animate: Newton mozgástörvényei force m 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 modell_head Orientáció beállítása world_head = velocity.UnitVector(); void Ship :: DrawIt() { glPushMatrix( ); glTranslatef(position.x, position.y, position.z); 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 avatar ship1 ship2 space sun bullet explosion
Ship: InteractIt m·M F = f r2 void Ship :: InteractIt( GameObject * object ) { if ( object->GetType( ) == PLANET ) { } if ( object->GetType( ) == AVATAR ) { m·M r2 F = f bullet avatar avatar aiming angle
Ütközésdetektálás lassú objektumok között Probléma, ha az objektum gyors t + t t adott t dist = obj1.position - obj2.position min = obj1.BoundingRadius() + obj2.BoundingRadius() if (dist.Length() < min) Collision!
Lövedék Nagyon komplex geometria Hasonló kinézet minden irányból Könnyebb a képét használni Ütközésdetektálás = gyors mozgás transparent
Plakátok: Billboards Egyetlen félig átlátszó textúra egy téglalapon X pos pos QUAD X Y Z x y z Tmodell Tview Tperspective QUAD pozíció orientáció kamera pozíció kamera orientáció
Bullet :: DrawIt a nézeti transzformáció forgatási részét kompenzáljuk 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); }
Gyors ütközésdetektálás: ray-tracing position rel_velocity = velocity - vel2 ray: position + rel_velocity·t If (ray intersects bounding sphere first AND tintersect < dt) Collision! vel2 velocity hit_object = world->Intersect(position,velocity,t); world avatar ship1 ship2 space sun bullet explosion
Robbanás Nagyon komplex geometria Hasonló kinézet minden irányból Plakátgyűjtemény Részecske rendszer
Részecske rendszerek Globális erőtér (szél fújja a füstöt) position: position += velocity * dt velocity: velocity += acceleration * dt acceleration: acceleration = force / weight lifetime age: age += dt; if (age > lifetime) Kill(); size, dsize: size += dsize * dt; weight, dweight: weight += dweight * dt color, dcolor: color += dcolor * dt Véletlen Kezdeti értékek
Robbanás paraméterei var Rand(mean, var) mean position = center; // initially focused lifetime = Rand(2, 1); size = 0.001; // initially small dsize = Rand(0.5, 0.25) / lifetime; velocity = Vector(Rand(0,0.4),Rand(0,0.4),Rand(0,0.4)); acceleration = Vector(Rand(0,1),Rand(0,1),Rand(0,1)); // from yellow opaque animate to reddish transparent color = Color(1, Rand(0.5, 0.25) 0, 1 ); dcolor = Color(0, -0.25, 0, -1) / lifetime;
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
Klaviatúra kezelés IsSpace, IsLeft, IsRight, IsUp, IsDown KeyboardFunc KeyboardUpFunc SpecialKeysFunc SpecialKeysUpFunc input IsSpace, IsLeft, IsRight, IsUp, IsDown IdleFunc: GameLoop virtual world
Avatar :: ProcessInput Avatar :: ProcessInput( GLUTWindow * input ) { if ( input->IsSpace( ) ) // shoot world -> Join(new Bullet(position, velocity)); Vector head = velocity.UnitVector(); if ( input->IsUp() ) force += up * (-1); if ( input->IsDown() ) force += up; if ( input->IsLeft() ) force += up % head; if ( input->IsRight() ) force += head % up; }
Avatar :: SetCameraTransform glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(position.x, position.y, position.z, position.x + head.x, position.y + head.y, position.z + head.z, up.x, up.y, up.z); } eye up = [0, 1, 0] vagy a gyorsulásból és a korábbi up átlagából lookat
Játékmotor Texture Load( char * fname) next Particle ParticleSystem GLUTWindow GameObject position, velocity, acceleration ControlIt(float dt ) AnimateIt(float dt) InteractIt( GameObject * o) DrawIt( ) IntersectIt(Ray r, float& t) Játékmotor GameEngine DisplayFunc IdleFunc KeyPress 500 C++ sor Texture Load( char * fname) world Member Control, Animate, Draw Interact, Intersect, Join next Particle avatar ParticleSystem Emit(int n) Avatar ProcessInput() SetCameraTransform() TexturedObject BillBoard DrawIt()
Űrjáték BillBoard TexturedObject Bullet ControlIt Ship DrawIt GameEngine Avatar BillBoard TexturedObject Self ProcessInput ControlIt InteractIt Bullet ControlIt Ship DrawIt InteractIt ControlIt Planet DrawIt AnimateIt Space DrawIt SpaceGame 350 C++ sor
Egy földi lövöldözős játék
Terepek Komplex geometria Bonyolult textúra Nem gondolkodik Nem mozog magasságmező Bonyolult textúra Nem gondolkodik Nem mozog Ütközés detektálás kell Megemeli az objektumokat
Terep geometria z y x z = height(x,y) Magasságmező: Diszkrét minták + Lineáris interpoláció
Diszkrét minták = FF kép Magasság mező Háromszög háló
Háromszögek számának csökkentése: Level of detail
Magasságmező textúrázás: vetítés felülről y x v u
Tereptextúra javítás: Multitextúrázás, Detail map
Terep ütközés detektálás if (height(x,y) > z) Collision! Séta a terepen: Position(x, y) = (x, y, height(x,y) + legsize) z x,y
Bi-lineáris magasságmező interpoláció float Terrain :: Height( float x, float y ) { x += wwidth/2; y += wlength/2; x = x / wwidth * w; y = y / wlength * l; int X = (int)x, Y = (int)y; float h1 = height_field[X][Y] * wheight; float h2 = height_field[X+1][Y] * wheight; float h3 = height_field[X][Y+1] * wheight; float h4 = height_field[X+1][Y+1] * wheight; float xd = x - X; float yd = y - Y; float hx1 = h1 + xd * (h2 - h1); float hx2 = h3 + xd * (h4 - h3); return (hx1 + yd * (hx2 - hx1)); } x,y X,Y
Ég Valamire textúrázott kép: Geometria: dóm, gömb, téglatest Nincs vezérlés, animáció, ütközésdetektálás
GLU kvadratikus felület GLUquadricObj * quadric; // definition quadric = gluNewQuadric( ); gluQuadricTexture(quadric, GL_TRUE); // draw glBindTexture(GL_TEXTURE_2D, sky_texture_id); gluSphere(quadric, sky_radius, 32, 20);
Ellenség poligonháló deformáció Animált geometria Kulcskeretekkel mozgásonként (clip) Áll, fut, támad, meghal poligonháló deformáció Textúrák (animált) AI Ütközés detektálás
Kulcskeret animáció: futás
Interpoláció: Keretek a kulcskeretekből Nem lineáris interpoláció lineáris interpoláció kulcskeretek t
Mit interpoláljunk? Minőségi animáció: Játékok: Newton törvények: C2 C1 spline interpoláció Interpolált keretekre is a fiziológiai tv. betartása: Csont animáció Játékok: Lineáris interpoláció A poligonháló csúcspontjait interpoláljuk Poligonháló deformáció (Mesh morphing)
Minden csúcsra lineáris interpoláció Mesh morphing: t= 0 Idő: t Két közrefogó kulcskeret Minden csúcsra lineáris interpoláció t= 1 Aktuális csúcspontok
Futás poligonháló deformációval + pozíció animáció: position += velocity * dt
Mozgás definíció Clip-ek definíciója kulcskeretekkel Összes clip összes kulcskeretek fájlban: MD2, MD3 Tipikus clip-ek: Run, stand, attack, die, pain, salute, crouch, wave, point, taunt, etc.
Clip-ek Áll 40 kulcskeret Fut 5 kulcskeret Szalutál 11 kulcskeret
Mozgásvezérlés AI motor Idő: t Clip = start, stop keyframe AI state Idő: t Clip = start, stop keyframe Keyframe animation Keyframe-ek MD2 fájlban A háromszög háló csúcspontjai
Ellenség AI Dont Care Escape Chase Attack Dying Dist < 4 && Avatar_angle < 40 Dont Care Escape Dist > 6 Avatar_angle < 20 Dist < 4 && Avatar_angle > 60 Dist < 1 Chase Attack Dist > 1 Collision with the bullet Dying Avatar_angle Avatar
Textúrázás
Golyó Geometria: gömb Textúrázott Nem intelligens Fizikai animáció
Lövedék fizikai animációja t+dt velocity t force, acceleration acceleration = (0, 0, -g) velocity += acceleration * dt position += velocity * dt
Röpül a lövedék: Animate, Draw void Bullet::AnimateIt( float dt ) { acceleration = Vector(0,0,-g); velocity += acceleration * dt; position += velocity * dt; } void Bullet::DrawIt( ) { glPushMatrix( ); glTranslate(position.x, position.y, position.z); glBindTexture(GL_TEXTURE_2D, bullet_texture); gluSphere(quadric, 1.0, 16, 10); glPopMatrix( );
Game Engine struktúra Objektumok Statikus színtér LOD,Strips Geometria Feldolgozás Illumináció feldolgozás Láthatóság Feldolgozás LOD,Strips Cellák+PVS Fénytérképek LOD menedzser Occlusion Culling Illumináció The purpose of preprocessing tools is to prepare data and files for on-line gaming or rendering. All workpackages have such modules. Geometry preprocessing takes individual objects and convert them to hierarchical level of detail data structures. Visibility preprocessing takes static part of the scene as a whole and identifies view cells and potentially visible sets for these view cells. Similarly illumination modules take the static part of the scene and precompute illumination information for it, including precomputed radiance maps, obscurance maps, images for image based rendering etc. During gaming on-line tools and library functions take the prepared data and perform real-time rendering. Note that it is not possible to prepare for everything since the scene is dynamic, so visibility and illumination packages have many methods that do not have preprocessing phases. The on-line tools run under the control of a game engine, therefore these methods should be integrated into the game engine. Dinamikus színtér
Geometria avatar (kamera) Geometria Egyszerű Komplex Feldolgozás Modell Komplex Objektum ”kevés” csúcspont CLOD (folytonos, GPU-n) Háromszög szalag avatar (kamera)
CLOD Strips
CLOD Trees
Grafikus kártya z-buffer Láthatóság Láthatóság Előfeldolgozás Statikus színtér Kd-tree + PVS Geometria View frustrum culling Occlusion Culling Dinamikus színtér The main achievements of the visibility workpackage is the development of tools to efficiently detect the visibility for the static part of the scene and for dynamic objects. The separation of static and dynamic scene parts is necessary since for the static part we can take advantage of preprocessing, while for dynamic objects it is not possible. The visibility workpackage has developed a preprocessing tool for the static part of the scene, which automatically finds view cells and generates the potentially visible set for these view cells. These data are read by the on-line visibility modules, which can find visibility information without scanning the who scene. For dynamic objects, a hardware occlusion query based approach has been developed, which synchronizes the CPU and the GPU in an optimal way, and is thus much faster than previous hardware occlusion queries. Látható? Grafikus kártya z-buffer
Hardware Occlusion Culling Befoglaló doboz raszterizálása Látható legalább 1 pixel? Igen: Bonyolult objektum raszterizálása Nem: semmi dolgunk Rossz!!! Ha sok látható: dupla rajzolás Várni kell az eredményt (csővezeték) Megoldás: Hierarchia ügyes kezelése Koherencia (előző frame)
Coherent Hiearchical Occlusion Culling
Összehasonlítás View Frustrum Culling-gal