Sign in to follow this  
daniel_i_l

outline a model

Recommended Posts

I have models of tanks and stuff in my game and want there to be a thin green outline around them when they're selected. I thought about drawing each model again in GL_LINE mode but that draws all the polys as lines, not just the outline so it looks more like a net. How can I do it? Thanks. (please ask if I wasn't clear)

Share this post


Link to post
Share on other sites
Thanks for that link.
So if I have code that draws a 3D model: DrawModel();
to get the outline I'd do:

glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA ,GL_ONE_MINUS_SRC_ALPHA);
glPolygonMode (GL_BACK, GL_LINE);
glLineWidth (outlineWidth);
glCullFace (GL_FRONT);
glDepthFunc (GL_LEQUAL);
glColor3fv (&outlineColor[0]);
DrawModel();

and I'd get the outline?
Thanks.

Share this post


Link to post
Share on other sites
On a similar note... are lines the "accepted" way to apply outlines to cell shading (to produce a cartoonish image)? Or is it a screen space normal direction test? (ie see which pixels are within a certain degree of perpendicular to the view direction)?

thanks
-Dan

Share this post


Link to post
Share on other sites
I tried and it didn't work. I did:

Display(tank,0,false);

//outline:

glDisable(GL_TEXTURE_2D);
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA ,GL_ONE_MINUS_SRC_ALPHA);

glPolygonMode (GL_BACK, GL_LINE);
glLineWidth (5);

glCullFace (GL_FRONT);

glDepthFunc (GL_LEQUAL);

glColor3f (0,1,0);

Display(tank,0,false);

glDepthFunc (GL_LESS);
glCullFace (GL_BACK);

glPolygonMode (GL_BACK, GL_FILL);

glLineWidth (1);

glDisable (GL_BLEND);
glEnable(GL_TEXTURE_2D);


were Display() renders a model. But instead of drawing a green outlin the whole tank cameout green, it eas even textured? It was like the front facing culling wasn't working or something.
What did I do wrong?
Thanks.

Share this post


Link to post
Share on other sites
daniel_i_l,
The other simple method is shifting(shaking) the object slightly left/right/up/down, then drawing it 4 times on the stencil buffer to make a silhouette. There is an old example at SGI site, silhouette.c It resizes glViewport instead moving the object. The result is not same as nehe lessen #37. It creates the outlines only, not all edges of the object.

Share this post


Link to post
Share on other sites
The way i did it is similar to songho's method, but without drawing it 4 times. The process goes like this:

1. Disable color writes and draw (filled) to create a mask in the stencil buffer
2. Enable color writes, set linewidth to something big (like 7) and draw again in wireframe where the stencil is not set.

This produces the same effect as his method, ie only the outline.

Share this post


Link to post
Share on other sites
Quote:
Original post by daniel_i_l
Do you think that you (or someone else) could explain more? Maybe with a little code?
Thanks.


Let me try. First, you must request a stencil buffer when you create your window. Then do something like I do:
glClear(GL_STENCIL_BUFFER_BIT);                        // Clear the stencil

glEnable(GL_STENCIL_TEST); //Enable stencil test
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); //Disable color writes

// Now set the stencil to create a 2d mask of the object
// Increase the value in the stencil buffer if the depth test passes
// ie, if it's visible
glStencilFunc(GL_ALWAYS, 1, 0xffff); // the stencil test always passes
glStencilOp(GL_KEEP, GL_INCR, GL_INCR);

object.Draw();

// At this point we have a mask of values > 0 where the object is
// so we set the stencilfunc to allow fragments where stencil >= 1
glStencilFunc(GL_GEQUAL, 1, 0xffff);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // don't modify the values

glLineWidth(5.0f);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
object.DrawInWireframe();

glDisable(GL_STENCIL_TEST);

That's it, i think. I hope you understand it, I'm not too good at explaining things.

Share this post


Link to post
Share on other sites
Quote:
Original post by Ademan555
On a similar note... are lines the "accepted" way to apply outlines to cell shading (to produce a cartoonish image)? Or is it a screen space normal direction test? (ie see which pixels are within a certain degree of perpendicular to the view direction)?

thanks
-Dan


Ademan, to adress your thought here. When I was making my toon shader, I ended up passing the model twice, the second time rendering the lines only on the inside of the model. That'll solve all problem without killing the stencil buffer. If you try to find out which edges are about perpendicular to you, you get a bad image. The reason is, because some people model diferently, and you can end up with huge black spots where they don't look cartoonish, just bad.

(Daniel, read those bolded words over again, remember, make it render green lines ON THE INSIDE, not outside.)

Couple images:

Bad:


Good:

Share this post


Link to post
Share on other sites
Gaenor: I used that code but for even one tank the framerate wnt incredibly slow. Could it be that the SB isn't good for more than simple geometry? Or is there another problem?
Since the tanks are pretty detailed and there're many of them I need a "quick and dirty" way of doing the outline.
Thanks.

Share this post


Link to post
Share on other sites
Is it that slow? I'm currently using it in my map editor to draw the lights' area, as you can see here.

P.S: The second pass should use glStencilFunc(GL_EQUAL, 0, 0xffff); That is, draw outside the shape. The method I posted has the drawback of drawing the wireframe when the camera is inside the object (which is what i wanted at that moment).

Share this post


Link to post
Share on other sites
Gaenor: I dont know what the problem is. I did this:

glClear(GL_STENCIL_BUFFER_BIT);

glEnable(GL_STENCIL_TEST);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glStencilFunc(GL_ALWAYS, 1, 0xffff);
glStencilOp(GL_KEEP, GL_INCR, GL_INCR);

glEnable(GL_TEXTURE_2D);
Display(tank, 0, false); //draws a 3D model of tank

glStencilFunc(GL_EQUAL, 0, 0xffff);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

glLineWidth(1.0f); //5 seemed huge, you could barely see the lines
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

glDisable(GL_TEXTURE_2D);
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE);
Display(tank, 0, false);
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL);

glDisable(GL_STENCIL_TEST);



the tank looked like this: (normally its fully textured)
Image hosting by Photobucket
Is it supposed to do that?
When I did this to one tank it went very slow. Is there anything I missed? Maybe some initialation thing?
Thanks for you help!

Share this post


Link to post
Share on other sites
The code you posted should be working. Make sure you're requesting a stencil buffer when you create your window. This is how I do it:


static PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR),
1, // Version number
PFD_DRAW_TO_WINDOW | // Format must support window
PFD_SUPPORT_OPENGL | // Format must support OpenGL
PFD_DOUBLEBUFFER, // Must support double buffering
PFD_TYPE_RGBA, // Request an RGBA format
bitsperpixel, // Select our color depth
0, 0, 0, 0, 0, 0, // Color bits ignored
8, // 8bit Alpha buffer please
0, // Alpha shift bits
0, // No accumulation buffer
0, 0, 0, 0, // Accumulation bits ignored
24, // 24Bit Z-Buffer (Depth buffer)
1, // We want a stencil buffer
0, // No auxiliary buffer
PFD_MAIN_PLANE, // Main drawing layer
0, // Reserved
0, 0, 0 // Layer masks ignored
};


You're right about the linesize. The screenshot you see in my previous post uses 3.0f

Also let me try to explain what we're doing. The stencil buffer is a screen sized buffer. It contains a x bits counter at each pixel. So what we are doing is draw the model filled incrementing the values in the stencil buffer. This creates a 2d mask containing x>0 values where your model is. In the second pass we are setting the stencil operation to let us draw where the stencil has values = 0, that is, anywhere but where the model is. So when we draw it again in wireframe, the stencil will block all fragments in the middle of the model, drawing only the outline.

Also, a cheap way of getting an outline is to scale the model a little (say, 1.01), draw it untextured in your preferred color, reset the scale and draw it normally. This method is affected by depth, though.

Share this post


Link to post
Share on other sites
Quote:
Original post by daniel_i_l
Gaenor: I dont know what the problem is. I did this:
*** Source Snippet Removed ***

the tank looked like this: (normally its fully textured)
Image hosting by Photobucket
Is it supposed to do that?
When I did this to one tank it went very slow. Is there anything I missed? Maybe some initialation thing?
Thanks for you help!


ALMOST ALMOST!!!

FIRST, when rendering a line, each line is actually 3 polygons, that's how video cards render them. That's why it's a bit slower.

SECOND, You need to render the NORMAL model.

THIRD, You need to make a SECOND PASS, rendering ONLY the BACK lines:

glPolygonMode( GL_BACK, GL_LINE);

During the second pass, you need to set the depth function to glDepthFunc(GL_LEQUAL); This will only render the outlines of the tank, which is what you're looking for.

[Edited by - dbzprogrammer on April 25, 2006 5:46:44 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Gaenor
The way i did it is similar to songho's method, but without drawing it 4 times. The process goes like this:

1. Disable color writes and draw (filled) to create a mask in the stencil buffer
2. Enable color writes, set linewidth to something big (like 7) and draw again in wireframe where the stencil is not set.

Gaenor,
This is much simpler and more efficient than what I said before. (BTW, that is an SGI example, not mine.) I could quickly test your method and it worked well. Thanks.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this