Write names to additional buffer similar to z-buffer

Hi there,

is it possible to write the names used on the namestack to a buffer object.

Let the z-Buffer be something like

0 0 0 0
1 0 1 1
1 1 1 1

and I used to “named” polygons for this rendering (names: 1 and 2).
I’d like the new buffer to be:

1 1 1 1
2 1 2 2
2 2 2 2

so I could easily say which is shown at pixel x,y. My aim is to avoid the picking-routine. I’ll apply this technique only with very static scenes.

Thanks for any answer
Klemens

Yup, but instead of ‘names’, use color-coding as a first rendering-pass:

  • disable all textures/lighting/shaders that change the fragment-colors
  • give every group-of-polys a unique color (glColor3ub)
  • grab the pixel-color at X,Y and multiply the R,G,B back to your ‘name’
    you can fake a name-stack with a max depth of 3, by limiting yourself to 256 items at each level

Now clear the scene, render it with textures, lighting and whatnot, like you’d normally do.

If you want to use it in a ‘buffer object’, you might want to render-to-texture, and fetch the values using a fancy shader.

Thanks for your reply. That’s what I wantet.

I think I will render into a pBuffer. The color-coding without shading and light was my idea too. Can I get the pixel-values of the pBuffer via pBuffer.getGL().glReadPixels(…)?

I’m using GLJPanel without an animator, so I’m not always aware of sporadic repaint => display-calls. So I don’t want to use mutexes which would block my on screen rendering.

Thanks so far.
Just little steps are missing

I have no experience with PBuffers unfortunately. I guess you can call glReadPixels() on them. If you’d go for RTT, you can read your pixels from the texture like any texture-pixel-download.

Just to confirm, yes, you can call glReadPixels() against a pbuffer. A GLAutoDrawable attached to a GLPbuffer behaves the same as when it’s attached to, for example, an on-screen GLCanvas.

Thanks for your answers…

I tried to setup my pBuffer (the canCreatePBuffer method returns true on my intel-855GM).

Unfortunately I can’t find the “another pbuffer thread”, which is quoted often within other threads.

But I get the following error:
pbuffer creation error: Couldn’t find a suitable pixel format

The code I am using:

GLCapabilities caps = new GLCapabilities();
// caps.setDoubleBuffered(true);
// caps.setHardwareAccelerated(true);
// caps.setRedBits(8);
// caps.setGreenBits(8);
// caps.setBlueBits(8);
// caps.setAlphaBits(8);
// caps.setSampleBuffers(true);
// caps.setNumSamples(3);

	mouseBuffer = GLDrawableFactory.getFactory().createGLPbuffer(
			caps, new DefaultGLCapabilitiesChooser(), renderPanel.getWidth(),
			renderPanel.getHeight(), null);

How can I work around this problem?

Greets
Klemens

In case, of an jogl/intel issue, I tried to render and read back the pixeldata…

//… rendering code with “false colors”

intBuffer = BufferUtil.newIntBuffer(renderPanel.getWidth() * renderPanel.getHeight() * 4);
gl.glReadPixels(0, 0, renderPanel.getWidth(), renderPanel.getHeight(), GL.GL_BGRA, GL.GL_UNSIGNED_BYTE, intBuffer);
for (int i = 0; i < renderPanel.getWidth() * renderPanel.getHeight() * 4; i ++) {
if (intBuffer.get(i) != 0) {
System.out.print(intBuffer.get(i) + " an " + i);
}
}

//… normal rendering code

unfortunately the intBuffer ist empty… just 0’s

I’m also new with the RTT thing… could you point me to the basic steps of this method?

I found this thread(http://www.java-gaming.org/forums/index.php?topic=13473.15), but I still keep getting 0’s as colors.

My colors are set via - for example: glColor3ub((byte) 255, (byte) 0,(byte) 0)


IntBuffer buf = BufferUtil.newIntBuffer(3);

gl.glReadPixels(renderPanel.getWidth() / 2, renderPanel.getHeight() / 2 - 30, 1, 1, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, buf);
buf.rewind();

int r = buf.get() & 0xFF; 
int g = buf.get() & 0xFF;
int b = buf.get() & 0xFF;

The pixel at this position is definitively != 0…

I hope the problem is just a very silly mistake on my side

And sorry for my flooding…

Try doing a simple test where you use glClear() and glClearColor() to get the entire background color set up. Also double-check to make sure the read buffer is set up correctly, although the default should be correct for the GLPbuffer. Also, triple-check to make sure you are calling this while an OpenGL context is current; you can do this by installing a DebugGL in your GLEventListener.init() method.

Oh I’m so sorry… it didn’t have the context current (just wondering why I didn’t get any exception)… within the renderloop I’m able to get my pixel-data. I now try to use the panel.getcontext().makeCurrent() just fearing some synchronization problems.

I tried to set up the GLPbuffer within the renderloop (with all the default values) but it kept failing with the above mentioned exception. I really think it’s because of my intel graphcis board!?

Thanks so far
Klemens

You’re interested in bytes (GL_UNSIGNED_BYTE) and you read 3 ints (meaning the last 2 ints are definitly 0)

So pass a ByteBuffer as argument, or read back only 1 int from the IntBuffer and fetch the bytes with some bit-shifting.

Hehe yes… it was the first quick hack…

In “production” use I’d changed this. For now I was quite happy with a result != 0 ;D

Just one problem is left…

In my application I do the false-color-rendering on demand after doing certain geometry-transforms. I didn’t want to do this in the display-cycle. So I wrote this code to ensure the context I’m using is “current”.

renderPanel.getContext().setSynchronized(true);
renderPanel.getContext().makeCurrent();
render(renderPanel.getGL(), true);
renderPanel.getContext().release();
renderPanel.getContext().setSynchronized(false);

For me it’s no problem to “wait” in the makeCurrent-method. But unfortunately if I run this code with the opengl-pipeline enabled my glReadPixels() results in 0’s just like before. Am I doing something wrong here?

My first workaround is something like this:

if (!Java2D.isOGLPipelineActive()) {
renderPanel.getContext().setSynchronized(true);
renderPanel.getContext().makeCurrent();
render(renderPanel.getGL(), true);
renderPanel.getContext().release();
renderPanel.getContext().setSynchronized(false);
} else {
needsFalseColoring = true;
}

where needsFalseColoring is a flag which is handled in the display-cycle. But as I said I’d like to bypass the display-cycle.

Thanks for any help (it’s no really bad problem, but I’d like to solve it)
Klemens

Sorry, but unless you rewrite the GLJPanel you’re not going to have access to the drawables and contexts you need in order to manually make the OpenGL context current. In fact, it isn’t possible to make it current on the current thread – you would currently have to run a Runnable on another thread. The best and most portable solution is to do all of your work in your display() callback.

Ok, so I will do it as my last code-block described.

I just found out, that the glReadPixel-method causes a crash when multiple reshape calls occur in short time-distances.

I have the glReadPixels in my render-method. If I (just for testing) change the size of the GLJpanel frequently via a frame-resize the programm (and sometimes the system) crashes.

Any suggestions on this?

Do you have a test case for this?

The system shouldn’t crash under any circumstances – this sounds like a driver bug. Perhaps the buffer you’re passing in to glReadPixels isn’t large enough? Note that the pbuffer which backs the GLJPanel is larger than the GLJPanel’s size, so if you aren’t careful you may be reading back too much data.

I’ll provide a test-case next week. Or maybe later today.

I always rebuild the buffer with gljpanel.getWidth() * gljpanel.getHeight() * 3 and I’m reading unsigned bytes. So that should be okay. The problem I see is about hectic resizes. Where the GLJPanel (as a simple jcomponent) is resized (so the getWidth and getHeight calls result the new width) but the viewport and the unerlying gl-stuff etc. is still of a different size cause of the reshape call come’s a bit later.

Currently I’m setting a flag like “rebuildTheBuffer” within the reshape method. Anfortunaltey there come a lot reshape calls after each other so just when this flag is used to rebuild the buffer other reshape calls (and other resizings of course) let it crash. I tried to synchronize between the reshape method and my glReadBuffer-call, but the problem lies deeper within the gljpanel (or the driver) so my reshape-callback comes too late to react in a fitting manner.

So here’s my testcase. Just try to resizes the frame some times without releasing the mouse.

Edit: I changed the mousebuffer to have a 102410243 size as you said ((width+1)*(height+1)*3 works as well). And no exceptions occur… so why is there a problem by using real sizes? So take this testcase just as an implementation question ;D

It crashes on nvidia and intel with following error:

An unexpected error has been detected by Java Runtime Environment:

EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x77f437fe, pid=3972, tid=4060

Java VM: Java HotSpot™ Client VM (1.6.0-b105 mixed mode)

Problematic frame:

C [ntdll.dll+0x37fe]

An error report file with more information is saved as hs_err_pid3972.log

If you would like to submit a bug report, please visit:

http://java.sun.com/webapps/bugreport/crash.jsp

Sometimes I get an error like: Windows detected a problem with javaw.exe…

import java.nio.ByteBuffer;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLJPanel;
import javax.media.opengl.glu.GLU;
import javax.swing.JFrame;

import com.sun.opengl.util.BufferUtil;


public class ReshapeReadPixelTest {

	public static boolean read = true;
	/**
	 * @param args
	 */
	public static void main(String[] args) {

		final GLJPanel panel = new GLJPanel();
		panel.setOpaque(false);
		panel.addGLEventListener(new GLEventListener() {

			private ByteBuffer mouseBuffer;
			private int width;
			private int height;

			public void display(GLAutoDrawable arg0) {
				GL gl = arg0.getGL();
				
				if (panel.shouldPreserveColorBufferIfTranslucent()) {
					gl.glClear(GL.GL_DEPTH_BUFFER_BIT);
				} else {
					gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
				}
				
				gl.glBegin(GL.GL_QUADS);
				gl.glVertex3f(-1, 1, 0);
				gl.glVertex3f(-1, -1, 0);
				gl.glVertex3f(1, -1, 0);
				gl.glVertex3f(1, 1, 0);
				gl.glEnd();
				
				if (read) {
					read = false;
					mouseBuffer = BufferUtil.newByteBuffer(width * height * 3);
					gl.glReadPixels(0, 0, width, height, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, mouseBuffer);
				}
			}

			public void displayChanged(GLAutoDrawable arg0, boolean arg1, boolean arg2) {
			}

			public void init(GLAutoDrawable arg0) {
			}

			public void reshape(GLAutoDrawable arg0, int x, int y, int width, int height) {
				this.width = width;
				this.height = height;
				read = true;
				GL gl = arg0.getGL();
				GLU glu = new GLU();
				gl.glViewport(x, y, width, height);
				gl.glMatrixMode(GL.GL_PROJECTION);
				gl.glLoadIdentity();
	
				glu.gluPerspective(45.0, (float) width / height, 1.0, 400.0);
				gl.glMatrixMode(GL.GL_MODELVIEW);
				gl.glLoadIdentity();
	
				glu.gluLookAt(0, 0, 5, 0, 0, 0, 0, 1, 0);
				
//				panel.repaint();
			}
			
		});
		
		JFrame f = new JFrame();
		f.setSize(300,300);
		f.add(panel);
		
		f.setVisible(true);
	}

}