Computer graphics/2013-2014/Laboratory 8
Quick links: front; laboratories agenda, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, JOGL template.
MORE ON STATE VARIABLES AND BUFFERS
editQuering the status of state variables
edit(J)OGL allows one to query the status of several state variables such as GL_BLEND, GL_LIGHTING, GL_VIEWPORT, GL_MAX_LIGHTS etc. The methods (functions) which facilitate these operations are: glGetBooleanv, glGetDoublev, glGetFloatv and glGetIntegerv. Each of them receives two variables: the desired state variable and a variable which will hold the result. In addition some state variables have their own methods (functions): glGetLight*(), glGetError(), or glGetPolygonStipple
Links:
Buffers
edit(J)OGL uses four buffers to handle each window (canvas):
- Color buffer - specified by GL_COLOR_BUFFER_BIT is responsible for storing the colors of the pixels
- glColorMask is a nice example of how one can restrict the modification of any of the RGBA color components of the pixels
For instance the following code snippet disables coloring, draws a triangle and enables coloring again:
public void display(GLAutoDrawable canvas) {
GL gl = canvas.getGL();
gl.glClear(GL.GL_COLOR_BUFFER_BIT);
// Disable operations on the Red, Greed, Blue and Alpha pixel values
gl.glColorMask(GL.GL_FALSE, GL.GL_FALSE, GL.GL_FALSE, GL.GL_FALSE);
// Draw a colored triangle here
// Enable coloring
gl.glColorMask(GL.GL_TRUE, GL.GL_TRUE, GL.GL_TRUE, GL.GL_TRUE);
gl.glTranslated(10, 0, 0);
// Draw a colored quad here
gl.glFlush();
}
- Depth buffer - specified by GL_DEPTH_BUFFER_BIT is responsible for storing the distance from each pixel to the observer's position. It is used by the z-buffer algorithm to eliminate the hidden surfaces
- glDepthFunc can be used to specify the function used to compare each incoming pixel depth value with the depth value present in the depth buffer
- the operators which can be used to compare the value stored in the buffer with the current pixel value are: GL_NEVER, GL_ALWAYS, GL_EQUAL, GL_NOTEQUAL, GL_LESS, GL_GREATER, GLGEQUAL. They can be specified by using the glDepthFunc method (function)
- Stencil buffer - specified by GL_STENCIL_BUFFER_BIT is used for generating shadows from multiple light sources, restricting drawing to a particular area, etc. One can draw into the stencil planes using GL drawing primitives, then render geometry and images, using the stencil planes to mask out portions of the screen
- it is activated by using glEnable(GL_STENCIL_TEST)
- there are four main functions for handling it: glClearStencil, glStencilFunc, glStencilOp and glStencilMask.
The following example shows how the stencil buffer can be used to create a custom shaped viewport:
public void display(GLAutoDrawable canvas) {
GL gl = canvas.getGL();
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_STENCIL_BUFFER_BIT);
// Enable stencil test
gl.glEnable(GL.GL_STENCIL_TEST);
// Specify what action to take when either the stencil test or depth test succeeds or fails
gl.glStencilOp(GL.GL_KEEP, GL.GL_KEEP, GL.GL_REPLACE);
// Prepare to write a single bit into the stencil buffer in the area outside the viewport for every rendered pixel
gl.glStencilFunc(GL.GL_ALWAYS, 0x1, 0x1);
// Draw the triangle here
// Only render pixels if the corresponding stencil buffer bit is 1 i.e. inside the previously defined triangle
// If one wishes to draw outside the triangle gl.glStencilFunc(GL.GL_EQUAL, 0x0, 0x1) should be used instead
gl.glStencilFunc(GL.GL_EQUAL, 0x1, 0x1);
// Draw some objects here
gl.glFlush();
}
- Accumulation buffer - specified by GL_ACCUMULATION_BUFFER_BIT is the simplest buffer and is used for effects such as anti-aliasing, motion blur, depth of field, etc.
- it only has one method (function) attached to it: glAccum. Its first parameter can be one of the following:
- GL_ACCUM - adds (in the buffer) the color from the color buffer multiplied by the second parameter
- GL_LOAD - replaces (in the buffer) the color from the color buffer multiplied by the second parameter
- GL_ADD - adds to all the values stored in the buffer the value of the second parameter
- GL_MULT - multiplies all the values stored in the buffer the value of the second parameter
- GL_RETURN - copies the values from the buffer to the color buffer
- it only has one method (function) attached to it: glAccum. Its first parameter can be one of the following:
The following example shows a simple use of the accumulation buffer:
public void display(GLAutoDrawable canvas) {
GL gl = canvas.getGL();
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_ACCUM_BUFFER_BIT);
for (int i=0; i<4; i++)
{
gl.glPushMatrix();
gl.glRotated(i * 10., 0, 1, 0);
// Draw the triangle here
gl.glPopMatrix();
// Acumulate the values in the buffer
gl.glAccum(GL.GL_ACCUM, 1.0/i /*0.25*/);
// Next we just want to see how the images get accumulated by using the front and back buffers
// Switch the current draw buffer to the front buffer
gl.glDrawBuffer(GL.GL_FRONT);
// Transfer the accumulation buffer contents into it
glAccum(GL.GL_RETURN, 1.0/(i+1));
// Swap the draw buffer back
gl.glDrawBuffer(GL.GL_BACK);
}
gl.glAccum(GL.GL_RETURN, 1.0);
gl.glFlush();
}
Links:
OBJECT PICKING
editPicking is used in interactive scenes where the user can select an object or click on it to obtain more information.
Rendering modes
editBefore proceeding it is good to know that there (J)OGL has three possible rendering states. Until now we have only used GL_RENDER (implicitly):
- GL_RENDER - used for rendering the objects in the frame buffer (on the screen)
- GL_SELECT - used for rendering objects in the select buffer - this is what will use for picking
- GL_FEEDBACK - used for displaying a lot of information without actually rendering anything
Changing the rendering mode is accomplished by using the glRenderMode(mode) method (function). It takes as arguments one of the above listed parameters.
Picking
editThe glRenderMode method (function) returns a value which depends on the previously used rendering mode. The value contains the number of visible objects drawn in the buffer. So for example if you restrict drawing in GL_SELECT mode to a window of NxN pixels it will count the number of objects drawn in that particular area. Now if you only use an area of one pixel around your mouse pointer and switch to GL_SELECT mode each time you click a button the previously mentioned method (function) will return to you the number of objects which were drawn (and thus visible) in that region. You therefore can count in this way how many objects you've selected.
The previous is not enough as you need to know which object you clicked on. For this (J)OGL provides us with methods (functions) (glPushName(name) and glPopName()) for naming objects and pushing them in the name stack. Before you draw the object you need to push its name on the stack. In the same way you pop it after drawing is complete:
public class MainFrame implements MouseListener [...]
{
[...]
public void display(GLAutoDrawable canvas) {
GL2 gl = canvas.getGL().getGL2();
[...]
// Push on the name stack the name (id) of the sphere.
gl.glPushName(1);
// Then draw it.
this.drawSphere(gl, glu, radius);
// We are done so pop the name.
gl.glPopName();
[...]
}
[...]
}
NOTE: the name stack is only used in selection mode (GL_SELECT). It will have no effect in the default GL_RENDER mode.
Important: be sure to initialize the name stack by using the glInitNames() method (function) right after entering selection mode.
One should add the following it the code in order to handle picking:
- define a drawing function which draws the objects. Before drawing each object you define it's name by calling glLoadName(name)
- define an event handling function which can handle mouse clicks:
- call a selection handling method (function) each time a click happens
- define a selection method (function) where you need to:
- initialize the selection buffer. It will contain data about selected objects
- save the info about the current view-port
- switch to GL_SELECT mode
- initialize the name stack
- restrict the drawing area around the mouse position (a 1x1 pixels or 5x5 should suffice). Is accomplished by reseting the view-volume (from the Projection Matrix). It can be done by using the gluPickMatrix method (function).
- draw the objects with their names
- get the number of hits, handle them and retrieve the picked object
The following fragment of code exemplifies all of the previous by drawing a sphere and allowing the user to pick it.
public class Main
{
[...]
// Variables for storing the mouse coordinates when a click event occurs.
private int mouseX, mouseY;
// Default mode is GL_RENDER;
private int mode = GL2.GL_RENDER;
public void display(GLAutoDrawable canvas) {
GL2 gl = canvas.getGL().getGL2();
if (mode == GL2.GL_RENDER)
{
// only clear the buffers when in GL_RENDER mode. Avoids flickering
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
this.drawScene(gl);
}
else
{
this.pickHandle (gl, this.mouseX, this.mouseY);
}
}
public void mousePressed(MouseEvent me) {
mouseX = me.getX();
mouseY = me.getY();
mode = GL2.GL_SELECT;
}
private void pickHandle(GL gl, int x, int y) {
//Calculate the select buffer capacity and allocate data if necessary
final int bufferSize = 10;
final int capacity = BufferUtil.SIZEOF_INT * bufferSize;
IntBuffer selectBuffer = BufferUtil.newIntBuffer(capacity);
// Send the select buffer to (J)OGL and use select mode to track object hits.
gl.glSelectBuffer(selectBuffer.capacity(), selectBuffer);
gl.glRenderMode(GL2.GL_SELECT);
// Initialize the name stack.
gl.glInitNames();
// Get the viewport.
int[] viewport = new int[4];
gl.glGetIntegerv(GL2.GL_VIEWPORT, viewport, 0);
// Get the projection matrix.
float[] projection = new float[16];
gl.glGetFloatv(GL2.GL_PROJECTION_MATRIX, projection, 0);
// Switch to the projection matrix mode.
gl.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
// Save the current projection matrix.
gl.glPushMatrix();
// Reset the projection matrix.
gl.glLoadIdentity();
// Restrict region to pick object only in this region.
// Remember to adjust the y value correctly by taking into consideration the different coordinate systems used by AWT and (J)OGL.
glu.gluPickMatrix(x, viewport[3] - y, 1, 1, viewport, 0);
//Load the projection matrix
gl.glMultMatrixf(projection, 0);
// Or redefine the perspective again.
// glu.gluPerspective ( 38.0, (float) screenWidth / (float) screenHeight, 0.1, z_far );
//Go back to modelview matrix for rendering.
gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
// Render the scene. Note we are in GL_SELECT mode now.
this.drawScene(gl);
gl.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
// Restore the saved projection matrix.
gl.glPopMatrix();
// Select the modelview matrix.
gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
// Return to GL_RENDER mode.
final int hits = gl.glRenderMode(GL.GL_RENDER);
mode = GL2.GL_RENDER;
// Process the hits.
processHits(hits, selectBuffer);
}
private void drawScene(GL gl)
{
gl.glLoadIdentity();
// Remember to either add these methods or use gluLookAt(...) here.
// aimCamera(gl, glu);
// moveCamera();
if (mode == GL2.GL_SELECT)
{
// Push on the name stack the name (id) of the sphere.
gl.glPushName(1);
}
// Then draw it.
this.drawSphere(gl, glu, 5);
if (mode == GL2.GL_SELECT)
{
// We are done so pop the name.
gl.glPopName();
}
}
// Retrieved from: http://user.cs.tu-berlin.de/~schabby/PickingExample.java
// It extracts the data in the selection buffer and writes it on the console.
private void processHits(int hits, IntBuffer buffer) {
int offset = 0;
int names;
float z1, z2;
System.out.println("---------------------------------");
System.out.println(" HITS: " + hits);
for (int i = 0; i < hits; i++) {
System.out.println("- - - - - - - - - - - -");
System.out.println(" hit: " + (i + 1));
names = buffer.get(offset);
offset++;
z1 = (float) buffer.get(offset) / 0x7fffffff;
offset++;
z2 = (float) buffer.get(offset) / 0x7fffffff;
offset++;
System.out.println(" number of names: " + names);
System.out.println(" z1: " + z1);
System.out.println(" z2: " + z2);
System.out.println(" names: ");
for (int j = 0; j < names; j++) {
System.out.print(" " + buffer.get(offset));
if (j == (names - 1)) {
System.out.println("<-");
} else {
System.out.println();
}
offset++;
}
System.out.println("- - - - - - - - - - - -");
}
System.out.println("--------------------------------- ");
}
[...]
}
Links:
API
edit- javax.media.opengl:
- GL:
- glClearStencil(int)
- glStencilFunc(int, int, int)
- glStencilOp(int, int, int)
- glStencilMasc(int)
- glAccum(int, float)
- glRenderMode(int)
- glPushName(int)
- glPopName()
- glGetBooleanv(int, bytev, int)
- glGetDoublev(int, doublev, int)
- glGetFloatv(int, floatv, int)
- glGetIntegerv(int, intv, int)
- glInitNames()
- glSelectBuffer(int, java.nio.IntBuffer)
- glMultMatrixf(floatv, int)
- GL:
- javax.media.opengl.glu:
Exercise
edit- Create a hexagon shaped viewport. Make it rotate.
- Starting from the exercise in the previous laboratory add support for picking the three spheres present in the scene.