glReadPixels reading z value

Started by
13 comments, last by svnstrk 12 years, 5 months ago
hi,

I'm trying to create a rts-style mouse picking: when i click an object it picks the (world) coordinate on the plane. the view is isometric (almost like diablo 2 style).
I tried to do that by this snipplet:


void GameManager::mouseInput(int x, int y) {

level.draw(); // drawing the isometric-viewed plane

GLint viewport[4];

GLdouble modelview[16];

GLdouble projection[16];

GLfloat winX, winY, winZ;

GLdouble posX, posY, posZ;



glGetDoublev( GL_MODELVIEW_MATRIX, modelview );

glGetDoublev( GL_PROJECTION_MATRIX, projection );

glGetIntegerv( GL_VIEWPORT, viewport );



winX = (float)x;

winY = (float)viewport[3] - (float)y;

glReadPixels( x, (winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ );

gluUnProject( winX, winY, 1.0, modelview, projection, viewport, &posX, &posY, &posZ);

mousePos.x = (float)posX;

mousePos.y = (float)posY;

mousePos.z = (float)posZ;

}




So what I did is I read the x and y from the screen space mouse position, and read the z value with glReadPixel. assuming I have the correct x, y, z value, I use glUnproject to figure out what is the world coordinate value of the current coordinate that I had.

but it does not work perfectly. I notice that the z value I got from glRedPixels returns me 0.99-0.99999 ( and 1 when nothing rendered). I set my near plane value 1 and far plane value 500, and the plane is rendered from ~50 to ~400. I read from this link:

http://www.sjbaker.org/steve/omniv/love_your_z_buffer.html

that z value is not linearly distributed based on the value. and I'm sure that's my problem. Any Idea how to tackle this? Any1 ever made this kind of rts-style coordinate picking?


thanks in advance
Advertisement
The link you show is exactly the problem. The depth buffer values are non-linearly distributed with regards to linear Z-values. And yes, if you have a near plane at 1, then it is not surprising at all that Z-values from 50 to 400 gives depth buffer values from 0.99 and up.

And yes, that really means that your depth buffer has 99% of its depth buffer values located in the Z-range from 1 to 50, and 1% of its depth values in the Z-range from 50 to 500. That is why it is very important to not put the near plane too close. Small absolute changes can have dramatic effects on the distribution.

The only thing you can to about it is to either have a shader that outputs linear depth values, or push the near clip plane out. That is, if precision loss due to the non-linear distribution is actually a problem, and not just because you want some more linear-looking values.
Like[color=#CB021A] Brother Bob user_popup.pngsaid, make a shader that outputs linear depth to a buffer. Create a Render Buffer (FBO) with a RGBA32F format (or one of the single formats GL_R32F to save memory if you have GL 3.3). Write your real z values to it. It is quite easy. I've been doing it since GL 2.1.
Sig: http://glhlib.sourceforge.net
an open source GLU replacement library. Much more modern than GLU.
float matrix[16], inverse_matrix[16];
glhLoadIdentityf2(matrix);
glhTranslatef2(matrix, 0.0, 0.0, 5.0);
glhRotateAboutXf2(matrix, angleInRadians);
glhScalef2(matrix, 1.0, 1.0, -1.0);
glhQuickInvertMatrixf2(matrix, inverse_matrix);
glUniformMatrix4fv(uniformLocation1, 1, FALSE, matrix);
glUniformMatrix4fv(uniformLocation2, 1, FALSE, inverse_matrix);
hi, thx fr the reply.

i dont have advance knowledge in glsl programming, but is it possible to render a 32 bit value RGBA value or a single 32 bit value? do you have a snipplet how to do that so I know how to start?
gluUnProject( winX, winY, 1.0, modelview, projection, viewport, &posX, &posY, &posZ);

1.0 should be winZ

NBA2K, Madden, Maneater, Killing Floor, Sims http://www.pawlowskipinball.com/pinballeternal


hi, thx fr the reply.

i dont have advance knowledge in glsl programming, but is it possible to render a 32 bit value RGBA value or a single 32 bit value? do you have a snipplet how to do that so I know how to start?


What do you mean by if it is possible?
Anyway, the Wiki has some info http://www.opengl.org/wiki/GL_EXT_framebuffer_object
I don't know why the page is called GL_EXT_framebuffer_object because it seems to be about core GL 3.0.

and there is the example
http://www.opengl.org/wiki/Framebuffer_Object_Examples#Quick_example.2C_render_to_buffer_.28p-buffer_replacement.29

where you can replace
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, 256, 256);

you can replace the GL_RGBA8 with whatever format you want.
Sig: http://glhlib.sourceforge.net
an open source GLU replacement library. Much more modern than GLU.
float matrix[16], inverse_matrix[16];
glhLoadIdentityf2(matrix);
glhTranslatef2(matrix, 0.0, 0.0, 5.0);
glhRotateAboutXf2(matrix, angleInRadians);
glhScalef2(matrix, 1.0, 1.0, -1.0);
glhQuickInvertMatrixf2(matrix, inverse_matrix);
glUniformMatrix4fv(uniformLocation1, 1, FALSE, matrix);
glUniformMatrix4fv(uniformLocation2, 1, FALSE, inverse_matrix);

What do you mean by if it is possible?
Anyway, the Wiki has some info http://www.opengl.or...mebuffer_object
I don't know why the page is called GL_EXT_framebuffer_object because it seems to be about core GL 3.0.

and there is the example
http://www.opengl.or..._replacement.29

where you can replace
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, 256, 256);

you can replace the GL_RGBA8 with whatever format you want.


hi v-man,

i tried the example but it didnt seem to work. here is how is set the fb:


glGenFramebuffersEXT(1, &fb1);

glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb1);

//Create and attach a color buffer

glGenRenderbuffersEXT(1, &tx1);

//We must bind color_rb before we call glRenderbufferStorageEXT

glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, tx1);

//The storage format is RGBA8

glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, width, height);

//Attach color buffer to FBO

glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, tx1);

//-------------------------

glGenRenderbuffersEXT(1, &rb1);

glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, rb1);

glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, width, height);

//-------------------------

//Attach depth buffer to FBO

glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, rb1);





GLenum status;

status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);

if (status == GL_FRAMEBUFFER_COMPLETE_EXT) {

cout << "good fb" << endl;

} else cout << "bad fb" << endl;







it prints good fb so my vga should support it. here is how i draw and capture the pixels:


glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb1);

glClearColor(0.0, 0.0, 0.0, 1.0);

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);



glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

gm.draw();



GLubyte pixels[4];

pixels[0] = '0';

pixels[1] = '0';

pixels[2] = '0';

pixels[3] = '0';

cout << "first: " << (char)pixels[1] << endl;

glReadPixels(901, 501, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels);

cout << (char)pixels[1] << endl;





but it seems that i cant get the pixel value, it always return 0, the value that i set as default. any idea why this happens?


thx in advance
There are two ways to select objects:

1.) a.)Draw every object with its own glColor (no textures), glReadPixels(RGB) and if its say yellow, then you selected the object that has a yellow selection color.
b.)Use a selection buffer, instead of doing it by color, you can render your models as integers and glReadPixels will give you an integer of the model you selected.
(Optimize this by chaning glViewport to the pixel location the mouse is at, 1x1 or if precision is off a bit use 2x2).

2.) glReadPixels with the z value you currently have ( a random number between 0 and 1 that is not linear, meaning .5 does not imply halfway between the near plane and far plane you gave. The depth is in a completely separate space. use gluUnproject with the pixels x,y, and z from the depth buffer. That will give you the x,y,z of that pixel in the actual world coordinates. You can use that to figure out what you selected by doing a 3d model distance from that x,y,z. More precisely, you can case a ray cast using gluUnproject with a depth of 1.0. That will unproject to a point that is very far away on your far plane. Take that point and the cameras position, and you have a line/ray in world coordinates that you can use to do raycasting in the world.

Instead of doing the framebuffer, which is a waste as you need to attach a separate depth buffer (meaning you will now need 2), just go ahead and search for opengl selection buffer. FYI glReadPixels only works on the framebuffer that is going to the screen, renderbuffer/render to texture are separate and not able to be read by glReadPixels because they are not the screen texture that goes to your display. Because of that, using an FBO/renderbuffer is not a solution to the problem. Which is why I'm not sure both those guys didn't tell you that. Would be nice if the new 4.0 spec allows that but I have not looked at that. Is this deprecated in 4.0 then?

NBA2K, Madden, Maneater, Killing Floor, Sims http://www.pawlowskipinball.com/pinballeternal


but it seems that i cant get the pixel value, it always return 0, the value that i set as default. any idea why this happens?

thx in advance


<br><br>First, let's clarify things. Why do you need the z-value for?<br>Do you want to select an object or do you need to find the z-value of something?<br><br>Do you need to select objects? Do you need to know where exactly on the object the user clicked?<br>If you just need to do simple object selection, then the Wiki covers it<br><br>http://www.opengl.org/wiki/Common_Mistakes#Selection_and_Picking_and_Feedback_Mode<br><br><br>
Sig: http://glhlib.sourceforge.net
an open source GLU replacement library. Much more modern than GLU.
float matrix[16], inverse_matrix[16];
glhLoadIdentityf2(matrix);
glhTranslatef2(matrix, 0.0, 0.0, 5.0);
glhRotateAboutXf2(matrix, angleInRadians);
glhScalef2(matrix, 1.0, 1.0, -1.0);
glhQuickInvertMatrixf2(matrix, inverse_matrix);
glUniformMatrix4fv(uniformLocation1, 1, FALSE, matrix);
glUniformMatrix4fv(uniformLocation2, 1, FALSE, inverse_matrix);
hi,

im not selecting objects, i need the exact world coordinate at the mouse i clicked. i finally figure it out by using 16bit rgb fbo, saving the world coordinate as the fbo using glsl. its probably expensive memory-wise, but its pretty straightforward.


thanks for the help guy :)

This topic is closed to new replies.

Advertisement