Jogl is a Java programming language binding for the OpenGL 3D graphics API. It supports integration with the Java platform's AWT and Swing widget sets while providing a minimal and easy-to-use API that handles many of the issues associated with building multithreaded OpenGL applications. Jogl provides access to the latest OpenGL routines (OpenGL 2.0 with vendor extensions) as well as platform-independent access to hardware-accelerated offscreen rendering ("pbuffers"). Jogl also provides some of the most popular features introduced by other Java bindings for OpenGL like GL4Java, LWJGL and Magician, including a composable pipeline model which can provide faster debugging for Java-based OpenGL applications than the analogous C program.
Jogl was designed for the most recent version of the Java platform and for this reason supports only J2SE 1.4 and later. It also only supports truecolor (15 bits per pixel and higher) rendering; it does not support color-indexed modes. Certain areas of the public APIs are more restrictive than in other bindings; for example, the GLCanvas and GLJPanel classes are final, unlike in GL4Java, and the GLContext class is no longer exposed in the public API. These changes have been made to keep the public API simple and because most of the programming errors that have been seen with earlier Java/OpenGL interfaces, in particular GL4Java, have been related to subclassing the OpenGL widget classes and performing manual OpenGL context management. Several complex and leading-edge OpenGL demonstrations have been successfully ported from C/C++ to Jogl without needing direct access to any of these APIs. However, all of these classes and concepts are accessible at the Java programming language level in implementation packages, and in fact the Jogl binding is itself written almost completely in the Java programming language. There are roughly 150 lines of handwritten C code in the entire Jogl source base (100 of which work around bugs in older OpenGL drivers on Windows); the rest of the native code is autogenerated during the build process by a new tool called GlueGen, the source code of which is in the Jogl source tree. Documentation for GlueGen is forthcoming.
Jogl provides two basic widgets into which OpenGL rendering can be performed. The GLCanvas is a heavyweight AWT widget which supports hardware acceleration and which is intended to be the primary widget used by applications. The GLJPanel is a fully Swing-compatible lightweight widget which supports hardware acceleration but which is not as fast as the GLCanvas because it reads back the frame buffer in order to draw it using Java2D. The GLJPanel is intended to provide 100% correct Swing integration in the circumstances where a GLCanvas can not be used. See this article on The Swing Connection for more information about mixing lightweight and heavyweight widgets. See also the section on "Heavyweight and Lightweight Issues" below.
Both the GLCanvas and GLJPanel implement a common interface called GLDrawable so applications can switch between them with minimal code changes. The GLDrawable interface provides
display()
method for forcing OpenGL rendering to
be performed
setRenderingThread()
,
setNoAutoRedrawMode()
) for controlling the
multithreading behavior of the widget
GLCanvas and GLJPanel instances are created using the factory methods in GLDrawableFactory. These factory methods allow the user to request a certain set of OpenGL parameters in the form of a GLCapabilities object, to customize the format selection algorithm by specifying a GLCapabilitiesChooser, to share textures and display lists with other GLDrawables, and to specify the display device on which the GLDrawable will be created.
A GLCapabilities object specifies the OpenGL parameters for a newly-created widget, such as the color, alpha,, z-buffer and accumulation buffer bit depths and whether the widget is double-buffered. The default capabilities are loosely specified but provide for truecolor RGB, a reasonably large depth buffer, double-buffered, with no alpha, stencil, or accumulation buffers.
An application can override the default pixel format selection algorithm by providing a GLCapabilitiesChooser to the GLDrawableFactory. The chooseCapabilities method will be called with all of the available pixel formats as an array of GLCapabilities objects, as well as the index indicating the window system's recommended choice; it should return an integer index into this array. The DefaultGLCapabilitiesChooser uses the window system's recommendation when it is available, and otherwise attempts to use a platform-independent selection algorithm.
The GLJPanel can be made non-opaque according to Swing's rendering model, so it can act as an overlay to other Swing or Java2D drawing. In order to enable this, set up your GLCapabilities object with a non-zero alpha depth (a common value is 8 bits) and call setOpaque(false) on the GLJPanel once it has been created. Java2D rendering underneath it will then show through areas where OpenGL has produced an alpha value less than 1.0. See the JGears and JRefract demos for examples of how to use this functionality.
Applications implement the GLEventListener interface to perform OpenGL drawing. When the methods of the GLEventListener are called, the underlying OpenGL context associated with the drawable is already current. The listener fetches the GL object out of the GLDrawable and begins to perform rendering.
The init()
method is called when a new OpenGL context is
created for the given GLDrawable. Any display lists or textures used
during the application's normal rendering loop can be safely
initialized in init()
. It is important to note that
because the underlying AWT window may be destroyed and recreated while
using the same GLCanvas and GLEventListener, the GLEventListener's
init()
method may be called more than once during the
lifetime of the application. The init() method should therefore be
kept as short as possible and only contain the OpenGL initialization
required for the display()
method to run properly. It is
the responsibility of the application to keep track of how its various
OpenGL contexts share display lists, textures and other OpenGL objects
so they can be either be reinitialized or so that reinitialization can
be skipped when the init()
callback is called.
The display()
method is called to perform per-frame
rendering. The reshape()
method is called when the
drawable has been resized; the default implementation automatically
resizes the OpenGL viewport so often it is not necessary to do any
work in this method. The displayChanged()
method is
designed to allow applications to support on-the-fly screen mode
switching, but support for this is not yet implemented so the body of
this method should remain empty.
It is strongly recommended that applications always refetch the GL and
GLU objects out of the GLDrawable upon each call to the
init()
, display()
and reshape()
methods and pass the GL object down on the stack to any drawing
routines, as opposed to storing the GL in a field and referencing it
from there. The reason is that multithreading issues inherent to the
AWT toolkit make it difficult to reason about which threads certain
operations are occurring on, and if the GL object is stored in a field
it is unfortunately too easy to accidentally make OpenGL calls from a
thread that does not have a current context. This will usually cause
the application to crash. For more information please see the section
on multithreading.
Jogl supports the "composable pipeline" paradigm introduced by the
Magician Java binding for OpenGL. The DebugGL pipeline calls
glGetError
after each OpenGL call, reporting any errors
found. It can greatly speed up development time because of its
fine-grained error checking as opposed to the manual error checking
usually required in OpenGL programs written in C. The TraceGL prints
logging information upon each OpenGL call and is helpful when an
application crash makes it difficult to see where the error occurred.
To use these pipelines, call GLDrawable.setGL
at the
beginning of the init
method in your GLEventListener. For
example,
class MyListener implements GLEventListener { public void init(GLDrawable drawable) { drawable.setGL(new DebugGL(drawable.getGL())); // ... } // ... }
As mentioned above, JOGL supplies both a heavyweight (GLCanvas) and a lightweight (GLJPanel) widget to be able to provide the fastest possible performance for applications which need it as well as 100% correct Swing integration, again for applications which need it. The GLCanvas provides much higher performance than the GLJPanel in nearly all situations and can be used in almost every kind of application except those using JInternalFrames. Please see the Swing Connection article mentioned above for details on mixing heavyweight and lightweight widgets. A couple of common pitfalls are described here.
When using JPopupMenus or Swing tool tips in conjunction with the
GLCanvas, it is necessary to disable the use of lightweight widgets
for the popups. See the methods
ToolTipManager.setLightWeightPopupEnabled
,
JPopupMenu.setLightWeightPopupEnabled
, and
JPopupMenu.setDefaultLightWeightPopupEnabled
.
There are occasionally problems with certain LayoutManagers and component configurations where if a GLCanvas is placed in the middle of a set of lightweight widgets then it may only grow and never shrink. These issues are documented somewhat in JOGL Issue 135 and most recently in the thread "Resize behaviour" in the JOGL forum. The root cause is behavior of the Canvas, and in particular its ComponentPeer. The implementation of getPreferredSize() calls getMinimumSize() and getMinimumSize() turns around and calls Component.getSize(). This effectively means that the Canvas will report its preferred size as being as large as the component has ever been. For some layout managers this doesn't seem to matter, but for others like the BoxLayout it does. See the test case attached to Issue 135 for an example. Replacing the GLCanvas with an ordinary Canvas yields the same behavior.
One suggestion was to override getPreferredSize() so that if a preferred size has not been set by the user, to default to (0, 0). This works fine for some test cases but breaks all of the other JOGL demos because they use a different LayoutManager. There appear to be a lot of interactions between heavyweight vs. lightweight widgets and layout managers. One experiment which was done was to override setSize() in GLCanvas to update the preferred size. This works down to the size specified by the user; if the window is resized any smeller the same problem appears. If reshape() (the base routine of setSize(), setBounds(), etc.) is changed to do the same thing, the demo breaks in the same way it originally did. Therefore this solution is fragile because it isn't clear which of these methods are used internally by the AWT and for what purposes.
There are two possible solutions, both application-specific. The best
and most portable appears to be to put the GLCanvas into a JPanel and
set the JPanel's preferred size to (0, 0). The JPanel will cause this
constraint to be enforced on its contained GLCanvas. The other
workaround is to call setPreferredSize(new Dimension(0,
0))
on a newly-created GLCanvas; this method is new in 1.5.
Another issue that occasionally arises on Windows is flickering during
live resizing of a GLCanvas. This is caused by the AWT's repainting
the background of the Canvas and can not be overridden on a per-Canvas
basis, for example when subclassing Canvas into GLCanvas. The
repainting of the background of Canvases on Windows can be disabled by
specifying the system property
-Dsun.awt.noerasebackground=true
. Whether to specify this
flag depends on the application and should not be done universally,
but instead on a case-by-case basis. Some more detail is in the thread
"TIP: JOGL
+ Swing flicker" in the JOGL forum.
Jogl was designed to interoperate with the AWT, an inherently multithreaded GUI toolkit. OpenGL, in contrast, was originally designed in single-threaded C programming environments. For this reason Jogl provides a framework in which it is possible to write correct multithreaded OpenGL applications using the GLEventListener paradigm.
If an application written using Jogl interacts in any way with the mouse or keyboard, the AWT is processing these events and the multithreaded aspects of the program must be considered.
OpenGL applications usually behave in one of two ways: either they
repaint only on demand, for example when mouse input comes in, or they
repaint continually, regardless of whether user input is coming in. In
the repaint-on-demand model, the application can merely call
GLDrawable.display()
manually at the end of the mouse or
keyboard listener to cause repainting to be done. Alternatively if the
application knows the concrete type of the GLDrawable it can call
repaint() to have the painting scheduled for a later time.
In the continuous repaint model, the application typically has a main
loop which is calling GLDrawable.display()
repeatedly, or
is using the Animator class, which does this internally. In both of
these cases the OpenGL rendering will be done on this thread rather
than the internal AWT event queue thread which dispatches mouse and
keyboard events.
Both of these models (repaint-on-demand and repaint continually) still
require the user to think about which thread keyboard and mouse events
are coming in on, and which thread is performing the OpenGL rendering.
OpenGL rendering may not occur directly inside the mouse or
keyboard handlers, because the OpenGL context for the drawable is not
current at this point (hence the warning about storing a GL object in
a field, where it can be fetched and accidentally used by another
thread). However, a mouse or keyboard listener may invoke
GLDrawable.display()
.
It is generally recommended that applications perform as little work
as possible inside their mouse and keyboard handlers to keep the GUI
responsive. However, since OpenGL commands can not be run from
directly within the mouse or keyboard event listener, the best
practice is to store off state when the listener is entered and
retrieve this state during the next call to
GLEventListener.display()
.
Furthermore, it is recommended that if there are long computational
sequences in the GLEventListener's display
method which
reference variables which may be being simultaneously modified by the
AWT thread (mouse and keyboard listeners) that copies of these
variables be made upon entry to display
and these copies
be referenced throughout display() and the methods it calls. This will
prevent the values from changing while the OpenGL rendering is being
performed. Errors of this kind show up in many ways, including certain
kinds of flickering of the rendered image as certain pieces of objects
are rendered in one place and other pieces are rendered elsewhere in
the scene. Restructuring the display() method as described has solved
all instances of this kind of error that have been seen with Jogl to
date.
Prior to Jogl 1.1 b10, the Jogl library attempted to give applications
strict control over which thread or threads performed OpenGL
rendering. The setRenderingThread()
,
setNoAutoRedrawMode()
and display()
APIs
were originally designed to allow the application to create its own
animation thread and avoid OpenGL context switching on platforms that
supported it. Unfortunately, serious stability issues caused by
multithreading bugs in either vendors' OpenGL drivers or in the Java
platform implementation have arisen on three of Jogl's major supported
platforms: Windows, Linux and Mac OS X. In order to address these
bugs, the threading model in Jogl 1.1 b10 and later has changed.
All GLEventListener callbacks and other internal OpenGL context
management are now performed on one thread. (In the current
implementation, this thread is the AWT event queue thread, which is a
thread internal to the implementation of the AWT and which is always
present when the AWT is being used. Future versions of Jogl may change
the thread on which the OpenGL work is performed.) When the
GLDrawable.display()
method is called from user code, it
now performs the work synchronously on the AWT event queue thread,
even if the calling thread is a different thread. The
setRenderingThread()
optimization is now a no-op. The
setNoAutoRedraw()
API still works as previously
advertised, though now that all work is done on the AWT event queue
thread it no longer needs to be used in most cases. (It was previously
useful for working around certain kinds of OpenGL driver bugs.)
Most applications will not see a change in behavior from this change
in the Jogl implementation. Applications which use thread-local
storage or complex multithreading and synchronization may see a change
in their control flow requiring code changes. While it is strongly
recommended to change such applications to work under the new
threading model, the old threading model can be used by specifying the
system property -Djogl.1thread=auto
or
-Djogl.1thread=false
. The "auto" setting is equivalent to
the behavior in 1.1 b09 and before, where on ATI cards the
single-threaded mode would be used. The "false' setting is equivalent
to disabling the single-threaded mode. "true" is now the default
setting.
Jogl exposes hardware-accelerated offscreen rendering (pbuffers) with a minimal and platform-agnostic API. Several recent demos have been successfully ported from C/C++ to Java using Jogl's pbuffer APIs. However, the pbuffer support in Jogl remains one of the more experimental aspects of the package and the APIs may need to change in the future.
To create a pbuffer, create a GLCanvas and (assuming it reports that
it can create an offscreen drawable) make a pbuffer using the
createOffscreenDrawable
API. Because of the multithreaded
nature of the AWT, the pbuffer is actually created lazily. However,
even if multiple pbuffers are created, and the order in which they are
rendered is significant, handling the lazy instantiation can be
straightforward: the display(GLDrawable) method of one pbuffer's
GLEventListener can directly call another pbuffer's display() method.
See the source code for the Jogl demonstrations such as the
ProceduralTexturePhysics demo and the HDR demo for examples of this
usage.
Additionally, pbuffers are only created when the parent GLCanvas's display(), init(), or reshape() methods are called; in other words, it may be necessary to manually "prime" the GLCanvas by calling display() on it until it creates all of its requested pbuffers. Again, please see the demonstrations for concrete examples of this. We hope that it may be possible to hide many of these details in the future.
A pbuffer is used by calling its display() method. Rendering, as always, occurs while the pbuffer's OpenGL context is current. There are render-to-texture options that can be specified in the GLCapabilities for the pbuffer which can make it easier to operate upon the resulting pixels. These APIs are however highly experimental and not yet implemented on all platforms.
Jogl contains support for the GLU (OpenGL Utility Library) version
1.3. Jogl originally supported GLU by wrapping the C version of the
APIs, but over time, and thanks to the contributions of several
individuals, it now uses a pure-Java version of SGI's GLU library. The
pure Java port is enabled by default, and addresses stability issues
on certain Linux distributions as well as the lack of native GLU 1.3
support on the Windows platform. In case of problems with the Java
port, the C version of the GLU library may be used by specifying the
system property -Djogl.glu.nojava
on the command
line. All of the same functionality is exposed with both the Java and
C versions of the GLU library; currently NURBS support is the only
missing feature on both sides. If you run into problems with the Java
port of the GLU library please file a bug using the Issue Tracker on
the Jogl home page.
The JOGL forum on javagaming.org is the best place to ask questions about the library. Many users, as well as the Jogl developers, read this forum frequently, and the archived threads contain a lot of useful information (which still needs to be distilled into documentation).
The JOGL demos provide several examples of usage of the library.
Pepijn Van Eeckhoudt has done JOGL ports of many of the the NeHe demos. These are small examples of various pieces of OpenGL functionality. See also the NeHe web site.
Pepijn also did a JOGL port of Paolo Martella's GLExcess demo. To see the news update about this port, go to the main GLExcess site and scroll down.
Gregory Pierce's introduction to JOGL is a useful tutorial on starting to use the JOGL library.
For release information about the JOGL library, please see the JOGL Release Information thread on the JOGL forum on javagaming.org.
Please post on the JOGL forum if you have a resource you'd like to add to this documentation.
The following issues, among others, are outstanding on all platforms:
For correct operation, it is necessary to specify the system property
-Dsun.java2d.noddraw=true
when running JOGL applications
on Windows; this system property disables the use of DirectDraw by
Java2D. There are driver-level incompatibilities between DirectDraw
and OpenGL which manifest themselves as application crashes, poor
performance, bad flickering, and other artifacts. This poor behavior
may exhibit itself when OpenGL and DirectDraw are simply used in the
same application, not even just in the same window, so disabling
Java2D's DirectDraw pipeline and forcing it to use its GDI pipeline is
the only way to work around these issues. Java Web Start applications
may set this system property by adding the following line to the
<resources>
section of the JNLP file:
<property name="sun.java2d.noddraw" value="true"/>
JOGL currently does not interoperate well with the OpenGL pipeline for Java2D available in JDK 5.0 and later. We will address this in a future JOGL release and plan to have better interoperability by the time JDK 6.0 is shipped.
There is a serious memory leak in ATI's OpenGL drivers which is
exhibited on Windows XP on Mobility Radeon 9700 hardware. It's
possible it will be present on other hardware as well though it was
not reproducible at the time of this writing on desktop Radeon
hardware or older ATI mobile chips. The bug is documented in JOGL Issue
166 and a bug has been filed with ATI. You can confirm the
presence of the bug either with the test case in that bug report or by
simply running the Gears demo; if the process size grows over time in
the Task Manager, the memory leak is present on your hardware. For the
time being, you can work around this memory leak by specifying the
system property -Djogl.GLContext.nofree
on the command
line when launching your JOGL applications. There is no good
general-purpose workaround for this bug which behaves well on all
hardware.
No outstanding issues at this time.
There are some problems with visual artifacts and stability problems with some of the Jogl demos on Mac OS X. It appears that at least some of these problems are due to bugs in Apple's OpenGL support. Bugs have been filed about these problems and it is hoped they will be addressed in the near future.
The Mac OS X port of Jogl, in particular the GL interface and its implementation, can be used either with the provided GLCanvas widget or with the Cocoa NSOpenGLView. In order to use it with Cocoa the following steps should be taken:
net.java.games.jogl.impl.macosx.MacOSXGLImpl
using the
public constructor taking no arguments.
MacOSXGLImpl.resetGLFunctionAvailability()
.
The following issues remain with the Mac OS X port:
JOGL's version history can be found online in the "JOGL Release Information" thread in the JOGL forum. Comments about the 1.1 release train are in the thread "JOGL 1.1 released".