create a shadow volume

Blitz3D Forums/Blitz3D Programming/create a shadow volume

bytecode77(Posted 2006) [#1]
hello!
i have got a question about stencil shaodwing!
i managed to make a stencil buffer demo, but now i have to make a function which creates shadow volumes on the fly!
how can i do that?
note: the shadow volume should only be created with the mesh edges, and not with the whole polygons of the mesh, because it wont work ;)

should look like this:



jfk EO-11110(Posted 2006) [#2]
First let me say I'm not an expert in stencil shadows, not at all.
A simple solution is to clone the vertices of the mesh and move the clones away from the origin in the direction of the lights rays.
Although this will use all vertices, something you don't want, as you said. Depending on the way you are doing the stencil shadows this method may work noless.

Finding the mesh edges may require a lot of calculations that will slow down things remarkably. Probably it would be easier to use a lowpoly copy of the mesh for this.

I know some people do know how to do these things, but unfortunately they are not around or something.

Maybe you'll find some information on other forums and pages, dedicated to stencil shading.


bytecode77(Posted 2006) [#3]
well, i was searching my whole life for the edge recognizing code, but no, there wasn't any results :(

*cry*


jfk EO-11110(Posted 2006) [#4]
I am sure there are several ways to do it. Tho it may be hard to find a GOOD one, a fast one.

http://www.gamasutra.com/features/19991115/bestimt_freitag_01.htm
http://www.caip.rutgers.edu/~kuttuva/shadow_volumes.html
http://en.wikipedia.org/wiki/Shadow_volume
http://www.gamedev.net/reference/articles/article1873.asp
http://downloads.gamedev.net/pdf/VolumeShadowsTutorial-2036.pdf
http://www.planetquake.com/q3empire/files/papers/Realtime_Shadowcasting.pdf
this one even has a link to a quake 1 modified engine that has stencil shadows in:
http://www.aceshardware.com/read_news.jsp?id=60000466


JoshK(Posted 2006) [#5]
I am working on this right now. Here is my approach:

-Create a list of the normal of every triangle in the mesh. There is an equation to get the normal or plane equation from the 3 points of a triangle.

-Create a list of every edge in the mesh, consisting of an indice a and an indice b. For each triangle, check for an existing edge between the points ab, bc, and ca. Test using vertex positions, since you might have concurrent edges that use different vertices. You also need a list of which two triangle this edges falls between.

-Create a list of dotproducts between your light vector and your triangle normal. This tells you whether a triangle faces towards the light or away.

-Look for edges between a triangle that faces the light, and one that faces away. These are your outline edges.

www.planetquake.com/q3empire/files/papers/Realtime_Shadowcasting.pdf
Wow. What a bunch of crap hyperbole. This paper is laughable.


bytecode77(Posted 2006) [#6]
thx, i will see what to do :)


big10p(Posted 2006) [#7]
-Create a list of dotproducts between your light vector and your triangle normal. This tells you whether a triangle faces towards the light or away.
Won't you have to rebuild this list every time the light and/or mesh moves - possibly, every frame? Isn't doing this very slow?


Chevron(Posted 2006) [#8]
I must admit that when i first saw this post i was convinced it was an April Fools joke as your picture looks just a litle bit phallic to me.


octothorpe(Posted 2006) [#9]
Off topic: Don't you need shaders to pull this off? I didn't know Blitz allowed shaders?


bytecode77(Posted 2006) [#10]
lol :D
but an april fools joke is that not :(...


jfk EO-11110(Posted 2006) [#11]
phallic in a microsoft way.

>>Wow. What a bunch of crap hyperbole. This paper is laughable.<<
Sorry, I didn't read it all.


JoshK(Posted 2006) [#12]
Won't you have to rebuild this list every time the light and/or mesh moves - possibly, every frame? Isn't doing this very slow?

I thought the same, but this is how it's done. Kind of nice to do something with the CPU for a change.

JFK, no offense meant, I just was annoyed with the psuedo-acedemic tone of that paper.


Scherererer(Posted 2006) [#13]
This is something I made a while ago. It is rather versitile, as you can apply shadow properties with a simple function and it is easy to implement and has support for colored shadows and transparent object shadows. Unfortunately, however, it is slow. It is kinda a modified version of Halo's demo that uses types instead of arrays plus some other goodies to make it more portable (and practical). The other problem is it can take a while to load a mesh with a large number of tris because of the algorithm it uses. It is in desperate need for a rewrite but you can use it for whatever you want.

;;;;;;;;;;;;;;;;;;;;;
;Shadows............;
;Michael Scherer....;
;[Tainted Instinct].;
;;;;;;;;;;;;;;;;;;;;;



Global FPS,LastCheck,Frames
Function GetFPS()
	Frames = Frames + 1
	
	If MilliSecs() > LastCheck+1000 Then
		LastCheck = MilliSecs()
		FPS = Frames
		Frames = 0
	EndIf
	Return FPS
End Function




Graphics3D 800,600

AntiAlias True

;COLLISON TYPES
Const shadowCOL = 1, terrainCOL = 2

;SHADOWMESH/SHADOWVERTEX TYPES
Type shadowmesh
	Field entity,shadow					;the entity handle and its shadow
	Field alpha#						;for meshes that are see-through
End Type

Type shadowvertex
	Field surf,tri,index				;exact location of vertex
	Field entity						;the pivot
	Field parent						;which shadowmesh this vertex belongs to
End Type

;CREATE A SHADOWMESH
Function shadowmesh(mesh,alpha#,r,g,b)
	s.shadowmesh = New shadowmesh
	
	s\entity = mesh
	s\shadow = CopyMesh(mesh)
	EntityFX s\shadow,1
	EntityColor s\shadow,r,g,b
	EntityAlpha s\shadow,.5				;changes based on distance from light
	s\alpha# = alpha#
	
	;get all vertex positions
	For sf = 1 To CountSurfaces(s\entity)
		surf = GetSurface(s\entity,sf)
		For t = 0 To CountTriangles(surf)-1
		For i = 0 To 2
			;vert = TriangleVertex(surf,t,i)
			sv.shadowvertex = New shadowvertex
			sv\surf = sf : sv\tri = t : sv\index = TriangleVertex(surf,t,i)
			
			;sv\x# = VertexX#(surf,i) : sv\y# = VertexY#(surf,i) : sv\z# = VertexZ#(surf,i)
			sv\entity = CreatePivot()
			PositionEntity sv\entity, VertexX#(surf,sv\index), VertexY#(surf,sv\index), VertexZ#(surf,sv\index)
			
			EntityType sv\entity,shadowCOL
			;EntityRadius sv\entity,.001
			
			sv\parent = Handle(s)
		Next
		Next
	Next
	
	;eliminate redundant vertices
	For s1.shadowvertex = Each shadowvertex
		For s2.shadowvertex = Each shadowvertex
			If Handle(s1) <> Handle(s2)
				If s1\parent = s2\parent And s1\surf = s2\surf And s1\index = s2\index
					FreeEntity s2\entity
					Delete s2
				EndIf
			EndIf
		Next
	Next
	
	Return Handle(s)		;return the shadowmesh handle just in case
End Function

;THE LIGHT TYPE
Type shadowlight
	Field entity
	Field x#,y#,z#
	Field range#
End Type

;BUILD A LIGHT
Function shadowlight(x#,y#,z#,range#)
	l.shadowlight = New shadowlight
	
	l\entity = CreateLight(2)
	LightRange l\entity,range#
	PositionEntity l\entity,x#,y#,z#
	
	l\x# = x# : l\y# = y# : l\z# = z#
	l\range# = range#
	
	Return Handle(l)		;return the lights handle just in case
End Function

;a shadow mesh
cube = CreateSphere()
;ScaleMesh cube,.2,.2,.2
EntityColor cube,255,0,0
EntityAlpha cube,.5
shadowmesh(cube,.5,255,0,0)						;set it as a shadow mesh
PositionEntity cube,0,0,0

cube2 = CreateCube()
PositionEntity cube2,3,0,-3
shadowmesh(cube2,1,0,0,0)

cube3 = CreateSphere(3)
PositionEntity cube3,-3,0,-3
shadowmesh(cube3,1,0,0,0)

;a light
light = shadowlight(2,5,2,10)

lightspright = CreateSprite()	;he he lightspright, get it? just a placemarker
PositionEntity lightspright,2,5,2
tex=CreateTexture(128,128)
SetBuffer TextureBuffer(tex)
Rect 0,0,28,128
Rect 0,100,128,28
SetBuffer BackBuffer()
EntityTexture lightspright,tex
FreeTexture tex

;plane to put the shadows on
plane = CreatePlane(16)
EntityType plane,terrainCOL
PositionEntity plane,0,-5,0
tex=CreateTexture(128,128)
SetBuffer TextureBuffer(tex)
Color 0,128,0
Rect 0,0,128,128

SetBuffer BackBuffer()
EntityTexture plane,tex
FreeTexture tex


;camera to see the shadows with
Global cam = CreateCamera()
RotateEntity cam,15,0,0
PositionEntity cam,0,5,-20

;LET THE VERTICES COLLIDE WITH TERRAIN
Collisions shadowCOL,terrainCOL,2,1

SetBuffer BackBuffer()

CameraClsColor cam,0,255,255

Color 255,255,255
While Not KeyDown(1)
	PositionShadows()
	
	UpdateWorld
	RenderWorld
	
	UpdateShadows()
	
	l.shadowlight = Object.shadowlight(light)
	
	If KeyDown(205) Then MoveEntity l\entity, .1,0,0
	If KeyDown(203) Then MoveEntity l\entity,-.1,0,0
	
	If KeyDown(208) Then MoveEntity l\entity,0,0,-.1
	If KeyDown(200) Then MoveEntity l\entity,0,0, .1
	
	PositionEntity lightspright, EntityX#(l\entity),EntityY#(l\entity),EntityZ#(l\entity)
	
	TurnEntity cube2,.1,.2,.3
	TurnEntity cube3,.2,.3,.4
	
	;debugging------------------
	If CommandLine$() = "/debug"
	For l.shadowlight = Each shadowlight
		CameraProject cam,l\x#,l\y#,l\z#
		x# = ProjectedX() : y# = ProjectedY()
		WritePixel ProjectedX(),ProjectedY(),$ffff00
		
		For sv.shadowvertex = Each shadowvertex
			CameraProject cam,EntityX#(sv\entity, True),EntityY#(sv\entity, True),EntityZ#(sv\entity, True)
			Line x#,y#,ProjectedX(),ProjectedY()
		Next
	Next
	EndIf
	
	Text 0,0,GetFPS()
	
	;Screen Shot
	If KeyHit(88)
		SaveBuffer(BackBuffer(),"screen"+s+".bmp")
		s = s + 1
	EndIf
	
	Flip
Wend

;POSITION ALL SHADOW'S VERTICES
Function PositionShadows()
	;hide the shadows
	For s.shadowmesh = Each shadowmesh
		HideEntity s\shadow
	Next
	
	piv = CreatePivot()
	
	For l.shadowlight = Each shadowlight
		l\x# = EntityX#(l\entity, True) : l\y# = EntityY#(l\entity, True) : l\z# = EntityZ#(l\entity, True)
		PositionEntity piv,l\x#,l\y#,l\z#
		For s.shadowmesh = Each shadowmesh
			entdist# = EntityDistance#(s\entity,piv)
			If entdist# <= l\range#
				;since we are in the light range, show the shadow
				ShowEntity s\shadow
				
				;apply appropriate alpha level
				EntityAlpha s\shadow, (1-(entdist#/l\range#))*s\alpha#
				
				;now we position its vertices
				For sv.shadowvertex = Each shadowvertex
					If sv\parent = Handle(s)						;Make sure the vertex belongs to this mesh
						PositionEntity sv\entity,l\x#,l\y#,l\z#		;put the vertex at the light
						
						surf = GetSurface(s\entity,sv\surf)
						x# = VertexX#(surf,sv\index) : y# = VertexY#(surf,sv\index) : z# = VertexZ#(surf,sv\index)
						TFormPoint( x#, y#, z#, s\entity, 0 )
						x# = TFormedX#() : y# = TFormedY#() : z# = TFormedZ#()
						
						PositionEntity piv,x#,y#,z#
						PointEntity sv\entity,piv
						
						ResetEntity sv\entity
						
						MoveEntity sv\entity,0,0,99999
					EndIf
				Next
			EndIf
		Next
		If CommandLine$() = "/debug"
		CameraProject cam,l\x#,l\y#,l\z#
		WritePixel ProjectedX(),ProjectedY(),$ffff00,FrontBuffer()
		EndIf
	Next
	
	FreeEntity piv
End Function

;UPDATE ALL SHADOWS
Function UpdateShadows()
	For s.shadowmesh = Each shadowmesh
		For sv.shadowvertex = Each shadowvertex
			If sv\parent = Handle(s)
				surf = GetSurface(s\shadow,sv\surf)
				
				VertexCoords( surf,sv\index, EntityX#(sv\entity, True), EntityY#(sv\entity, True), EntityZ#(sv\entity, True) )
				
				If CommandLine$() = "/debug"
				CameraProject cam,EntityX#(sv\entity),EntityY#(sv\entity),EntityZ#(sv\entity)
				WritePixel ProjectedX(),ProjectedY(),$ff0000,BackBuffer()
				
				verts = verts+1
				EndIf
			EndIf
		Next
		
		UpdateNormals s\shadow
	Next
	If CommandLine$() = "/debug"
		Text 0,0,verts
	EndIf
End Function



jfk EO-11110(Posted 2006) [#14]
Thanks for sharing, I got to try this asap.

Right now I have an other problem. I tried to write a very
simple solution for the silhouette volume creation. Although
my method is slow, it's nice to see how simple it could be.

Unfortunately there is a probem, that's why I am asking for
help here. Let me explain the method first:

for every triangle in the shadowcaster, two vertex-triangles
(without faces)are created in a temp. shadow volume mesh.
the 2nd tris of them is moved away from the light, by a
pseudo-infinite range. Then the 2 vertex-trs are connected
by 6 triangle faces, covering every potential quad between
the 2 tris.
Now this is a bunch of triangles, especially hen used with
eg. a sphere source mesh.

We also NEED to remove the enclosed triangles to get a
proper silhouette volume that can be used for pseudo-stencil
operations.

So a pivot is positioned just behind the light. Then a
Linepick is performed from the pivot to each Triangle of the
shadow volume. THe Source mesh is also pickable, so this way
(in theory) it will only pick silhouette (edge) triangles.

A further volume mesh is created, containing all
successfully picked tris. These should be only shilhouette
tris.

That's the theory. The linepicks may be slow, but it's so
dead simple that I had to try it.

Now, unfortunately the reality doesn't care too much about
theories, therefor it doesn't work correctly: some tris that
should be detected are missing, and some are detected that
should be fully obscured.

Here's a screenshot:


I really wonder is there a bug in my code (tho I tried to
find one for hours), or is there something wrong with my
theory, or (that's what I guess) is this caused by rounding
errors?

Here's the sourcecode, experimental of course. If anybody
can solve this problem this would be fantastic.
;create shadow volume with the help of Linepick:
;-----------------------------------------------


; make silhouette mesh / shadow volume

; works partially, pretty slow with high poly stuff (linepick exponential slowdown).
; bugous behaviour of PickedTriangle, probably due to rounding errors.

Graphics3D 640,480,32,2
SetBuffer BackBuffer()




Global helper=CreatePivot()

Global infi#=50 ; length shadow volume
Global pick_radius#=0.1 
Global pick_back#=100 ; pick from n behind light
Global divi#=3.0


plane=CreatePlane() ; map
TranslateEntity plane,0,-5,0
EntityColor plane,0,255,0

Global camera=CreateCamera()
TranslateEntity camera,0,0,-12

light=CreateLight()
RotateEntity light,45,45,45

cube=CreateSphere(5) ;CreateCube() ; object to cast shadow
RotateEntity cube,45,45,0
EntityPickMode cube,2

lime=CreateSphere() ; position of light
ScaleEntity lime,0.1,0.1,0.1
PositionEntity lime,4,4,0

volume=make_volume(cube,lime)

RenderWorld()
Text 0,0,TrisRendered()
Flip
WaitKey()
End




Function make_volume(source,lite)
 ; for every triangle create 2 tris in the shadow volume, where one is 
 ; transposed by "infi" in the direction of lightrays
 tri_count=0
 vol=CreateMesh()
 For s=1 To CountSurfaces(source)
  su=GetSurface(source,s)
  su2=CreateSurface(vol)
  For tri=0 To CountTriangles(su)-1
   vx0#=VertexX(su,TriangleVertex(su,tri,0))
   vy0#=VertexY(su,TriangleVertex(su,tri,0))
   vz0#=VertexZ(su,TriangleVertex(su,tri,0))

   vx1#=VertexX(su,TriangleVertex(su,tri,1))
   vy1#=VertexY(su,TriangleVertex(su,tri,1))
   vz1#=VertexZ(su,TriangleVertex(su,tri,1))

   vx2#=VertexX(su,TriangleVertex(su,tri,2))
   vy2#=VertexY(su,TriangleVertex(su,tri,2))
   vz2#=VertexZ(su,TriangleVertex(su,tri,2))
   
   v0=AddVertex(su2,vx0,vy0,vz0)
   v1=AddVertex(su2,vx1,vy1,vz1)
   v2=AddVertex(su2,vx2,vy2,vz2)

   TFormPoint vx0,vy0,vz0,source,0
   PositionEntity helper,TFormedX#(),TFormedY#(),TFormedZ#()

   PointEntity helper,lite
   MoveEntity helper,0,0,-infi
   TFormPoint EntityX(helper),EntityY(helper),EntityZ(helper),0,source
   v0_b=AddVertex(su2,TFormedX#(),TFormedY#(),TFormedZ#())

   TFormPoint vx1,vy1,vz1,source,0
   PositionEntity helper,TFormedX#(),TFormedY#(),TFormedZ#()

   PointEntity helper,lite
   MoveEntity helper,0,0,-infi
   TFormPoint EntityX(helper),EntityY(helper),EntityZ(helper),0,source
   v1_b=AddVertex(su2,TFormedX#(),TFormedY#(),TFormedZ#())

   TFormPoint vx2,vy2,vz2,source,0
   PositionEntity helper,TFormedX#(),TFormedY#(),TFormedZ#()

   PointEntity helper,lite
   MoveEntity helper,0,0,-infi
   TFormPoint EntityX(helper),EntityY(helper),EntityZ(helper),0,source
   v2_b=AddVertex(su2,TFormedX#(),TFormedY#(),TFormedZ#())

   AddTriangle su2, v0  ,v1  ,v1_b
   AddTriangle su2, v0_b,v0  ,v1

   AddTriangle su2, v1_b,v1,v2_b
   AddTriangle su2, v1,v2,v2_b

   AddTriangle su2, v2,v0_b,v2_b
   AddTriangle su2, v2,v0,v0_b

   tri_count=tri_count+6

  Next
 Next
 UpdateNormals vol
 EntityPickMode vol,2
 EntityFX vol,1 
 EntityColor vol,0,0,255
 RotateEntity vol, EntityPitch(source),EntityYaw(source),EntityRoll(source),1


; vol is now a shadow volume, containing the silhouette and a lot of enclosed, hidden tris.

;Return ; you may return here to watch this state...
; -------------------------------


; create a copy without the enclosed tris:


 vol3=CreateMesh()
 su3=CreateSurface(vol3)

 PositionEntity helper ,EntityX(lite),EntityY(lite),EntityZ(lite),1
 PointEntity helper,source,0
 MoveEntity helper,0,0,-pick_back


 ; check the visibility of every triangle of the shadow volume from one step behind the light 
 ; using Linepicks:
 ; only silhouette tris should be pickable here! (since the source mesh obscures inner parts, 
 ; as well as the silhouette tris will do so - at least in theory o_O)

 For i=0 To tri_count-1
  x0#=VertexX(su2,TriangleVertex(su2,i,0))
  y0#=VertexY(su2,TriangleVertex(su2,i,0))
  z0#=VertexZ(su2,TriangleVertex(su2,i,0))

  x1#=VertexX(su2,TriangleVertex(su2,i,1))
  y1#=VertexY(su2,TriangleVertex(su2,i,1))
  z1#=VertexZ(su2,TriangleVertex(su2,i,1))

  x2#=VertexX(su2,TriangleVertex(su2,i,2))
  y2#=VertexY(su2,TriangleVertex(su2,i,2))
  z2#=VertexZ(su2,TriangleVertex(su2,i,2))

  x#=(x0+x1+x2)/divi ; get a triangles interpolated center point
  y#=(y0+y1+y2)/divi
  z#=(z0+z1+z2)/divi

  TFormPoint(x,y,z,vol,0)

  he=helper
  p=LinePick(EntityX#(he),EntityY#(he),EntityZ#(he)  ,TFormedX#()-EntityX#(he),TFormedY#()-EntityY#(he),TFormedZ#()-EntityZ#(he),pick_radius#)

  If (p=vol)
   If PickedTriangle()=i ; did we pick the tris of the corresponding center point?
    v0=AddVertex(su3,x0,y0,z0) ; yes, then it must be a silhouette tris
    v1=AddVertex(su3,x1,y1,z1)
    v2=AddVertex(su3,x2,y2,z2)
    AddTriangle(su3,v0,v1,v2)
   EndIf
  EndIf
 Next

 FreeEntity vol ; release old volume
 vol=vol3
 EntityColor vol,255,0,0
 EntityFX vol,1 Or 16
 EntityAlpha vol,0.5
 RotateEntity vol, EntityPitch(source),EntityYaw(source),EntityRoll(source),1
 Return vol
End Function




jfk EO-11110(Posted 2006) [#15]
Off topic: Don't you need shaders to pull this off? I didn't know Blitz allowed shaders?


Actually it's possible to do a kind of stencil shadows in plain blitz3d, if you once got a proper shadow volume, it can be done, tho without to access the stencil buffer, but basicly it's the same as stencil shadows.

Eurythmia released a little demo some time ago that demonstrated the trick. It had no shadow volumes, but it used a fake volume mesh.

It works this way: everything in the level is painted grey, 128,128,128 Only the shadow volume is say 64,64,64. Now when the shadow volume has EntityFX 17 and alpha 0.5, there will be two possible colors, byside the background color: 32,32,32 (that's where the renderer had to go through shadow volume faces only one time) and 16,16,16, (that's where it had to render 2 layers of faces: outer side and inner side). Practicly this results in zones that are intersecting with the ground etc. that will be 32,32,32 and zones that are not intersecting (16,16,16 or 128,128,128). The rendered screen is then grabed and Drawimage is used to draw the intersection parts only (using MaskImage).

Of course this could also be done using a semitransparent pixelperfect sprite that is covering the screen.


Stevie G(Posted 2006) [#16]
@ jfk , Interesting concept ... do you have a copy of Eurythmia's code that I can take a butchers at?

I have experimented in the past with a more optimised version of Tainted & Halo's fake stencil code but this method is only really effective when using lower poly proxy meshes to represent the shadows and with a flatish receiver mesh.

Cheers
Stevie


bytecode77(Posted 2006) [#17]
i am so happy to get that code from u :)

u are so nice, thx

can u bugfix i`t? i am trying to bugfix it, too...

PS: I will make a stencil shadow libary, and if i manage it, you'll get the comercial version for free :) as a present of mine!


jfk EO-11110(Posted 2006) [#18]
me? well I'm trying to bugfix it, but no success since some hours, so I start thinking about some work-arounds.

Linepick is also pretty slow. It's slowlyness is exponential, so when a scene has 100 Tris it may take say 1 millisec, but when it has 200 tris then it won't take 2 ms, but eg. 4ms.

Linepick may be replaced by some clever code, I even think I remember elias_t posted something in the code archives, line intersect triangle or something.

I was also thinking about to use the old unique color identification trick, but this would require a renderworld for each light, and I guess there would be even more rounding errors.

While the silhouette tris are logically visible from the helper pivots position, they build such a thin ring around the source mesh that overlapping edges of triangles may obscure one another. If a triangles width is 1 or less pixels in this perspective, they may easily cover one another. I think that's the reason for the inconsistent pickability in the code I posted.

BTW sorry cannot find Eurythmias code right now. My copy is on an other machine. But the concept is real simple.

color all meshes grey 128,128,128, entityfx 1, alpha 1.0

color the shadow volume black, entityfx 17, alpha 0.5

-render the scene
-grab the backbuffer
-set maskcolor of this img to 128,128,128
-cls the backbuffer in color 32,32,32
-drawimage to the backbuffer
-grab it again
-set maskcolor to 32,32,32

-reset the scenes original colors and fx etc.
-render the scene normally.
-drawimage the img you grabed previously. everything but the intersecting parts will be masked. The intersected parts are the shadows.

Note: to prevent shadows on the backside of the source mesh you may have to use EntityOrder, so the source mesh will be rendered in front of the shadow volume. Not sure if this will make further troubles.


bytecode77(Posted 2006) [#19]
you rock baby ;)
edit: i have got toms stencil libary from here and when your code work, i'll manage to make a shadow libary and you'll get it fisrt :)

edit2: after long searching i found a function which createes a volukme BUT the volume is created not just from the edges... so it is not useful :(

Function CreateVolume(entity, light)
;entity - the entity that casts the shadow
;light - the light entity from which the light
;
;This function is included in case somebody wants to build shadow volumes and try stuff out.
;This is fairly slow because it extrudes and caps every triangle on a mesh. This means
;that 6 times more triangles need to be made for each one on the mesh.
;(I tried faster methods but trust me its not easy)
;It may be useful only for very basic geometry.


lx#=EntityX(light,True)
ly#=EntityY(light,True)
lz#=EntityZ(light,True)

	a_vol.volumes=New volumes
		a_vol\mesh=CreateMesh()
		
		;EntityBlend a_vol\mesh,2
		;EntityFX a_vol\mesh,1
		EntityColor a_vol\mesh,0,0,0
		EntityAlpha a_vol\mesh,0.001

		volsurf=CreateSurface(a_vol\mesh)
		numsurf=CountSurfaces(entity)		
		vpos=CreatePivot()
		vposend=CreatePivot()
		VolumeDepth#=1000
		
		
		
		;For surfcount=1 To numsurf-1
		surfcount=1

		While surfcount <= numsurf
			cursurface=GetSurface(entity, surfcount)
			maxtricount=CountTriangles (cursurface)
			;For tricount=0 To tricount-1
							
			While tricount < maxtricount
			v1x#=VertexX(cursurface, TriangleVertex(cursurface, tricount, 0))
			v1y#=VertexY(cursurface, TriangleVertex(cursurface, tricount, 0))
			v1z#=VertexZ(cursurface, TriangleVertex(cursurface, tricount, 0))
			TFormPoint v1x#,v1y#,v1z#, entity, 0
			v1x#=TFormedX()
			v1y#=TFormedY()
			v1z#=TFormedZ()
			
			v2x#=VertexX(cursurface, TriangleVertex(cursurface, tricount, 1))
			v2y#=VertexY(cursurface, TriangleVertex(cursurface, tricount, 1))
			v2z#=VertexZ(cursurface, TriangleVertex(cursurface, tricount, 1))			
			TFormPoint v2x#,v2y#,v2z#, entity, 0
			v2x#=TFormedX()
			v2y#=TFormedY()
			v2z#=TFormedZ()
			
			v3x#=VertexX(cursurface, TriangleVertex(cursurface, tricount, 2))
			v3y#=VertexY(cursurface, TriangleVertex(cursurface, tricount, 2))
			v3z#=VertexZ(cursurface, TriangleVertex(cursurface, tricount, 2))
			TFormPoint v3x#,v3y#,v3z#, entity, 0
			v3x#=TFormedX()
			v3y#=TFormedY()
			v3z#=TFormedZ()
			
			
			PositionEntity vpos,v1x#,v1y#,v1z#
			PositionEntity vposend, lx#,ly#,lz#,1
			PointEntity vposend, vpos
			MoveEntity vposend, 0,0,VolumeDepth#
			v1xe#=EntityX(vposend, True)
			v1ye#=EntityY(vposend, True)
			v1ze#=EntityZ(vposend, True)
			
			PositionEntity vpos,v2x#,v2y#,v2z#
			PositionEntity vposend, lx#,ly#,lz#,1
			PointEntity vposend, vpos
			MoveEntity vposend, 0,0,VolumeDepth#
			v2xe#=EntityX(vposend, True)
			v2ye#=EntityY(vposend, True)
			v2ze#=EntityZ(vposend, True)
			
			PositionEntity vpos,v3x#,v3y#,v3z#
			PositionEntity vposend, lx#,ly#,lz#,1
			PointEntity vposend, vpos
			MoveEntity vposend, 0,0,VolumeDepth#
			v3xe#=EntityX(vposend, True)
			v3ye#=EntityY(vposend, True)
			v3ze#=EntityZ(vposend, True)
			
			v1=AddVertex(volsurf,v1x#,v1y#,v1z#)
			v1e=AddVertex(volsurf,v1xe#,v1ye#,v1ze#)
			v2=AddVertex(volsurf,v2x#,v2y#,v2z#)
			v2e=AddVertex(volsurf,v2xe#,v2ye#,v2ze#)
			v3=AddVertex(volsurf,v3x#,v3y#,v3z#)
			v3e=AddVertex(volsurf,v3xe#,v3ye#,v3ze#)			

			AddTriangle (volsurf, v1,v1e,v2)
			AddTriangle (volsurf, v2,v1e,v2e)
			AddTriangle (volsurf, v1,v3,v1e)
			AddTriangle (volsurf, v3,v3e,v1e)
			AddTriangle (volsurf, v3,v2,v3e)
			AddTriangle (volsurf, v2,v2e,v3e)
			AddTriangle (volsurf, v1,v2,v3) 	;cap top
			AddTriangle (volsurf, v1e,v2e,v3e) 	;cap bottom			
			
			tricount=tricount+1
			Wend 
			;Next		
		surfcount=surfcount+1
		Wend
		;Next

FreeEntity vpos
FreeEntity vposend

End Function



jfk EO-11110(Posted 2006) [#20]
yeah, that's about the same as when you use the first return in my function: simply create a volume for every triangle. The fine art is then to remove the enclosed tris. Even more efective would be to create only the needed Tris/quads, of course.

I have found elias_t's ray intersect triangle code in the archives and I assume it would work much faster than linepick, but I still try to find an idea on how to use it.

where Linepick will return the first pickable triangle that was "hit", the function by elias will only tell if the ray was intersecting a certain triangle. So theoreticly I'd have to check all triangles for every ray and return the one of those that are intersecting wich has the lowest distance from the intersection point to the pivot.

I guess that's also how Linepick works internally (thus the exponential slowdown with highpoly stuff and Linepick)

At least this Linepick replacement allows a simpler structure in general:

now we only have to store the vertex and triangle data in arrays and will then be able to generate a shadow volume using only the required sihouette triangles.

The source mesh'es triangle data must also be accessible in the arrays to allow to obscure things, like in the version using Linepick.

It's also useful that the ray intersection code doesn't care about normals, so it will work form both sides and you don't have to worry about flipped triangles.

The only question left is: will the 32 Bit accuracy be high enough to prevent the rounding errors seen with Linepick? (if it really WAS linepick that caused it).
And maybe: will it be fast enough?

I'll try to implement it this way the sooner or later.


jfk EO-11110(Posted 2006) [#21]
Ok, I got it partially working with a Linepick replacement. It seems the rounding errors are occuring here too :(
Additionally it ssem I got troubles with the backface culling flag of the rit function (ray intersect triangle).

This version is faster than the Linepick version, even with this whole brute force triangle intersection scanning:
with Linepick it took 235 millisecs, with "rit" only 131ms.

Nevertheless, one thing is clear: the entire linepick (and rit) philosophy is way to slow for practical use. Using this for a scene of several thousand tris may take several seconds to complete.

So I'd suggest we stop this and try to do it the correct, official way, checking the neightbors normals etc. as described in some of the pages I linked to.


bytecode77(Posted 2006) [#22]
i think the error is in the routine which creates the volume, and not in the routine which cuts the unuseful polys, beacause if you return the volume after step1, you'll get the same error...


Roland(Posted 2006) [#23]
Hey guys, good work here! I'd love to see this come together. On the subject of linepicks and such, I recall that there's a wrapper for the Coldet collision engine lying around somewhere which has a pretty sweet set of ray / intersection tests. If I remember correctly it's much much faster than B3d. Might be worth checking out!

Let me know if you need me to post links -- I don't have them at the moment, but I can dig them up.

cheers,
roland


jfk EO-11110(Posted 2006) [#24]
Thanks Roland. Is coldet for free? Thought it's not. well if
it is, this would be great!

BTW Xware, not sure how you meant the error is in the basic mesh? Especially, how could you see that? Anyway, practically you're right, I have found the bug, it was a wrong vertex assignenment:

there's this block where 6 potential silhouette tris are added:
   AddTriangle su2, v0  ,v1  ,v1_b
   AddTriangle su2, v0_b,v0  ,v1

   AddTriangle su2, v1_b,v1,v2_b
   AddTriangle su2, v1,v2,v2_b

   AddTriangle su2, v2,v0_b,v2_b
   AddTriangle su2, v2,v0,v0_b

The second line of it should be:
   AddTriangle su2, v0_b,v0  ,v1_1b


Additionally use
Global infi#=50 ; length shadow volume
Global pick_radius#=0.0001 
Global pick_back#=50 ; pick from n behind light
Global divi#=3.0


This give s a much better result, although it still isn't perfect. In fact the rounding errors of the Z depth, caused by the 3D hardware can never be fully fixed.

Using a Linepick replacement that is using 64bit or 96bit floating point internally might solve the problem.

Plus, the speed of it is still very slow. Tho I am pleased it really works at all.
EDIT after fiddling with the infini, pick radius and pick_back, I got the sphere working correctly. Tho this is all pretty fragile.

Coldet may be a solution, for both floating point resolution and linepick speedup.

But I still think there's this unneccessary creation of enclosed tris, just to remove them after the visibility check. Although still pretty fast (byside linepick), it's not optimal.

BTW currently I am working on a third cheating version, this one is trying to utilize the ICU color detection trick. I still got some problems with rounding errors, like with the two prev. versions, but the speed of this one is much faster, 230 ms was reduced to about 5 ms. It requires an additional renderworld for every light source.

Every triangle of the shadow volume 1 (containing all potential tris) will use its unique vertex color that is representing its triangle index.
An orto camera is positioned at the helper pivots position (one step behind the light) and takes a render.
Readpixelfast is now used to determine the visibility of the triangles.

It's much faster to read eg. 64*64 pixels than checking the ray intersection of every triangle against every other triangle in the volume. This also causes no exponential slowdown compared to Linepick.

Tho, I still have to fix the rounding error roblem here, this one seems to be even trickier than in the prev. two versions (pixel precision only).


bytecode77(Posted 2006) [#25]
yes, thats a good idea to use coldet...thx roland :)
perhabs there are other libs for cheating to volume by :)
...


bytecode77(Posted 2006) [#26]

yeah, that's about the same as when you use the first return in my function:



ok, but if i return the volume after creating it, without deleting the tris, the error appears, too.
and there is a "convex hull" dll in the dll archives, can u may use this^^?


jfk EO-11110(Posted 2006) [#27]
I think the convex hull is used for physics libraries, someone correct me if I'm wrong. I think we don't need a convex hull, but in fact a shadow volume.

Yes coldet is for free and thanks to elias for the wrapper. I have modified it so it is using coldets Ray-Collision function instead of Linepick. The problem is: you have to define what mesh should be checked, so you need to do those ray-collision tests for every mesh in the scene, eg: to create one volume you got to do it with the volume as well as with the obscurer (source mesh).

I still can't make coldet detect the right triangles, but I already see the speed of coldet. Where the Linepick version took 230ms coldet requires only about 50 ms. Tho, complicate things pretty much (coldet volumes must be built for every shadow volume etc.)

So I really still hope I can make the ICU version work correctly ince it's the fastest yet: 5ms compared to 230.

That said, a real solution if still welcome :)


bytecode77(Posted 2006) [#28]
cool, thats great :)


jfk EO-11110(Posted 2006) [#29]
ok here's somethin gto play with, until we get a real solution by somebody who's actually knowing what he's doing, so not by me o_O

This is the fixed version of the Linepicking Volume creator. Wit this settings it's working pretty nicely, say about 99.5% correctly, although of course still slow with high poly stuff, but you may try it with some low poly geometry and see. Maybe somebody can add a faster Linepick:


additionally here's the same function, now demonstrating how it could be used in plain Blitz to create Stencil Shadows:





jfk EO-11110(Posted 2006) [#30]
Ok finally here's a combination of the ICU method and the linepick method.

First it uses the ICU detection method that is pretty fast. But then there are still some enclosed Triangles left that should not be there. So I additionally use the Linepick method to get rid of them. It's a bit less accurate than the Linepick only version. However, the speed is around 9ms, compared to the linepickversion (230ms), the coldet version (50ms) and the plain ICU method(ca. 6ms, tho not accurate enough).

I have also seen as soon as I use smaller triangles (like a sphere with more segments), the rouding errors show up more oftenly. So I'd say: this may be useful for simple, rough geometry only.

Additionally there's the bitter fact that animated meshes cannot make use of this because VertexX etc. will ignore Animations and return the initial pose.




Well right now this is the best I can do. Maybe I'll try it again from scratch one day, with a completely diffrent method, preferably the official math way and not cheats like these.


Steven Noyce(Posted 2006) [#31]
Good code! Thanks a lot!
Sad to hear that you are not developing it further, and wish I could help, but I have NO idea of where to begin with this sort of thing. Good work jfk! Thanks again!


jfk EO-11110(Posted 2006) [#32]
thank you. the lack of accuracy makes the whole approach pretty useless, so it was kind of a waste of time.

Cause, when you try to use this with a loaded mesh, it simply doesn't work for some reason.

I think a proper solution is needed. Unfortunately those who did it (I remember that Stencil Shadow demo with the beethoven mesh, using the dx7test.dll, creating realtime shadow volumes too) don't seem to be willing to release the sources, or maybe they're not around. Or in the words of a guy from the past: the scientists have locked up the scripts and hidden the key.

Personally I am not very clever when it comes to 3D maths. Well maybe I'll find a simple C source that can be translated to Blitz.


Stevie G(Posted 2006) [#33]
Nice work JFK .. shame ( like you say ) it's too slow for in game.

Stevie


I think a proper solution is needed. Unfortunately those who did it (I remember that Stencil Shadow demo with the beethoven mesh, using the dx7test.dll, creating realtime shadow volumes too) don't seem to be willing to release the sources, or maybe they're not around. Or in the words of a guy from the past: the scientists have locked up the scripts and hidden the key.



That'd be Fredborg, still have it on my desktop :)


bytecode77(Posted 2006) [#34]
thank u very much :) :) :)

please give me your email adress, therefore i can give you the ready system in a few weeks...
you'll get it!


bytecode77(Posted 2006) [#35]
if u have managed to add the coldet version, can you please give it to me?


edit: i just seen, that if i use a model of a gun or a mesh, the volume looks corrupt...?


jfk EO-11110(Posted 2006) [#36]
yes, I've seen that too. at least in my shadow demo concave shapes won't work, only convex ones. The reason why is concave shapes may force the renderers ray to go trough the hull surface not 1 or to time sonly, but 3 or 4 times.

Where in true stencil shadows the counter would add one when the ray hits the outside surface of the volume and subtract 1 when it hits the inside surface (resulting in 0 or 1 for shadow or not shadow), this blitz based demo uses ALpha to produce 2 maskables shades of grey.

I have also started the implementation of volume creation using a method desribed in the papers. This method will create a list conaining the 3 neighbour triangles of every triangle. It will then determine the backface culling state of every triangle, from the viewpoint of the light. Now every nonculled triangle that has at least 1 neighbour that is culled is a potential edge triangle. Finally I have to determine if the edge triangles are obscured by other triangles. If they are not, then they are the wanted edge triangles.

The problem is: I again have to check the visibility of every triangle from the viewpoint of the light. That's the slow part again, No matter if you use Linepick, Coldet picks or even the fast ICU method.

For the ICU method a copy of the mesh must be created on the fly, with unwelded vertices, the triangles are then colored individually, just to render it, then use readpixelfast to determine what colors were rendered (=visible triangles) and finally delete the ICU mesh copy again. Basicly ICU may be faster than all math picking stuff, but it also complicates things massively and makes it unflexible.

I still hope Fredborg will release the source for the volume creation of the Beethoven Stencil shadows demo.

I stopped this since this is really trying to reinvent the wheel.


bytecode77(Posted 2006) [#37]
wait! i have got an idea, without any linepick functions:

you use TFormNormal and check for every triangle out whether it is culled or not, and if two triangles are conected, and the one is culled, but the other not, THIS IS AN EDGE!!! then, you can create a quad on this edge...
this would be fast, but i dont know how to make it real...
may we can make a shadow system by helping each other... :)

ps: i'm actually in holidays, so i will answer a little bit later...

cu :)


Braincell(Posted 2006) [#38]
I will post the alpha of the blitz stencil shadow system tomorrow. It has code for building and updating volumes, highly optimised. Making volumes is 90% of the work in a stencil shadow system and i have been improving that part all of the time.

PS i didnt read nearly any posts in this thread.


OJay(Posted 2006) [#39]
thanks len, cant wait :)

btw: i mailed fredborg, but got no reply yet...we'll see...


bytecode77(Posted 2006) [#40]
i love u braincell!!!
thx :)


Steven Noyce(Posted 2006) [#41]
Where will the link to braincell's system be?
In his worklog?
In this thread?


Braincell(Posted 2006) [#42]
here you go guys

http://www.blitzbasic.com/Community/posts.php?topic=58882


jfk EO-11110(Posted 2006) [#43]
thanks a lot! great work!