Those dear shaders

Good day everyone.

Still working on my first 3D game, I just finished most of the logical and level editing parts.

However. I have a few graphical features that I need to implement. I have absolutely no idea how: I believe it might be done using shaders. But I have no knowledge of shaders whatsoever, despite spending the past two days studying them I still have no idea what kind of sorcery make them work.

Here are the three features I need to implement:

-> Outlining several models with red or green lines,

-> Lighting with ambiant, directional, spot and point lights.

-> A more complicated thing: a screenshot will explain it better:
http://livingwithanerd.com/wp-content/uploads/2010/10/fallout-2-screenshot.jpg
The walls around the playing characters are fading out. I need to implement this as well.

So, what do you think ?
Do you have any solutions for those that don’t imply using a shader ? What would they be ?

And most of all. Knowing that I have never worked with shaders, and that I am bad at math (and really really really bad at geometry: I stopped understanding anything after Pythagore).
Knowing that, is there a way I will ever be able to implement shaders for those features ?

Phew, quite a few questions.

Panda ATM only supports outlines rendering for the whole scene.
But colored and selective outlines was done once by a community member. You can read about it here: [Cartoon Painter)

For lighting you’ll need to be a bit more specific. What do you need that is not described here?: panda3d.org/manual/index.php/Lighting

The looking through walls thing could be solved in several ways. The first that comes to my mind is rendering the wall into a separate layer, using a stencil map for cutting out a circle and combining it with the rest of the scene.

For the shaders issue: you’ll need quite a solid understanding of some linear algebra at least.

Here is some non shader code to draw outlines. It only works in opengl though. Basically, it draws every object twice. On the first draw, the object is drawn normally except it sets the stencil mask to mark out the parts of the screen that have been drawn. The second draw, draws the same object again but slightly bigger in wireframe mode. By checking the stencil buffer during the second draw, the larger object is only shown on the screen in areas untouched by the first object, thus outlining the first object.

In Panda, drawing an object again requires a second camera.


from pandac.PandaModules import loadPrcFileData

import direct.directbase.DirectStart
from panda3d.core import *

#Draw smiley and mark the stencil
npModel = __builtin__.loader.loadModel('smiley')
npModel.reparentTo( __builtin__.render )
npModel.setAttrib( StencilAttrib.make(1,StencilAttrib.SCFAlways,StencilAttrib.SOZero, StencilAttrib.SOKeep,StencilAttrib.SOReplace, 8, 255, 255) )
__builtin__.base.cam.node().setActive(1)

#Setup a false node to setup some initial states
temp = NodePath('')
temp.setTextureOff( 100 )
temp.setColor( 0, 1, 0, 1)
temp.setAttrib( RenderModeAttrib.make( RenderModeAttrib.MWireframe, thickness = 5 ), 1 )
### Don't forget to override the priority of the stencil buffer
temp.setAttrib( StencilAttrib.make(1, StencilAttrib.SCFGreaterThan, StencilAttrib.SOKeep, StencilAttrib.SOKeep,StencilAttrib.SOKeep, 8, 255, 255), 1)
temp.setDepthTest( False )
temp.setDepthWrite( False )
temp.setScale(1.2)

#Make a new camera and inherit the node states from the false node
npCamera = __builtin__.base.makeCamera( __builtin__.base.win, sort = 1, lens = __builtin__.base.camLens, scene = __builtin__.render)
npCamera.node().setInitialState( temp.getState() )
npCamera.node().getDisplayRegion(0).disableClears()

#The two cameras have to be aligned in the same spot at all times.
#Use a task to do this every frame if base.cam moves.
__builtin__.base.cam.setPos( 0, -10, 0)
npCamera.setPos( 0, -10, 0)

print "Stencil Clear Value", __builtin__.base.win.getClearStencil()
print "Stencil Clear Active", __builtin__.base.win.getClearStencilActive()

run()

For lighting shaders, I suggest you google for a free copy of NVidia’s excellent introductory shader book called “The Cg Tutorial”.

[quote]
→ A more complicated thing: a screenshot will explain it better:

http://livingwithanerd.com/wp-content/uploads/2010/10/fallout-2-screenshot.jpg
The walls around the playing characters are fading out. I need to implement this as well.

[quote]
Once you master the outline stencil approach, you can do this without a shader.

Prioritize your draw such that

  1. Draw the floor + figure first
  2. Draw a black ball in the same location as the figure and set the stencil buffer to mark that part of the screen. The black ball should be drawn using using ColorBlendAttrib(ColorBlendAttrib.MAdd) such that the black ball doesn’t affect the color of the screen. (Black + Any Color = Unchanged color) We only want to use the black ball to mark the stencil buffer.
  3. Draw the walls with the stencil rejection set in the same way as the outline method above and now the walls will not draw over the character! Hence, looking transparent.
    [/code]

Thank you for helping !

Oops, that’s me not writing what I’m thinking about. I wanted to speak about shadow mapping.
It’s said that it only works with spotlights and directional lights.

But I would still need to have shadows with point lights.
Also, I never got the “Shadow Mapping” part of the manual to work with any kind of lights anyway.

(And come to think of it, ambient light with shadow mapping seems completely irrelevant ! I’ll use a directional light to simulate day/night cycle instead. Forget I ever talked about ambient lights)

Looks like this is exactly what I need.
I’ll post the result if I manage to port it to C++ properly.

Well this looks quite straightforward. I have no idea how to do most of this :smiley: ! But I’ll manage with the docs, I suppose I should find everything in the Render Attributes part of the manual ?

The black ball can be any spheric model I want, can it ?

Also, this technique won’t make a difference between a wall behind and a wall in front of the character right ?
Maybe I should first cast a CollisionTube between the character and the camera, and only apply this effect on the models that collide with it. Does this look like a good solution ?

You might have to sort the walls back to front. Anyway, I would try to get it work with one wall first, then worry about the back wall later.

Jesus Christ ! It’s starting to work ! I can make the walls disappear around my character !
It took me a while to figure out how to initialize the StencilAttribs, but it works now. And I have a much better understanding of these ^^ !

But now that I’ve done this, I only understand better how my ideas for handling the back/front walls was stupid.
The only ideas left now are:
-> To compute which are the walls in the back and those in the front, and reparent them to two different NodePath, with only one of them using the “reader” stencil.
-> Go over all those nodepath and make an override for those in the back.

But they both look wrong. Is there an easier or more appropriate way to do this ?

EDIT: Wait, actually those solutions are all wrong.
If one of the model is a house, then there’s the walls and the floor. Some of the walls might be in front of the characters, some behind…
So if I need only part of this model not to be drawn with the stencil attribs… well it’s over, I’m screwed, am I not ?

Hmm… Could you not use some form of z-testing to determine which pixels to overwrite? After all, if I’m not much mistaken, the only walls that you want to make transparent are those that have a z-depth less than that of the character at that point.

I’m not even sure how to do that.
Would it be enough ? I’m not entirely sure how OpenGL draws stuff, but if I just say not to draw the pixel in front of the character, will it still draw the pixel behind the character, even though it’s from the same model ?

And if yes, which tools do I need for that purpose ?

Well… I’ve turned the problem around and around, and I’ve come to the conclusion that this is impossible to solve.
You just can’t cut a part of a model and replace it with another part from that same model. Case closed.

I’ll keep looking into the outlines now. I have a topic right there about porting the cartoon painter example to C++:
panda3d.org/forums/viewtopi … 9230#89230
No answers yet: I just need to understand what are base.cam2d and base.makeCamera… and understand how to mimic their behaviors in C++.

All right, I think that I have an idea for your wall cut-out:

  1. Set up your scene to render with both walls and characters.

  2. Render your characters and floors to a separate (small) buffer (or set of buffers, if called for), and combine with your edge-blending mask.

  3. Place the buffer’s texture onto a card in the main scene, placed such that the rendered characters match up with those rendered in the main scene.

3.1) Use bin-assignment to force the card to be rendered after the characters, walls and floors at the least.
3.2) Use NodePath.setAttrib to cause the card to render with a reversed depth-test, like so:

nodePath.setAttrib(DepthTestAttrib.make(RenderAttrib.MGreater))

(I think that it’s MGreater that should be used there; you might want to double-check that.)

This should cause the card to be rendered only where the depth buffer has a value lower than that of the card. Pixels in the background should have a higher depth than the card, and so not be drawn over. Walls in front of the characters, however, should produce depth values lower than those of the characters, and so should be replaced by pixels from the card.

Note that if we were to render the scene without the characters in the first step we would presumably have the characters only visible when behind walls, which might be interesting, but probably not what you intend… :stuck_out_tongue:

Set the walls to use transparency and project a white texture with a alpha channel hole onto the wall (use MModulate). Set the texture projector betwen the character and the camera, pointing down with a bit of a tild. If you get it right the hole should be projected only on the wall that is in front of the character.
I think I could make a sample if You are interested.

Thank you Thaumaturge :slight_smile: !
But if it is mandatory to render floors and walls separately, then I can’t do anything: floors and walls are often part of the same model (I would have to make 3D designers have floor and walls in separated models, which would mean twice as much work for level designers, and a complete re-write of the models handling for me).

Same thing goes for wezu’s solution, I believe.
To put a texture on a wall, I would have to know which faces of a model are part of walls. And I don’t think there’s any way to find that out.

Which makes me think this feat is simply not doable.

You’d have to parent all the walls under a common node, when designing levels/maps you’d have to make a decision what is a wall and what is not. I see no other way.

Hmm… Could you get away with having your modellers design them as separate geoms, and then either tagging them or applying a naming convention that makes for easy use of the “find” method?

I do think that this is quite possible, but you probably will want to separate walls from floors one way or another, I suspect.