create a shadow volume

Blitz3D Forums/Blitz3D Programming/create a shadow volume

The forum is locked. These are the original blitzbasic.com forums, kept so they can still be read. No new topics, replies or edits can be made here. To join in, head over to the Blitz3D+ forums.

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.


;create shadow volume with a combination of Linepick and the ICU method:
;-----------------------------------------------------------------------

; works kind of, tho meshes with lots of little triangles will produce more rounding errors



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



Global helper=CreatePivot()
Global helper2=CreatePivot()
Global icucam=CreateCamera()
CameraProjMode icucam,0
Dim icu_seen(0)

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


lidis#=4

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

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

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

cube=CreateSphere(5) ;CreateCube() ; object to cast shadow
;cube=CreateCube() ; object to cast shadow
;cube=CreateCone(10)
RotateEntity cube,45,45,0
;RotateEntity cube,245,245,0
EntityPickMode cube,2
;TranslateEntity cube,1.2,0.5,1.3

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

a#=90
While KeyDown(1)=0
 a=(a +2) Mod 360
 PositionEntity lime,Sin(a)*lidis,lidis,Cos(a)*lidis
; PositionEntity light,Sin(a)*lidis,lidis,Cos(a)*lidis
; TurnEntity cube,1,2,3
 If vol<>0 Then FreeEntity vol
 t1=MilliSecs()
 vol=make_volume(cube,lime)
 t2=MilliSecs()
 RenderWorld()
 Text 0,0,"tris "+TrisRendered()
 Text 0,16,"ms "+(t2-t1)
 Text 0,32,"space=pause"
 Flip
 While KeyDown(57) 
  Delay 1
 Wend 
Wend
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

 TFormPoint EntityX(lite),EntityY(lite),EntityZ(lite),0,source
 PositionEntity helper2,TFormedX(),TFormedY(),TFormedZ()
 litemesh=lite
 lite=helper2

 vol=CreateMesh()
 su2=CreateSurface(vol)
 For s=1 To CountSurfaces(source)
  su=GetSurface(source,s)
  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))
   
   ; create projected vertex coords
   PositionEntity helper,vx0,vy0,vz0
   PointEntity helper,lite
   MoveEntity helper,0,0,-infi
   vx0_b#=EntityX(helper)
   vy0_b#=EntityY(helper)
   vz0_b#=EntityZ(helper)


   PositionEntity helper,vx1,vy1,vz1
   PointEntity helper,lite
   MoveEntity helper,0,0,-infi
   vx1_b#=EntityX(helper)
   vy1_b#=EntityY(helper)
   vz1_b#=EntityZ(helper)

   PositionEntity helper,vx2,vy2,vz2
   PointEntity helper,lite
   MoveEntity helper,0,0,-infi
   vx2_b#=EntityX(helper)
   vy2_b#=EntityY(helper)
   vz2_b#=EntityZ(helper)

 ; add all potential shadow volume tris and set their ID color
;-   AddTriangle su2, v0  ,v1  ,v1_b
   vv0=AddVertex(su2,vx0,vy0,vz0)
   vv1=AddVertex(su2,vx1,vy1,vz1)
   vv2=AddVertex(su2,vx1_b,vy1_b,vz1_b)
   icu_code=(tri_count+10)+0
   icu_red=(icu_code And $FF0000) Shr 16
   icu_green=(icu_code And $FF00) Shr 8
   icu_blue=(icu_code And $FF)
   VertexColor su2,vv0,icu_red,icu_green,icu_blue
   VertexColor su2,vv1,icu_red,icu_green,icu_blue
   VertexColor su2,vv2,icu_red,icu_green,icu_blue
   AddTriangle su2, vv0 ,vv1 ,vv2

;-   AddTriangle su2, v0_b,v0  ,v1_b
   vv0=AddVertex(su2,vx0_b,vy0_b,vz0_b)
   vv1=AddVertex(su2,vx0,vy0,vz0)
   vv2=AddVertex(su2,vx1_b,vy1_b,vz1_b)
   icu_code=(tri_count+10)+1
   icu_red=(icu_code And $FF0000) Shr 16
   icu_green=(icu_code And $FF00) Shr 8
   icu_blue=(icu_code And $FF)
   VertexColor su2,vv0,icu_red,icu_green,icu_blue
   VertexColor su2,vv1,icu_red,icu_green,icu_blue
   VertexColor su2,vv2,icu_red,icu_green,icu_blue
   AddTriangle su2, vv0 ,vv1 ,vv2

;-   AddTriangle su2, v1_b,v1,v2_b
   vv0=AddVertex(su2,vx1_b,vy1_b,vz1_b)
   vv1=AddVertex(su2,vx1,vy1,vz1)
   vv2=AddVertex(su2,vx2_b,vy2_b,vz2_b)
   icu_code=(tri_count+10)+2
   icu_red=(icu_code And $FF0000) Shr 16
   icu_green=(icu_code And $FF00) Shr 8
   icu_blue=(icu_code And $FF)
   VertexColor su2,vv0,icu_red,icu_green,icu_blue
   VertexColor su2,vv1,icu_red,icu_green,icu_blue
   VertexColor su2,vv2,icu_red,icu_green,icu_blue
   AddTriangle su2, vv0 ,vv1 ,vv2


;-   AddTriangle su2, v1,v2,v2_b
   vv0=AddVertex(su2,vx1,vy1,vz1)
   vv1=AddVertex(su2,vx2,vy2,vz2)
   vv2=AddVertex(su2,vx2_b,vy2_b,vz2_b)
   icu_code=(tri_count+10)+3
   icu_red=(icu_code And $FF0000) Shr 16
   icu_green=(icu_code And $FF00) Shr 8
   icu_blue=(icu_code And $FF)
   VertexColor su2,vv0,icu_red,icu_green,icu_blue
   VertexColor su2,vv1,icu_red,icu_green,icu_blue
   VertexColor su2,vv2,icu_red,icu_green,icu_blue
   AddTriangle su2, vv0 ,vv1 ,vv2


;-   AddTriangle su2, v2,v0_b,v2_b
   vv0=AddVertex(su2,vx2,vy2,vz2)
   vv1=AddVertex(su2,vx0_b,vy0_b,vz0_b)
   vv2=AddVertex(su2,vx2_b,vy2_b,vz2_b)
   icu_code=(tri_count+10)+4
   icu_red=(icu_code And $FF0000) Shr 16
   icu_green=(icu_code And $FF00) Shr 8
   icu_blue=(icu_code And $FF)
   VertexColor su2,vv0,icu_red,icu_green,icu_blue
   VertexColor su2,vv1,icu_red,icu_green,icu_blue
   VertexColor su2,vv2,icu_red,icu_green,icu_blue
   AddTriangle su2, vv0 ,vv1 ,vv2


;-   AddTriangle su2, v2,v0,v0_b
   vv0=AddVertex(su2,vx2,vy2,vz2)
   vv1=AddVertex(su2,vx0,vy0,vz0)
   vv2=AddVertex(su2,vx0_b,vy0_b,vz0_b)
   icu_code=(tri_count+10)+5
   icu_red=(icu_code And $FF0000) Shr 16
   icu_green=(icu_code And $FF00) Shr 8
   icu_blue=(icu_code And $FF)
   VertexColor su2,vv0,icu_red,icu_green,icu_blue
   VertexColor su2,vv1,icu_red,icu_green,icu_blue
   VertexColor su2,vv2,icu_red,icu_green,icu_blue
   AddTriangle su2, vv0 ,vv1 ,vv2


   tri_count=tri_count+6

  Next
 Next
 EntityPickMode vol,2
 EntityFX vol,1 Or 2
 EntityColor vol,0,0,255
 RotateEntity vol, EntityPitch(source),EntityYaw(source),EntityRoll(source),1
 PositionEntity vol,EntityX(source),EntityY(source),EntityZ(source)

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

; -------------------------------

; 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

 PositionEntity icucam ,EntityX(litemesh),EntityY(litemesh),EntityZ(litemesh),1
 PointEntity icucam,source
 MoveEntity icucam,0,0,-pick_back

 Dim icu_seen(tri_count)
 ; prepare scene for ICU scan
 sz=64;127
 ; work around rounding Z errors...
 remx#=EntityX(source)
 remy#=EntityY(source)
 remz#=EntityZ(source)
 rempitch#=EntityPitch(source)
 remyaw#=EntityYaw(source)
 remroll#=EntityRoll(source)
 PointEntity source,litemesh
 MoveEntity source,0,0,.1
 RotateEntity source, rempitch,remyaw,remroll
; eo work around
 EntityColor plane,0,0,0
 EntityColor source,0,0,0
 HideEntity litemesh
 CameraViewport icucam,0,0,sz+1,sz+1

;CameraProjMode icucam,1;2 ; better? guess not
;CameraRange icucam,10,5000
;CameraZoom icucam,10

 CameraProjMode icucam,2
 CameraRange icucam,0.0002,100
 CameraZoom icucam,.0001

 CameraProjMode camera,0

 ; create ICU render and scan it for valid ID colors
 RenderWorld()
 SetBuffer BackBuffer()
 LockBuffer()
 For j=0 To sz
  For i=0 To sz
   rgb=ReadPixelFast(i,j) And $FFFFFF
   If( rgb > 0) And (rgb<=tri_count+10)
    icu_index=rgb-10
    icu_seen(icu_index)=icu_seen(icu_index)+1
   EndIf
  Next
 Next
 UnlockBuffer()


 ;PositionEntity source, remx,remy,remz
 EntityColor plane,0,255,0
 EntityColor source,255,255,255
 ShowEntity litemesh
 CameraProjMode icucam,0
 CameraProjMode camera,1

 ;Flip 0 ; use this for visual control of the ICU render
 ;WaitKey()


 ; create a new volume mesh containing the triangles with detected ICU ID colors (visible tris)

 For i=0 To tri_count-1

  If icu_seen(i)>0
   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))

   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
 Next

 ; Goto skip_combi ; use this to see the ICU product only!

 FreeEntity vol ; release old volume
 vol=vol3

 UpdateNormals vol
 EntityPickMode vol,2
 EntityFX vol,1 Or 2
 EntityColor vol,0,0,255
 RotateEntity vol, EntityPitch(source),EntityYaw(source),EntityRoll(source),1
 PositionEntity vol,EntityX(source),EntityY(source),EntityZ(source)


 ; now we create a new mesh and use the linepick method to scan trough the
 ; imperfect shadow volume mesh we just created using the ICU method.
 ; this Linepick method is more accurate, but much slower. Thanks to the
 ; ICU method we only have to scan a fraction of the first volume mesh.

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


 vol3=CreateMesh()
 su3=CreateSurface(vol3)
 su2=GetSurface(vol,1)
 tri_count=CountTriangles(su2)
 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

 .skip_combi

 ; (undo cosmetics)
 PositionEntity source, remx,remy,remz


 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

 ;so we first created a mesh containig every potential triangle required for the shadow volume mesh.
 ;then we used the ICU (Identification by Color Uniqueness) Method to make a rough copy of the 
 ;silhouette triangles (= removing enclosed Triangles). This is not very accurate: there are
 ;still some triangles copied that should not be there.
 ;so finally we scan this rough silhouette mesh and create a third mesh, containing only the 
 ;picked Silhouette Triangles.

End Function


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.