Simple character shadow

Blitz3D Forums/Blitz3D Programming/Simple character shadow

Cubed Inc.(Posted 2012) [#1]
How can I make a simple circular shadow like the ones seen in pretty much every Nintendo 64 game ever made? (Super Mario 64, Banjo-Kazooie)


Yasha(Posted 2012) [#2]
You need a circular black fuzzy dot texture, set to multiply blend and applied to a mesh that hugs the ground, moving with the player.

Since you won't be bothering with light sources (they don't really match up with this kind of shadow casting), the only real hard part is how to manage the ground-hugging.

How hard this is will vary depending on your rules for the ground itself: is it flat? If so, job done: a quad will do. Is it more of a bumpy terrain? You have a slightly tougher task, but still very easy if it's a regular terrain (like a heightmap grid) compared to the mess of applying a shadow to an arbitrary mesh. Are there stairs and other regular-but-not-arbitrary complex meshes? Then the system needs to be slightly more complicated.

If you don't have any rules yet, I suggest drawing some up. Terrain+quads (i.e. stairs, floors and ramps) should easily be enough for 95%, of games and will enable you to use a much simpler system than arbitrary mesh backgrounds would (not to mention having infinite benefits for your other level design challenges, like pathfinding and collision management).

Assuming you choose this "rule" that shadows are only cast on a floor-object, and a floor-object is a collection of non-overlapping polygons (which is enough for flat ground, rough ground, ramps, and stairs), actually applying a shadow texture becomes quite easy and can be pretty much entirely based on the character's X and Z position, combined with the heights of the underlying polygons, themselves fairly easily trackable if you keep each floor-object fairly small and keep track of which one a character is on.


Cubed Inc.(Posted 2012) [#3]
Yasha
Actually I was thinking more like a flat cube textured with a shadow spot that aligns with the environment. Like I said, i'm just trying to get something simple.

Your texturing idea is good in theory but couldn't work becuase
the mapping coordinates of the map would completely throw of the shadow.

Still, when I first heard it, I was like "Thats not a bad idea"
Alas, I tried it and it didn't avail.


Kryzon(Posted 2012) [#4]
Actually I was thinking more like a flat cube textured with a shadow spot that aligns with the environment.

It doesn't need to be a cube. A quad\sprite will do fine.

Linepick from the character's position downwards, then position the shadow quad at the picked XYZ and align it with the picked NX,NY,NZ.
Remember to add a +0.01 offset to the Y axis so there's no Z-fighting with the environment.


Cubed Inc.(Posted 2012) [#5]
sorry but i'm still confused


Yasha(Posted 2012) [#6]
Your texturing idea is good in theory but couldn't work becuase
the mapping coordinates of the map would completely throw of the shadow.


Two ways around this:

-- one, you could use the second UV set; apply a whole new set of coords. This of course rules out using them for anything else, like a lightmap, and would only work for one character, so it's pretty crappy, but you could use it as a test run.

-- two, the way most B3D shadow systems work (and I actually had in mind) is to have a small mesh match the surface you want to cast over, rather than texture it directly. I am guessing that the "rule" about up-facing, non-overlapping, regular quads in relatively small, pickable floor entities would make finding the polygons to copy into the temporary shadow mesh a faster operation than trying to do it based on arbitrary mesh geometry (which is possible, and I can even supply code for that, but it's not going to be very efficient by comparison).


Cubed Inc.(Posted 2012) [#7]
Actually I was confused on the alignment of the shadow sprite to the world explaination. I feel that the alignment method would be the best, but I need to learn and understand it.


Kryzon(Posted 2012) [#8]
It's a cookie-cutter method, with LinePick+AlignToVector:


Function updateShadowDecal()

;Be 'shadowSprite' a one-sided quad textured with a smooth shadow image.

Local picked%
Local groundDistance#

;Perform a LinePick from the player's position to 100 units down on the Y axis.
picked = LinePick(EntityX(playerMesh,True), EntityY(playerMesh,True), EntityZ(playerMesh,True), 0, -100, 0)

If picked Then 
	PositionEntity shadowSprite,PickedX(),PickedY(),PickedZ(),True
	AlignToVector shadowSprite,PickedNX(),PickedNY(),PickedNZ(),3
 	MoveEntity shadowSprite,0,0,0.01 ;Move the decal very little, but enough to give the rasterizer no doubts when rendering - this avoids Z-fighting. The axis you offset it to is the axis where it's visible side faces.

	;If you don't see anything maybe you'll need to flip the shadowSprite mesh after you create it, since sprites and quads are generally created with the visible side facing negative Z axis.	

	;Fade the shadow blob based on the vertical distance from player to point of contact.
	;If the distance is greater than 20 units then the shadow is invisible.
	groundDistance = EntityY(playerMesh,True) - PickedY()
	If groundDistance > 20 Then groundDistance = 20
	EntityAlpha shadowSprite, 1.0 - (groundDistance/20.0)
EndIf

End Function

EDIT: Added the Z axis offset to the decal to prevent flimmering.

Last edited 2012


Cubed Inc.(Posted 2012) [#9]
sorry but the code makes no sense to me. Can you explain?


Kryzon(Posted 2012) [#10]
I'll assume you know how LinePick works: it tests the intersection of a 3D ray against any pickable entity in its way. It then registers the contact details: handle of the first entity that was picked, the position of the point of contact\intersecton, the normal of the triangle of the mesh that was picked, etc.
In that case I used a LinePick starting from the player's position then going straight down 100 units - it's a vertical linepick.

We can use the point of contact AND the normal of contact to respectively position the shadow decal and align it with the underlying triangle.

If you just position the shadow decal and say, you're walking down a hill or slope, the shadow decal will stick out and it will not look pretty [look below, it's the left frame].
By aligning the shadow decal - in this case I used AlignToVector, but it could be done using a RotateEntity along with VectorPitch() & VectorYaw() - you make the decal be aligned to whichever triangle it's supposed to be shadowing, similar to how a shadow is projected in real life. Then I proceed to slightly offset the decal in its local Y axis so it stands out from the triangle it's shadowing so no Z-fighting happens.
Take your time to understand this graphic:



The last part of that code makes the shadow more transparent the farther the player is from the ground.
I take the Y of the player and subtract from it the Y of the linepicked point on the ground - this gives me how far, vertically, the player is from the ground. If this distance is farther than 20 units, I limit it to 20 units anyway since that's the maximum value I want.
Then I proceed to calculate "what percentage of 20 units is the player away from the ground". I calculate it with this: "distance / 20.0" (since 'distance' is always a value from 0 to 20, this division will always return a float value between 0.0 and 1.0).
The final alpha value applied to the shadow is given as "1.0 - result" of that division (when the player is at his maximum distance from the ground the division returns "1.0", so the final alpha value will be "1.0 - 1.0" = zero).
This gives the following behavior:
- If the player is 20 units or more away from the ground, the shadow will be invisible.
- If the player is touching the ground then the shadow will be fully opaque.

Last edited 2012


Cubed Inc.(Posted 2012) [#11]
Now I understand the math behind it, but I still can't translate it into code properly. I even tried your example and the shadow just stays in one place and doesn't follow the player at all. It doesn't even align with the
world. Does it matter of the shadow's a flat cube or sprite?

I know that you are very talented with Blitz3d programming and programming in general, so it's strange that your example doesn't even work with me. I understand the method behind it, but I can't translate it into code.


Kryzon(Posted 2012) [#12]
Instead of a cube it'd be more economic to just use a free-mode sprite (free-mode is set with SpriteViewMode) or a manually created quad mesh.
Both these primitives are formed by two triangles.

You should go step by step since this is your first time:

- Instead of a sprite or quad, use a small sphere as shadow object - you're first trying to get the LinePick and positioning to work. Once you see the sphere following the player like it's supposed to, proceed to using the quad and aligning it to the environment as discussed.

- Make sure the environment has a "polygon" EntityPickMode().

- If it's still not working, create a separate project exclusively to make this shadow decal functional - a crude test room. Once you have it working, add it to your game.

Last edited 2012


Cubed Inc.(Posted 2012) [#13]
Ok, i'm sorry kryzon but your code doesn't work with me. I can't get it working correctly. Could you post the whole code you used. I can't work with this one chunk of code. I seriously can't get it working.


Stevie G(Posted 2012) [#14]
Not sure if this is the issue but the Aligntovector should be on the Y-Axis (2), not (3).


Kryzon(Posted 2012) [#15]
The right axis depends on the type of object being used. If you're using a Blitz3D Cone, which starts pointing up (in other words, is 'created' pointing up), then you need axis (2).
But quads and sprites are generally created towards the camera, Z axis. So you use (3).
Since most people create the quad to face the camera (*against* positive Z axis), you also need to flip the mesh or use Double-Sided FX (16), else the moment AlignToVector is called the invisible side will be shown.

@TA: I didn't write that from a bigger code, it's a generic version you need to adapt to yours. It's as clear as it should be - you need to experiment on your own.

See if you can run this:


Last edited 2012


Yue(Posted 2012) [#16]
@Krizon

I will always be ignorant, I will always have something new to learn, the more likely it is that is an ignorant until my dying day. Thanks for the code I use to apply to impact decals on the walls by bullets or explosions =)


Cubed Inc.(Posted 2012) [#17]
Kryzon
I hope I did not offend you in anyway. I was just being honest. I did experiment with your code before. I set up a crude program to test it and it didn't work. I'm not half-as*ing, I do my part to get my game working.


Kryzon(Posted 2012) [#18]
Hi! Don't worry man, I was just being direct.

See if that demo tells it better. Consider the 'bigSphere' as your environment.


Cubed Inc.(Posted 2012) [#19]
The demo helped tremendously.
Now I have a concrete idea of linepick and i've got alot of things I can use it for. At first when I emplemented it, it had some bugs like the shadow stopping when the character jumps to high, but after playing around with the code I managed to get it working perfectly

Thats the beauty of programming. You learn so much from just playing around. Its like learning you abcs but with the wooden blocks.


Yue(Posted 2012) [#20]
This gives much flexibility to do things, such as a beacon of light projecting onto the concrete car ... xD