PDA

View Full Version : Hiding a bitmap



devdept
01-13-2009, 11:39 PM
Hi All,


Here is today's question:

We need to draw a label (square bitmap containing a number) for numbering each cube vertex. The cube is shaded.

We managed to draw the bitmap in each corner converting world coords in screen coords but we don't know how to hide the labels behind the cube.

How can be done?


Thanks,

Alberto

yooyo
01-14-2009, 01:56 AM
Use gluProject call to get proper screen coordinates. Now, readback depth value at specific point and check against projected value. If projected value is smaller than readed depth (means closer to viewer) then draw string.

gluProject returns screen x, y coordinate of projected point and screen depth (0..1).

devdept
01-14-2009, 02:23 AM
Thanks yooyo,

How do I "read back depth value at specific point" ?

Thanks,

Alberto

remdul
01-14-2009, 02:52 AM
Use glReadPixels, read back one pixel (or more than one if you wan to smoothly fade out depending on how much of the vertex is visible) from the depth buffer. Draw glPoints to the depth buffer before you call read them back (use glPointSize(2) to avoid accuracy problems with gluProject, if any).

glReadPixels will induce a pipeline stall though. If you want fastest possible performance, I would suggest to render the labels as sprites (GL_QUADS or perhaps point sprites) in the scene at a constant size (relative to screen), and take advantage of depth-testing.

If the cube is wireframe, use glColorMask to disable color buffer rendering, and 'bake' the solid cube to the z-buffer. Then enable color rendering again, and draw the labels.

You may want to use polygon offset to avoid intersection of the sprites with the cube.

Another alternative is to use occlusion queries, rather than glReadPixels.

devdept
01-14-2009, 03:11 AM
I need to test only one pixel the one where the label is attached.

Why should I draw points? It isn't enough to read the pixel depth and compare it with the label anchor 3D point?

Thanks.

Alberto

remdul
01-14-2009, 05:00 AM
It highly depends on rasterization of the depth at the vertex positions, and how accurate your 'world to screen coordinate' projection function is (but to a lesser degree). If the depth changes alot between frames, the tags may flicker.

I've implemented something similar before. Slight differences in the rasterized vs. projected depth coordinates caused the tags to flicker, because the depth value would one frame be deeper (invisible), and the next to be exactly the same or larger (visible). Because the distribution of the depth buffer is non-linear, you can't simply add a depth margin to counter this.

I did not use the projected winZ component returned by gluProject, as it is in linear space, and the depth buffer is non-linear. You could probably use winZ to calculate it proper, but you have to get the exact formula that the GL implementation uses or add a margin.

If I recall correctly, I did it like this:
1) render visible scene
2) disable color buffer rendering (glColorMask) and clear depth buffer
3) glPointSize(3), to take in account inaccuracy of project function and badly implemented rasterization (read Intel graphics drivers)
4) render vertices as GL_POINTS to depth buffer
5) loop through vertices and project these to get the screen coordinates, store these
6) loop through vertices and read back depth from z-buffer, store this
7) render occluder (e.g. solid cube)
8) loop through vertices second time, and read back depth, store as well
9) setup 2d view projection (for 2d font/tag drawing etc)
10) loop through vertices and do the depth compare, if visible draw tag, if invisible skip

I had a structure for each vertex something like this:

struct vert
{
vec3 pos; // world space position
int screen_x; // projected screen space X
int screen_y; // projected screen space Y
float depth_1; // read back depth at screen space X,Y before occluder is drawn
float depth_2; // read back depth at screen space X,Y after occluder is drawn
};

Keep in mind this way vertices may also occlude other vertices (in my case this was desirable), but can be avoided if you dot he whole process for vertices individually (not recommended!).

As you can see, lots of glReadPixels with means lots of stalls. In my application this was ok, as it wasn't real-time.

IMO, better to draw the tags into the 3d scene, if that is ok for your purposes. It is faster and relies less on the assumed correctness of the GL implementation. You just need to make sure the tags are scaled properly with respect to the eye position and it will work efficiently.

devdept
01-14-2009, 06:21 AM
Hi remdul,


Thanks for your detailed description: looks not trivial btw.

We thought a lot about doing it in 3D but the problem is we want labels always parallel to screen and of the same size.


Thanks,

Alberto

Y-tension
01-14-2009, 05:31 PM
You may also use point sprites with textures for the numbers and maybe offset the depth coordinate a little at the fragment shader to account for depth fighting at the front of the cube.

ZbuffeR
01-15-2009, 03:19 AM
As explained remdul, going for full 3D is less fragile.
Point sprites are nice but not always available, and some implementations have bugs.

Search for 'opengl billboarding', this is pretty common.

devdept
01-15-2009, 06:17 AM
ZbuffeR,


I gave a look to the billboarding concept and still I think we don't need it: mainly because we need the same scale for each label (drawn like a semitransparent bitmap). Btw if billboarding is easiear to implement we must think of it.

To make everybody understand better our problem I add an image: TankNodes.jpg (http://www.devdept.com/TankNodes.jpg) (hidden labels must not be drawn)

What do you think? Should we go for remdul apprach or for billboarding?


Thanks so much again guys.

Alberto

devdept
01-22-2009, 02:05 AM
Hi ZbuffeR,

May I know your opinion on the last message (with image) I posted?

Thanks,

Alberto

_NK47
01-22-2009, 02:52 AM
"hidden labels must not be drawn"
i think if you couple labels (text) with those red dots you be able to skip the label rendering because noticed that hidden red dots are not rendered whereas the text still is. haven't read the whole thread, hope im not way out of topic here.

devdept
01-22-2009, 03:17 AM
I think if you couple labels (text) with those red dots

_NK47,

What do you mean with "couple"?

Thanks,

Alberto

_NK47
01-22-2009, 03:22 AM
example:

struct Node
{
Vec3 position;
String text;
};

if(isVisible(node))
{
drawRedDot(node->position);
printText(node->text);
}

here the position of the dot and its text are in one structure meaning they are related in some way. in the screenshot you posted they seem to be related but you draw them separatly i believe, because you already cull those dots somehow.

edit: just realized they are probably culled with depth test. i would project text position to screen space on CPU, read the depth value to see if it is occluded by something ( link (http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=44) ) or do occlusion query to see how many pixels the printed text occupies.

devdept
01-22-2009, 04:26 AM
The hard thing is to determine:


if(isVisible(node))
{
...


I thnk the only feasible way to to follow remdul instructions:


If I recall correctly, I did it like this:
1) render visible scene
2) disable color buffer rendering (glColorMask) and clear depth buffer
3) glPointSize(3), to take in account inaccuracy of project function and badly implemented rasterization (read Intel graphics drivers)
4) render vertices as GL_POINTS to depth buffer
5) loop through vertices and project these to get the screen coordinates, store these
6) loop through vertices and read back depth from z-buffer, store this
7) render occluder (e.g. solid cube)
8) loop through vertices second time, and read back depth, store as well
9) setup 2d view projection (for 2d font/tag drawing etc)
10) loop through vertices and do the depth compare, if visible draw tag, if invisible skip

Alberto

yooyo
01-22-2009, 03:56 PM
Huh.. try this:


void DrawString(vec3 pos, const char* txt, double *mv, double* proj, GLint* vport)
{
GLdouble sx, sy, sz;
// project point on screen
if (gluProject(pos.x, pos.y, pos.z, mv, proj, vport, &sx, &sy, &sz) == GL_TRUE)
{
float depth;
glReadPixels(sx, sy, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);

if (sz < depth)
{
//point is visible
RenderText(sx, sy, txt);
}
}
}

std::vector<vec3> points;

GLdouble mv[16];
GLdouble proj[16];
GLint viewport[4];

glGetDoublev(GL_MODELVIEW_MATRIX, mv);
glGetDoublev(GL_PROJECTION_MATRIX, proj);
glGetIntegerv(GL_VIEWPORT, viewport);

for (uint i=0; i<points.size(); i++)
{
DrawString(points[i], "Hello", mv, proj, viewport);
}



One hint.. be careful when you are reading modelview matrix. If you dont use glTranslate, glRotate and glScale (ie.. your objects are already in worldspace) then you can read modelview matrix once.
But if your app use glTranslate/Rotate/Scale to adjust object pos before render, then you must readback modelview matrix for each object just before draw calls.

devdept
01-23-2009, 01:42 AM
Hi yooyo,

I wish it would be so easy, I'll try and let you know.

Thanks,

Alberto

devdept
06-16-2009, 01:25 AM
Hi Guys,

Unfortuantely I am still struggling with this feature.

All the approaches based on:

1) Draw points
2) glReadPixel
3) Draw occluder
4) glReadPixel

Aren't reliable because in case of visible labels they blink frame to frame bacause of depth values almost equal.

Shoud I convert depth coordinate to linear and compare it more robustly or there is a way to make geometry (stay out, in depth sense) of points?

Thanks,

Alberto