JOGL and DepthBuffer

Recently I got the chance to use JOGL (Java’s Binding for OpenGL) for a project. This project is a 3D sketch tool with a standard Java Swing UI, and inside the UI it contains a JPanel in the middle, which contains a JOGL Canvas.

With Java, I handled all the mouse clicked and dragged events using the traditional MouseListener and MouseMotionListener, and assigned them to the canvas. All of these worked great until I wanted to access the depth buffer. Since this is a 3D sketch tool, I wanted to map my 2D sketches onto some 3D surface inside the 3D world, so I went for the approach of gluUnproject with the depth value obtained using the 2D screen coordinates of the sketch.

So intuitively, I saved the 2D coordinates of the sketch during the mouseDragged event, and I accessed the depth buffer and did the projection in the mouseReleased event, after each sketch is drawn. However, the problem came here. No matter what I did, the buffer keeps returning 0.0 for all x and y coordinates.

After spending some time and digging up this problem, I found that it was threading issue between the JOGL rendering thread and the application thread. If you try to get the viewport, model view, and projection matrix outside the rendering thread, chances are you’ll also get a bunch 0’s, and this is exactly what happens with the depth buffer. So in order to avoid this, you’ll have to save all the mouse inputs, and process them inside the rendering thread if you want to do any rendering-related things to them.

One way to read the depth buffer is through the use of glReadPixels(), using 2D screen coordinates (p in this case) to get the depth of a particular location in the Depth Buffer and a FloatBuffer to store the resulting depth value:

FloatBuffer winZ = FloatBuffer.allocate(1);
gl.glReadPixels(p.getX(),p.getY(), 1, 1, GL.GL_DEPTH_COMPONENT, GL.GL_FLOAT, winZ);

gluUnproject also uses 2D screen coordinates as inputs, along with a depth value as mentioned before. This depth value is usually between 0.0 to 1.0, unless you specify your own range using glWritePixels or shaders.

Finally, here is the code for projecting 2D points in screen coordinates to 3D points in world coordinates. Because of the use of depth buffer, hence it is used to project these 2D points onto some 3D object surface:

public void projectSketch(){
	 //3D point unprojected from 2D screen coordiantes to world coordinates
	DoubleBuffer unProjectedPoint = DoubleBuffer.allocate(4);
	//Z-value fetching the Z-buffer
	FloatBuffer winZ = FloatBuffer.allocate(1); 
	ArrayList points3D = new ArrayList();
	for(Point p : sketchPointsIn2D){
		//Read Z Buffer for each sketch point
		gl.glReadPixels(p.getX(), p.getY(), 
				1, 1, GL.GL_DEPTH_COMPONENT, GL.GL_FLOAT, winZ);
		//Unproject 2D screen coordinates to 3D world coordinates using depth value
		glu.gluUnProject(p.getX(), p.getY(), winZ.get(0) - 0.0001, 
						modelMatrix, projectionMatrix, viewport, unProjectedPoint);
		points3D.add(new Vector3D((float) unProjectedPoint.get(0), 
					(float) unProjectedPoint.get(1), (float) unProjectedPoint.get(2)));
	}
	sketchPoints.add(points3D);		
		
	sketchPointsIn2D.clear();		
	sketchProcessStatus = SketchProcessStatus.NONE;
}

Leave a Reply

Your email address will not be published. Required fields are marked *