Το κείμενο του εγγράφου

These notes describe an active rendering framework for JOGLwhich updates and renders a game (or any animated application)at a reliable, near constant, framerate. It also allows greatercontrol over the application’s execution behavior, such as how itpauses, resumes, and deals with resizing.These notes form part of a tutorial held at CyberGames 2007. Themain aim is to introduce JOGL and OpenGL to an audienceunfamiliar with 3D graphics through the means of a simple 3Dgame. The tutorial includes pointers to numerous sources offurther information.

JOGL, OpenGL, active rendering, animation, framework.1. INTRODUCTIONJOGL is an open-source technology initiated by the GameTechnology Group at Sun Microsystems in 2003(https://jogl.dev.java.net/). JOGL provides full access to the APIsin the OpenGL 2.0 specification (http://www.opengl.org/), as wellas vendor extensions, and can be combined with Java’s AWT andSwing GUI components. It supports both the main shaderlanguages, GLSL and NVIDIA’s Cg.JOGL has the same focus as OpenGL, on 2D and 3D rendering. Itdoesn’t include support for gaming elements such as sound orinput devices, which are dealt with by other Java APIs.

Permission to make digital or hard copies of all or part of this work forpersonal or classroom use is granted without fee provided that copies arenot made or distributed for profit or commercial advantage and that copiesbear this notice and the full citation on the first page. To copy otherwise,or republish, to post on servers or to redistribute to lists, requires priorspecific permission and/or a fee.CyberGames 2007, September 10–11, 2007, Manchester, UK.Copyright 2007 ??…$??.

The active rendering framework described here utilizes JOGLfeatures for accessing the application’s drawing surface andcontext (OpenGL’s internal state) [2]. The surface is typically asubclass of AWT’s Canvas, and is manipulated with a dedicatedrendering thread, as illustrated by Figure 1.

Figure 1. An active rendering application.

The rendering thread’s pseudocode:

initialize rendering;while game isRunning {update game state;render scene;put the scene onto the canvas;

sleep a while;maybe do game updates without rendering them;}discard the rendering context;exit;

The tricky aspect of this code is that OpenGL must bemanipulated from within the rendering thread only. Any mouse,key, or window events must be processed there, rather than inseparate listeners.The principal advantage of the active rendering approach is that itallows the programmer to more directly control the application’sexecution. For example, it’s straightforward to add code thatsuspends updates when the application is iconified or deactivated(i.e., when it’s not the topmost window). Also, access to thetiming code inside the animation loop permits a separation offrame rate processing from application updates.

2. AN ACTIVE RENDERING EXAMPLEA simple application using the active rendering framework isshown in Figure 2. The program, called CubeGL, rotates a multi-colored cube around its x-, y-, and z- axes.

Figure 2. The CubeGL application.

The class diagrams for CubeGL are given in Figure 3.

Figure 3. Class diagrams for CubeGL with active rendering.

CubeGL creates the GUI, embedding the threaded canvas,CubeCanvasGL, inside a JPanel. It also captures window eventsand component resizes and calls methods in CubeCanvasGL todeal with them.

2.1 Thread RenderingThe run() method in CubeCanvasGL is based on the pseudocodegiven earlier:

An OpenGL display list acts as a storage space for rendering andstate commands. The commands are compiled into an optimizedform, which allows them to be executed more quickly. Thebenefit of a display list is that it can be called multiple timeswithout OpenGL having to recompile the commands, therebysaving processing time.The cubeDList display list created in initRender() groups thecommands that draw the cube. This part of initRender() will varyfrom application to application.makeContentCurrent() connects OpenGL’s graphic context to thethread:

makeCurrentContext() calls GLContext.makeCurrent(), whichshould immediately succeed, since no other thread is using thecontext. The while-loop around the call is extra protection, sincethe application will crash if OpenGL commands are calledwithout the thread holding the current context.resizeView() sets the OpenGL camera viewport dimensions, andspecifies a perspective view into the scene:

The GL.glViewport() call defines the size of 3D drawing window(viewport) in terms of a lower-left corner (x, y), width, andheight.The matrix mode is switched to PROJECTION (OpenGL’sprojection matrix) so the mapping from the 3D scene to the 2Dscreen can be specified. GL.glLoadIdentity() resets the matrix,and GLU.gluPerspective() creates a mapping with perspectiveeffects (which mirrors what happens in a real-world camera).FOV is the camera’s viewing angle.

2.3 The Rendering LooprenderLoop() implements the while-loop in the active renderingpseudocode:

while game isRunning {update game state;render scene;put the scene onto the canvas;

sleep a while;maybe do game updates without rendering them;}

The loop is complicated by having to calculate the amount of timeit takes to do the update-render pair. The sleep time that followsmust be adjusted so the time to complete the iteration is as closeto the desired frame rate as possible.If an update-render takes too long, it may be necessary to carryout some game updates without rendering their changes. Theresult is a game that runs close to the requested frame rate, byskipping the time-consuming rendering of the updates.The timing code distinguishes between two rates: the actual framerate that measures the number of renders/second (FPS for short),and the update rate that measures the number of updates/second(UPS).FPS and UPS aren’t the same. It’s quite possible for a slowplatform to limit the FPS value, but the program performsadditional updates (without rendering) so that its UPS number isclose to the requested frame rate.This separation of FPS and UPS makes the animation loop morecomplicated, but it’s one of the standard ways to create reliableanimations. It’s especially good for games where the hardware isunable to render at the requested frame rate.The following is the code for renderLoop():

// constantsprivate static final int NO_DELAYS_PER_YIELD = 16;/* Number of iterations with a sleep delayof 0 ms before the animation threadyields to other running threads. */

private static int MAX_RENDER_SKIPS = 5;/* no. of renders that can be skipped inany one animation loop; i.e. the gamesstate is updated but not rendered. */

// globalsprivate long prevStatsTime;private long gameStartTime;private long rendersSkipped = 0L;

private long period;// period between drawing in nanosecsprivate volatile boolean isRunning = false;// used to stop the animation thread

/* If the rendering is taking too long, thenupdate the game state without renderingit, to get the UPS nearer to therequired frame rate. */int skips = 0;while((excess > period) &&(skips < MAX_RENDER_SKIPS)) {excess -= period;gameUpdate();// update state but don’t renderskips++;}rendersSkipped += skips;

context.release();}} // end of renderLoop()

The “sleep a while” code in the loop is complicated bydealing with inaccuracies in Thread.sleep(). sleep()’s executiontime is measured, and the error (stored in overSleepTime) adjuststhe sleeping period in the next iteration.The if-test involves Thread.yield():

if (++noDelays >= NO_DELAYS_PER_YIELD) {Thread.yield();noDelays = 0;}

It ensures that other threads get a chance to execute if theanimation loop hasn’t slept for a while.renderLoop calls makeContentCurrent() and GLContext.release()at the start and end of each rendering iteration. This allows theJRE some time to process AWT events.gameUpdate() contains any calculations that affect gameplay,which for CubeGL are only the x-, y-, and z- rotations used by thecube.

2.4 Rendering the SceneScene generation is carried out by renderScene():

if (gameOver) //report that the game is overSystem.out.println("Game Over");} // end of renderScene()

renderScene() checks that the thread still has the current context;if it doesn’t, the application exits. A more robust response wouldbe to try to regain the context by callingGLContext.makeCurrent() again, reinitializing the scene, andrestarting the animation loop.renderScene() calls resizeView() to update the OpenGL view ifthe window has been resized (i.e. when isResized is true).The matrix mode is switched to MODELVIEW so OpenGL’smodel-view matrix can be utilized. It defines the scene’scoordinate system, used when positioning or moving 3D objects.After the new rotations have been applied to the worldcoordinates, the cube is drawn via its display list. This part ofrenderScene() will vary from application to application.The method finishes by checking the gameOver boolean, andprinting a simple message. In a real game, the output would bemore complicated.3. MORE INFORMATIONThe principal source for JOGL help is its forum site athttp://www.javagaming.org/forums/index.php?board=25.0.The NeHe site (http://nehe.gamedev.net/) is an excellent place tostart learning OpenGL. It contains an extensive collection oftutorials, articles, examples, and other programming materials.There are a growing number of textbooks on OpenGL (e.g. [1, 3,4], with a comprehensive list available athttp://www.opengl.org/documentation/books.html.4. REFERENCES[1] Angel, E. OpenGL: A Primer, Pearson, 2005, 2nd ed.,http://www.cs.unm.edu/~angel/