Jump to content

  • Log In with Google      Sign In   
  • Create Account

Half Silhouette?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
9 replies to this topic

#1 BlinksTale   Members   -  Reputation: 315

Like
0Likes
Like

Posted 23 May 2012 - 08:32 PM

Since 2002, 3d Mario games have used this fantastic silhouette feature:

Posted Image

If Mario is more than halfway obscured by something else, a silhouette or shadow is drawn to represent the rest of him. This lets you track the player when they run around somewhere the camera does not go, or has not caught up to yet.

I have a rough idea for how to (in OpenGL) mimic a full silhouette (worst case: I can draw everything, check Line of Sight, and if character is not visible, draw a second instance of the character in full black at half opacity as the last thing to be drawn, or something) but I don't have any idea where to start for a silhouette that kicks in at the half way mark, and doesn't overlay the character itself. Google searches are proving that there is either nothing on the subject or that I don't know the right terminology. Any thoughts?

Sponsor:

#2 Ashaman73   Crossbones+   -  Reputation: 7422

Like
1Likes
Like

Posted 23 May 2012 - 08:42 PM

You can do it with a stencil check like this:

1. Draw the whole scene (without your char)
2. Draw your character without writing him to the buffer, set a stencil bit when the z-test failed (=is hidden by something).
3. Draw your character normaly.
4. Draw the bounding box of your char or fullscreen quad, turn stencil test on and darken the area where the stencil test succeed (i.e. blend a dark color with the background).


In OpenGl you should look into stencil opertions: chapter 10, section Stencil Test

#3 szecs   Members   -  Reputation: 2140

Like
1Likes
Like

Posted 23 May 2012 - 11:14 PM

Another, simpler method (but may not be that neat) is to simply render the silhouette first with disabled depth test, then render the character normally, with enabled depth test. This will draw the non-obscured pixels on top of the silhouette, thus overwrites where the character is visible.

Or another one (without overdraw): draw the silhouette after the scene and the normal character but with reversed depth test (GL_GREATER if I recall correctly). This means only the obscured portion of the character will be rendered (since the normal character has the same depth, the visible portion of it won't be overwritten). If you apply the same transformation to the silhouette and the normal character (which you'd do by default), then there won't be any z-fighting between the silh and the norm character.

I'd prefer the second method, since all the scene with the character will be on screen (drawn in whatever order that suits you best), and the silhouette is drawn after everything like an effect, so you can do whatever post-processing you want and the renderer will be less messy (all scene drawing will be at one place).

Edited by szecs, 23 May 2012 - 11:43 PM.


#4 Ashaman73   Crossbones+   -  Reputation: 7422

Like
0Likes
Like

Posted 23 May 2012 - 11:48 PM

Another, simpler method (but may not be that neat) is to simply render the silhouette first with disabled depth test, then render the character normally, with enabled depth test. This will draw the non-obscured pixels on top of the silhouette, thus overwrites where the character is visible.

Or another one (without overdraw): draw the silhouette after the scene and the normal character but with reversed depth test (GL_GREATER if I recall correctly). This means only the obscured portion of the character will be rendered. If you apply the same transformation to the silhouette and the normal character (which you'd do by default), then there won't be any z-fighting between the silh and the norm character.

Due to overdraw of the character itself you will have trouble with blending in both solutions.

#5 szecs   Members   -  Reputation: 2140

Like
0Likes
Like

Posted 24 May 2012 - 12:10 AM

Hmmm, that's true.

#6 Digitalfragment   Members   -  Reputation: 844

Like
0Likes
Like

Posted 24 May 2012 - 12:20 AM

You can do it with a stencil check like this:

1. Draw the whole scene (without your char)
2. Draw your character without writing him to the buffer, set a stencil bit when the z-test failed (=is hidden by something).
3. Draw your character normaly.
4. Draw the bounding box of your char or fullscreen quad, turn stencil test on and darken the area where the stencil test succeed (i.e. blend a dark color with the background).


In OpenGl you should look into stencil opertions: chapter 10, section Stencil Test

The problem with that is that the character has overlapping polygons and will z-fail parts of himself.

0) From a clear colour/stencil/z
1) Draw player to stencil buffer, setting bit to non-zero
2) Draw everything the scene (not the player) as per normal, ignoring stencil
3) Draw player again, but set stencil operation to set zero if z-test passes, but leave it alone if it fails
4) Draw fullscreen quad where the stencil buffer is not zero.

Edited by Digitalfragment, 24 May 2012 - 12:22 AM.


#7 szecs   Members   -  Reputation: 2140

Like
0Likes
Like

Posted 24 May 2012 - 12:26 AM

"2. Draw your character without writing him to the buffer" that includes without writing to the depth buffer

I can see problems with this though in some corner cases:

mario.JPG

But it should be easy to solve, just swap step 3 and step 4 in Ashaman's solution.

(sorry to chip in)

Edited by szecs, 24 May 2012 - 12:37 AM.


#8 BlinksTale   Members   -  Reputation: 315

Like
0Likes
Like

Posted 24 May 2012 - 04:13 PM

These are all great sounding solutions, thank you. I think I'll be going the simpler route myself, as my main character is cube shaped and I don't think I'm using any blending right now. Thank you guys though!

#9 Jihodg   Members   -  Reputation: 519

Like
0Likes
Like

Posted 30 May 2012 - 12:52 PM

I think you don't even need a stencil buffer...

1. just draw your scene normally and leave the character for the end... then
2. disable depth writes and reverse the depth test, draw the character and only output a solid color (this draws the occluded silhoutte)
3. restore the depth states and draw the character normally

maybe I am missing something here because this sounds too simple!

#10 BlinksTale   Members   -  Reputation: 315

Like
0Likes
Like

Posted 26 September 2012 - 11:25 PM

Another, simpler method (but may not be that neat) is to simply render the silhouette first with disabled depth test, then render the character normally, with enabled depth test. This will draw the non-obscured pixels on top of the silhouette, thus overwrites where the character is visible.


SOLUTION: This one worked! Finally got around to trying this out today, and stopping depth test to draw a silhouette copy was the easiest solution, then turning it back on again. Literally, my code:

  ...
  glDisable(GL_DEPTH_TEST); // disable depth test for "shadow cube"
  drawSilhouette();
  glEnable(GL_DEPTH_TEST); // Then return to normal stuff
 
  // call on cubeShape's function, drawCube, to make a cube visual
  draw();
  ...

And over in cubeShape, my balancing (and admittedly a few magic numbers) to get the silhouette looking right...

// Grabs your rgb1 colors and makes a dark version of yourself
// Perfect for walking behind walls, but player identification
void CubeShape::drawSilhouette() {
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glEnable( GL_BLEND );
  glPushMatrix();
  // These code blocks modified from work on songho.ca
  // activate and specify pointer to vertex array
  glEnableClientState(GL_VERTEX_ARRAY);
  glVertexPointer(3, GL_FLOAT, 0, vertices);
  // draw first half, range is 6 - 0 + 1 = 7 vertices used
  for (int i=0; i<6; i++) {
    if (!useNeighbors || !neighbors[i]) {
	  glColor4f(r1*0.25+0.125,g1*0.25+0.125,b1*0.25+0.125,0.5);
	  // 0 to 3 means we only have four vertices used for each face
	  // 6 is the total points we create though from those four.
	  // indices+6*i is where we are looking, which face in indices
	  glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_BYTE, indices+6*i);
    }
  }
  // deactivate vertex arrays after drawing
  glDisableClientState(GL_VERTEX_ARRAY);
  glPopMatrix();
  glDisable( GL_BLEND );
}

Turning on GL_BLEND ended up being important to have it work with what was already there, rather than just being a starkly different color.

Anyways, it works now, and I'm quite happy with how it looks!

Posted Image

Thanks everyone for helping me get this far!




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS