'Clean' Sswift Shadow demo?

Blitz3D Forums/Blitz3D Programming/'Clean' Sswift Shadow demo?

Dock(Posted 2004) [#1]
I'm currently playing around with the sswift shadow system, but I'm not having a huge amount of success. Whilst I'm not hugely experienced with coding, I'm finding it tricky to pick out what exactly _is_ and _isn't_ needed from the given demo.

Could someone with experience with the system possibly help me out and tell me what variables I _have_ to define, and which functions need to be called.

A perfect example would be a simple demo with a plane, a spinning torus casting dynamic shadows, and nothing else.

Any ideas?


.rIKmAN.(Posted 2004) [#2]
Doesn`t the demo come with code that casts shadows from moving primitives - or is that just an exe?


Dock(Posted 2004) [#3]
Yep, it comes with sample code, but there's 700+ lines of code - most of which control the demo rather than the shadow system itself. The shadow system is an include of course so that's seperate, but I'm curious as to which of these variables is called by the include and which is just to make the demo work.

I'm trying to pick through it all slowly but I'm curious as to whether anyone has anything a little more simple :)


(tu) sinu(Posted 2004) [#4]
their is also an example setup file which shows you what you need to do, comes with the demo iirc.


sswift(Posted 2004) [#5]
Dynamic shadows:
----------------

To use the shadow system call:

Cast_Shadow for each object you want to cast a shadow,
Receive_Shadow for each object you want to receive a shadow,
Cast_Light for each object you want to be a light source.

You need at least one of each to see any shadows.

Then every frame call update_shadows between updateworld and renderworld. (You call it between them because updateworld changes the positions of objects after they collide with something.)

UpdateWorld
Update_Shadows(Camera)
Renderworld


If you want an object to stop casting light, or stop receiving shadows, or stop casting shadows, use these functions:

Delete_Shadow_Caster(Entity)
Delete_Shadow_Receiver(Entity)
Delete_Light_Caster(Entity)




Static shadows:
---------------

If you want objects to cast static shadows, set up your receivers and light casters, and then call Cast_Static_Shadow for each static shadowcaster. Then call Update_Static_Shadows() ONCE. That will create the static shadows and the shadow maps required. You should do this outside your main loop.

If you want to change the static shadows later, you can delete a static shadow and disable the static shadow caster simply by calling Delete_Static_Shadow_Caster.

If you want to ADD a static shadow caster, then you'll need to call Cast_Static_Shadow for that object, and then call Update_Static_Shadows once to create the shadow.




Advanced options:
-----------------

If you are seeing Z-fighting in your scene... shadows rendering behind objects, you can adjust the variable Shadow_Nudge# at any time. The default value is 0.01. Increasing it will reduce Z fighting. Decreasing it will move the shadows closer to the surface so they don't appear to float over it.


Adjusting these global variables adjusts the amount of ambient light that affects the shadows. Higher values mean lighter shadows. Values of 0 will produce dark black shadows.

Shadow_Ambient_Light_R# = 0
Shadow_Ambient_Light_G# = 0
Shadow_Ambient_Light_B# = 0


And finally, if you want softer shadows, you can set the global variable Blur_Quality. Increasing this value improves the quality of the blur. 0 is the default. Blur_Radius# is another value you can adjust. It adjusts the radius of the blur. Larger radius + more quality = more blur. You'll have to play with these values to see how they affect the shadows. Blurring slows down shadow rendering a little.


(I've just added this to the system zip to provide some basic documentation.)


Dock(Posted 2004) [#6]
Excellent - this is precisely what I needed. Thanks Shawn!

I'm throwing together a simple example, but the Cast_Light function keeps giving the error; "Entity is not a model" from the recursivepaintentity.bb Include. Hmm...


sswift(Posted 2004) [#7]
That's not possible. Cast_Light doesn't even call that function.

[edit]
Of course, updateshadows could throw that error once you add a light to a scene with recievers and caster...
[/edit]


sswift(Posted 2004) [#8]
This should fix the problem. Replace the old version of recursivepaintentity with this one:

; -------------------------------------------------------------------------------------------------------------------------------------
; This function paints an entity and its children.
; -------------------------------------------------------------------------------------------------------------------------------------
Function RecursivePaintEntity(Entity, Brush)

	Local Child
	
	If (EntityClass$(Entity) = "Mesh") Or (EntityClass$(Entity) = "BSP") Then PaintEntity Entity, Brush
	
	For Child = 1 To CountChildren(Entity)
		RecursivePaintEntity(GetChild(Entity, Child), Brush)
	Next				

End Function

It never ceases to amaze me some of these bugs that people report... This bug for example. Has been in since I released the latest version a few weeks ago. Nobody reported it till now even though it should have affected a lot of people. And static shadows were broken for more than a month before someone reported it. :-)


IPete2(Posted 2004) [#9]
Shawn,

Could it be that no one is currently using the static side of your shadow system? I haven't looked at it yet.

I have been getting fabulous success with your shadow system on my new project (which I can't show here yet...but wait for it!)

I am hoping you will be able to sort the rotate receiver aspect of the shadow system as my life would be much easier with the current project if I could rotate the receiver.

The resoultion problem I had was fixed by making all the casters individuals. There are still a few issues I have to resolve, but overall at the moment the word "Sweeeeet!" comes to mind.

IPete2.





IPete2.


sswift(Posted 2004) [#10]
"Could it be that no one is currently using the static side of your shadow system? I haven't looked at it yet."

Well, the function above is a bug with the dynamic shadows caused by casters having pivots. And I know for a fact some people are using static shadows. :-)

Someone else was having problems with casting dyanmic shadows from objects with pivots... But I fixed that. They didn't complain when this latest version came out... But apparently if I try to paint pivots it doesn't work? Perhaps the issue only affects pivots that are children, or perhaps the latest version of Blitz made Blitz error when you try to paint a pivot instead of failing quietly.


Knotz(Posted 2004) [#11]
It never ceases to amaze me some of these bugs that people report... This bug for example. Has been in since I released the latest version a few weeks ago.


@swift
I noticed it, but was to busy to send you notice...


Dock(Posted 2004) [#12]
Okay - that seems to help! However, I'm still having no dice with the actual shadow code. I must be missing something. Here's a simple demo I put together...

;--------------------------------------
; Simple SSwift Shadow Demo
;--------------------------------------

Include "Swift Shadow System.bb"

; Initialise graphics + camera


Graphics3D 640,480,16,2
ClearTextureFilters 
cam=CreateCamera()

arenalight=CreateLight()
LightColor arenalight,100,150,150

period=1000/UPS
time=MilliSecs()-period
Const UPS=60

; Load Meshes + Textures
Global samplecone = CreateCone()
EntityColor samplecone,255,150,150

Global arena = CreateCube()
EntityColor arena,140,190,240
ScaleEntity arena,20,0.05,20
PositionEntity arena,0,-0.1,0
EntityShininess arena,0.5


; Position and animate Entities
PositionEntity cam,0,1,0
RotateEntity   cam,0,0,0
PositionEntity arena,0,0,0
PositionEntity samplecone,0,2,8
RotateEntity   samplecone,0,180,0
PositionEntity arenalight,0,20,0


; Initialise Shadow Casters/REceivers
Cast_Light(arenalight,500)
Cast_Shadow(samplecone)
Receive_Shadow(arena)


.MainLoop

Repeat

	; Camera Movement code
	If KeyDown(200) MoveEntity cam,0,0,0.15
	If KeyDown(208)	MoveEntity cam,0,0,-0.15
	If KeyDown(203) TurnEntity cam,0,2,0
	If KeyDown(205) TurnEntity cam,0,-2,0


	; Frame Limiting Code Start	
	Repeat
		elapsed=MilliSecs()-time
	Until elapsed
	ticks=elapsed/period
	tween#=Float(elapsed Mod period)/Float(period)
	
	For k=1 To ticks
		time=time+period	
		If KeyHit(1) End
		UpdateWorld
	Next
	; End Frame Limiting

	TurnEntity samplecone,1,2,3
	
	Update_Shadows(cam)    		; Update Shadows
	RenderWorld tween			; Draw World (tween indicates 60fps)
	Flip

Forever


I think the code ought to be casting a shadow, but I don't see it. What am I doing wrong?


sswift(Posted 2004) [#13]
You cannot use scaleentity or rotateentity on receivers.

Use scalemesh instead.


sswift(Posted 2004) [#14]
Here this version will work. You also needed to update the normals on the cube you created because Blitz doesn't set them correctly, and the system needs the normals to determine how to offset the shadow from the surface.

;--------------------------------------
; Simple SSwift Shadow Demo
;--------------------------------------

Include "Swift Shadow System - 115.bb"

; Initialise graphics + camera


Graphics3D 640,480,16,2
ClearTextureFilters 
cam=CreateCamera()

arenalight=CreateLight()
LightColor arenalight,100,150,150

period=1000/UPS
time=MilliSecs()-period
Const UPS=60

; Load Meshes + Textures
Global samplecone = CreateCone()
EntityColor samplecone,255,150,150

Global arena = CreateCube()
EntityColor arena,140,190,240
ScaleMesh arena,20,0.05,20
PositionEntity arena,0,-0.1,0
UpdateNormals Arena

; Position and animate Entities
PositionEntity cam,0,1,0
RotateEntity   cam,0,0,0
PositionEntity arena,0,0,0
PositionEntity samplecone,0,2,8
RotateEntity   samplecone,0,180,0
PositionEntity arenalight,0,20,0


; Initialise Shadow Casters/REceivers
Cast_Light(arenalight,500)
Cast_Shadow(samplecone)
Receive_Shadow(arena)


.MainLoop

Repeat

	; Camera Movement code
	If KeyDown(200) MoveEntity cam,0,0,0.15
	If KeyDown(208)	MoveEntity cam,0,0,-0.15
	If KeyDown(203) TurnEntity cam,0,2,0
	If KeyDown(205) TurnEntity cam,0,-2,0


	; Frame Limiting Code Start	
	Repeat
		elapsed=MilliSecs()-time
	Until elapsed
	ticks=elapsed/period
	tween#=Float(elapsed Mod period)/Float(period)
	
	For k=1 To ticks
		time=time+period	
		If KeyHit(1) End
		UpdateWorld
	Next
	; End Frame Limiting

	TurnEntity samplecone,1,2,3
	
	Update_Shadows(cam)    		; Update Shadows
	RenderWorld tween			; Draw World (tween indicates 60fps)
	Flip

Forever



Dock(Posted 2004) [#15]
Thanks! That has fixed it for me, and definitely helped me realise what I'm doing with this system. My shadows are not black at the moment, but I'm sure I can figure out how to fix that :)

I think it would be a good idea to include a very simple example such as this as a demo, so that users can see how easily shadows can be set up.


Dock(Posted 2004) [#16]
Actually - maybe I spoke too soon. ^^; My shadow is colour, and seems to chop in half from certain angles. I tried adjusting the ambient colour and shadow nudge, but this didn't help either issue.

Sorry for the hassle, I really appreciate the help.


sswift(Posted 2004) [#17]
If your shadow is colored then that is the ambient light setting at work most likely. OR, you have set the translucency option on cast_shadow. As far as the shaodw being chopped in half, try a shadow nudge of 1.0. If that doesn't have the shadow floating off the ground, then send me a sample program that displays this issue. There is also a caster_plane nudge parameter which might have an affect. Read about that in the source.

If you don't mind me including your code with the shadow system then maybe I'll include it in the zip for future versions.


BODYPRINT(Posted 2004) [#18]
If you updatenormals on the cone it looks a bit nicer too :-)


Dock(Posted 2004) [#19]
SSwift - Sure, I have no problems with you including the code!

I think my coloured shadow problem has more to do with B3D Pipeline and my model produced in that than anything else. I swapped in a different model (the boy on the right) and it works fine.



I'll have to figure out what it is in my B3D Pipeline export that is causing this to happen. Nothing I can do can change the value of the shadow - and indeed I don't even seem to be able to change the texture of the model! The textures are pre-loaded and won't be overwritten by entitytexture. Strange!

Admittedly my shadows are going nuts at higher resolutions than 256, but that's probably something else to blame. :)


HNPhan(Posted 2004) [#20]
SSWift, are you gonna add stencil shadows support now that 1.88 seems to enable us to do so?


Dreamora(Posted 2004) [#21]
Dock: I have the same problem with shadows on higher resolution.

Tought it was just me, but perhaps it is a Blitz3D 1.87 problem or a problem with my catalyst driver version ...

it happens even in the simple demo ... a lot of strange shadow artifacts appear ( "shadow lines" like shadows from small cylinders )


sswift(Posted 2004) [#22]
"The textures are pre-loaded and won't be overwritten by entitytexture. Strange!"

Try this. EntityTexture is always overridden by a surface texture.

; -------------------------------------------------------------------------------------------------------------------------------------
; This function paints an entity and its children.
; -------------------------------------------------------------------------------------------------------------------------------------
Function RecursivePaintEntity(Entity, Brush)

	Local Child
	Local Surface

	; Paint all surfaces in this mesh.	
	If (EntityClass$(Entity) = "Mesh") Or (EntityClass$(Entity) = "BSP") 
		For Surface = 1 To CountSurfaces(Entity)	
			PaintSurface GetSurface(Entity, Surface), Brush
		Next
	EndIf	
	
	For Child = 1 To CountChildren(Entity)
		RecursivePaintEntity(GetChild(Entity, Child), Brush)
	Next				

End Function



sswift(Posted 2004) [#23]
"Admittedly my shadows are going nuts at higher resolutions than 256, but that's probably something else to blame. :)"

Shadow resolution is limited by the vertical resolution of the video mode you are in. If you are running a 640x480 window, then you won't be able to render shadow textures that are 512 tall. That is a limitation of Blitz.

Also, keep in mind that higher res shadows can slow things down significantly, because copying the rendered images to a texture is slow. Each 512x512 texture is equivalent to four 256x256 textures.

On the other hand, you can improve the quality of the textures your characters by blurring the textures, and that, while costing four or eight times the fill rate, only equates to two 256x256 texture copies.

Also, make sure that the center of your mesh is the center of your model! If your characters in mesh space are standing on 0,0,0 onstead of centered on 0,0,0, then your shadow textures will appear to be half the resolution they would otherwise appear to be. Basically the character ends up being zoomed out more when the texture is rendered because the vertcies in the model tell the system the model is bigger than it really is.


sswift(Posted 2004) [#24]
"SSWift, are you gonna add stencil shadows support now that 1.88 seems to enable us to do so?"


If I did something with stencil shadows it would be a completely new system. But I'm not yet convinced that even with this limited access to the stencil buffer that we can do stencil shadows in Blitz. In order to do stencil shadows one needs to be able to either build shadow volumes manually, which we cannot do in Blitz for animated entities, or one needs to be able to "stretch" an entity along an arbitrary axis to form a shadow volume that way. Stretching an entity I believe involves something more complex than simply scaling an entity along an abritrary axis, and even if it does merely involve that, I'm not even sure we can scale an entity along an arbitrary axis. In the worst case scenario we need access to vertex shaders in order to do the stretching.

I wouldn't count on anyone getting real stencil shadows working in Blitz anytime soon. The stuff you've seen people do so far doesn't work on all video cards, has visual artifacts, and only makes stuff inside an entity appear shadowed, rather than make the entity itself appear to cast shadows.


Dock(Posted 2004) [#25]
Hurrah - I got it working. Thanks for the input, it really helped me nail down what works and what doesn't. Moving the character into the 0,0,0 position helped a lot, and I figured out what I was doing in B3D Pipeline to cause the shadows to be transparent (I think it was to do with the materials being set as alpha instead of multiply).



However, because I'm never satisfied... my Blur Shadow variable [still] doesn't work at all! Anything between 0 and 0.5 gives the normal result, and anything higher kills the shadow entirely. Any idea why this is?


sswift(Posted 2004) [#26]
In the blur shadow function, throw a flip in after the renderworld. That will show you what the blurred texture looks like. Then you can adjust the values more easily.

It's possible there is a bug, I'll look into it.


sswift(Posted 2004) [#27]
Eh... I think there is a bug, I'm trying to fix it.


Dock(Posted 2004) [#28]
I'm glad it's not just me then :) Thanks.


AdrianT(Posted 2004) [#29]
used to get the weird coloured shadows when we had vertex colours on the model casting. This was back in the AA days over a year ago though.


sswift(Posted 2004) [#30]
Okay, replace the blurtexture.bb file with this:

; -------------------------------------------------------------------------------------------------------------------
; This function blurs a texture using a technique that takes advantage of 3D acceleration.  
;
; * You MUST hide all other cameras before calling this function!
; * You MUST reset your texture's blending mode, scale, and position after calling this function!
;
; Texture is the texture you want blurred.
;
; Blur_Quality defines the quality of the blur.  1 = 4 passes, 2 = 8 passes, 3 = 12 passes, etc.
;
; 	(The reason that the passes are in multiples of four is because interference artifacts are created when
; 	the number of passes is not a multiple of four... meaning that ten passes will actually look far worse
; 	than eight.)
;
; Blur_Radius# defines the radius of the blur, in pixels, assuming a map size of 256x256.
;
;	(Ie, a radius of 16 will be the same width regardless of whether the texture is 16x16 or 512x512.  It will
; 	only be exactly 16 pixels wide if the map is 256x256.)
; -------------------------------------------------------------------------------------------------------------------
Function BlurTexture(Texture, Blur_Quality, Blur_Radius#)

	; This is used for temporary storage of the meshes used for soft shadow blurring.
	Local BlurMesh[16*4]
	Local Loop
	Local Blur_Cam

	Local BLUR_CAM_X# = 65536.0
	Local BLUR_CAM_Y# = 65536.0
	Local BLUR_CAM_Z# = 0.0

	; If blurring is enabled...
	If Blur_Quality > 0

		Blur_Cam = CreateCamera()

		; Set the camera viewport to the same size as the texture.		
		CameraViewport Blur_Cam, 0, 0, TextureWidth(Texture), TextureHeight(Texture)

		; Set the camera so it clears the color buffer before rendering the texture.
		CameraClsColor Blur_Cam, 0, 0, 0
		CameraClsMode  Blur_Cam, True, True						

		; Set the camera's range to be very small so as to reduce the possiblity of extra objects making it into the scene.
		CameraRange Blur_Cam, 0.1, 100
	
		; Set the camera to zoom in on the object to reduce perspective error from the object being too close to the camera.
		CameraZoom Blur_Cam, 16.0

		; Aim camera straight down.	
		RotateEntity Blur_Cam, 90, 0, 0, True
		
		; Position the blur camera far from other entities in the world.
		PositionEntity Blur_Cam, BLUR_CAM_X#, BLUR_CAM_Y#, BLUR_CAM_Z#
		
		; Create the sprites to use for blurring the shadow maps.
		For Loop = 0 To (Blur_Quality*4)-1
			BlurMesh[Loop] = CreateSprite()
		Next
		
		; Set the texture blend mode to multiply.
		TextureBlend Texture, 2
												
		; Scale the texture down because we scale the sprites up so they fill a larger area of the
		; screen.  (Otherwise the edges of the texture are darker than the middle because they don't
		; get covered.
		ScaleTexture    Texture, 0.5, 0.5
		PositionTexture Texture, 0.5, 0.5
						
		; Blur texture by blitting semi-transparent copies of it on top of it.
		BlurRadius# = Blur_Radius# * (1.0 / 256.0)
		BlurAngleStep# = 360.0 / Float(Blur_Quality*4)

		; Normally we would just divide 255 by the number of passes so that adding all the passes
		; together would not exceed 256.  However, if we did that, then we could not have a number of
		; passes which does not divide 256 evenly, or else the error would result in the white part of
		; the image being slightly less than white.  So we round partial values up to ensure that
		; white will always be white, even if it ends up being a little whiter than white as a result
		; when all the colors are added, since going higher than white just clamps to white.
		BlurShade = Ceil(255.0 / Float(Blur_Quality*4))
		
		; Place each of the blur objects around a circle of radius blur_radius.
		For Loop = 0 To (Blur_Quality*4)-1
		
			EntityTexture BlurMesh[Loop], Texture
			EntityFX BlurMesh[Loop], 1+8
			EntityAlpha BlurMesh[Loop], 1.0 / Float(Loop+1)
			ScaleSprite BlurMesh[Loop], 2, 2
																						
			BlurAngle# = BlurAngleStep# * Float(Loop) + 180.0*(Loop Mod 2)
							
			Xoff# = BlurRadius# * Cos(BlurAngle#)
			Yoff# = BlurRadius# * Sin(BlurAngle#)

			PositionEntity BlurMesh[Loop], BLUR_CAM_X# + Xoff#, BLUR_CAM_Y# - 16.0, BLUR_CAM_Z# + Yoff#, True
					
		Next
					
		; Render the new texture.
		RenderWorld
				
		; Copy the new texture from the screen buffer to the texture buffer.		
		CopyRect 0, 0, TextureWidth(Texture), TextureHeight(Texture), 0, 0, BackBuffer(), TextureBuffer(Texture)
					
		; Free the blur entities.
		For Loop = 0 To (Blur_Quality*4)-1
			FreeEntity BlurMesh[Loop]
		Next

		; Free the blur camera.
		FreeEntity Blur_Cam
			
	EndIf

End Function

It was just the texture blending mode which was being changed to add by the shadow system, but the blut rexture function wasn't setting it to multiply like it needs to be.

Blurtexture seems to create a more significant speed hit than I thought it would. I suspect it's just the slow 2D copy operation causing that, in which case it's unavoidable unless someone figures out how to render directly to textures or a way to blur a shadow texture without actually rendering twice.

"Blur Shadow variable [still] doesn't work at all! Anything between 0 and 0.5 gives the normal result"

Oh and if that is happening, then you're doing somethign wrong. Blur_Quality must be an integer. 1 or 2 are good values. Blur_Radius# is the floating point value, and 1 corresponds to a 1 pixel wide blur on a 256x256 texture. By defualt it is set to 16. 4 would soften the edges only slightly, 8 might be better than the default of 16 for general use.


Dock(Posted 2004) [#31]
I was only experimenting with Blur_Quality because 0-16 (as it says in the include) didn't work, so I tried between 0-1.

Blur seems to work very nicely now, thanks for fixing it. You're right about it having a nasty speed hit (especially at extreme settings) but at 256 texture with quality = 2 and Radius = 8, the quality of shadow is very nice (and the speed isn't tooo bad). ^_^