oyvindra's picture

Color picking multiple objects

Hi,

I have set up color-picking of objects in my application, where I do a render with all objects colored in a unique color, and then GL.ReadPixel to find the color at the position where I clicked the mouse...

This has worked very well so far, but I need to expand it to a system where I can drag a rectangle, and then get all the objects inside this rectangle...

The only way I can see how to do this is to copy the pixels inside the rectangle, and then find all the unique colors in the copied image... Each of these unique colors would represent an object.

My question is then whether anyone has a better method, or if not; what is the quickest way to copy a range of pixels from OpenGL? (Any pointers to a quick Unique-color search is also appreciated)

I expect that this problem has been solved before, and look forward to reading some good tips and input :)

- oyvindra


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
objarni's picture

Is ReadPixels that slow? Even if you read all pixels in one go, instead of one-by-one?

oyvindra's picture

objarni; I am not sure, I've just read that it is... And if there was something like ReadRectangle, that should be faster :\

the Fiddler's picture

It probably is. Reading data from the framebuffer is the slowest OpenGL operation I am aware of.

I've never implemented color-picking before, but it might be faster to render to an FBO and read its data with GL.GetTexSubImage. You could also use a PBO to download data asynchronously, but I have no idea about driver support.

Another approach that might work is to treat the selection rectangle as the front face of a bounding frustum. Build the frustum, compare it to the bounding boxes of the visible items and select the ones it contains.

Edit: I wonder how model editors do this.

Edit 2: The name 'ReadPixel' is a bit misleading - it actually works as a 'ReadRectangle'.

objarni's picture

Isn't the name "ReadPixels" <- an s at the end? It is in OpenGL/C at least:

http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/gl/...

oyvindra's picture

Ah, I didn't actually notice I could read a width and height of pixels... I might try that and see if I get something clever from it...

I have been thinking of using a bounding box method, but the problem is that I am also selecting NURBS surfaces, drawn with glu. The only way I can think of to get a bounding box for this surface is to use the control polygons.
That could result in me clicking outside the real surface, but inside the control polygon, and getting it registered as a selection... Which is not acceptable.

Edit:
Not a good solution it seems, ReadPixels on more than about 10 x 10 pixels crashes my application. Not on the first or second selection though... so something weird is going on.

the Fiddler's picture

Can you post the relevant code? (always on the lookout for OpenTK bugs :) )

Edit:

objarni wrote:

Isn't the name "ReadPixels" <- an s at the end? It is in OpenGL/C at least:

http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/gl/readpixels.html

Indeed, it's ReadPixels. Slip of the tongue, it's correct in OpenTK.Graphics.

oyvindra's picture
static int IntFromPosition( Point pixelPosition ) {
	byte[] color = new byte[300];
	int[] viewport = new int[4];
	GL.GetInteger( GetPName.Viewport, viewport );
 
	// Read color of pixel at the given position of the active/current viewport (the -4 was added because picking was off by 4 pixels.
	// Also, the Y coordinate is transformed from screen coordinates to OpenGL coordinates
	GL.ReadPixels( pixelPosition.X, viewport[3] - pixelPosition.Y - 4, 10, 10, OpenTK.Graphics.PixelFormat.Rgb, PixelType.UnsignedByte, color );
 
	// Convert to Color type
	Color pixelColor = Color.FromArgb( color[0], color[1], color[2] );
	int index = IntFromColor( pixelColor );
	return index;
}

I'm not sure this is where the problem is though... It seems to hang only when I click-drag... so it might be somewhere else in my picking code.

Edit - changing to:

 byte[] color = new byte[1024];

seems to have helped, I seems to need more than three bytes per pixel read.

the Fiddler's picture

Something is fishy here, you shouldn't need more than 10 * 10 * 3 bytes here.

Can you please check the index of the last non-zero byte in the array? (If it's something like 301 or 304, it might hint at a corrupted heap).

objarni's picture

Is it possible it is some alignment/pixel read-write/storage legacy stuff? I always get those things wrong ;)

oyvindra's picture

Its going all the way to 317 before it stops...

color[317] = 255
color[318] = 0