We all hate it when you need to do complicate things to get some shadows, right? Wouldn't it be much easier to turn on shadows like eg. a switch?. Well raytracing IS that simple. Unfortunately it's dead slow.
Here's a description of how it is done sometimes, eg. in Blitz3D:
Do a camerapick on every pixel. If there is a mesh under the pixel, then position a dummy pivot at pickedx, pickedy, pickedz and use EntityVisible to check if the pivot can see the light entity. If the light can be seen, this pixel will not be in shadow, otherwise it will be shaded.
Pretty simple, but it may take a minute or so to compute.
The main bottlenecks in this technic are:
-CameraPick
-EntityVisible
Now, after some heavy brainstorming I had 2 revolutionary, weird and crazy ideas to speed things up:
I replaced Camerapick by something I call "Distance by Fog".
Every mesh in the scene is fullbright and white (may be a temporary state). I added some black fog to the main camera.
Based on some formulas and tricks I was then capable to read a pixels RGB and calculate the 3D position of the underlying mesh from it. It really works with an accuracy of about one pixel.
But EntityVisible, that had to be performed for every picked pixel was still endless slow.
This was exactly where my good ol ICU trick came in handy: I created a mesh containing (in theory) one triangle for every pixel (practically I reduced it to 160*120 Tris for speed reasons). Every Triangle gets its individual Vertex Color (ICU=Identification by Color Uniqueness).
Since I already know the 3D coords of all pixels, given by my homebrew quick camerapick, I only got to position the corresponding Tris at those coords. Now I position a second camera (no fog) at the lights coordinate and take a 6-sided (cubemap) render. Using Readpixelfast it allows me now to determine the visibility of each Triangle, from the viewpoint of the light source. Hence I can check this way if a given Pixel is lit or not.
It really works. There are still many problems to solve:
rounding errors due to the limited range of fog levels. This results in tiny inaccuracies in the determination of the "picked" coordinates. No biggie, unfortunately the tris have to be very small to make sure they won't cover one another and therefor some of them end up under the scenes mesh surfaces and get kind of missed in the ICU check. This is the main problem right tnow.
However, I'm real happy that it works at all. These two tricks are real weird IMHO. Maybe somebody can get more out of it.
One warning: don't expect too much: this ist still very bugous and far away from being useful. It's an experiment, still not finished, maybe some people want to join as long as there is something to explore.
Here's the source:
Here's a description of how it is done sometimes, eg. in Blitz3D:
Do a camerapick on every pixel. If there is a mesh under the pixel, then position a dummy pivot at pickedx, pickedy, pickedz and use EntityVisible to check if the pivot can see the light entity. If the light can be seen, this pixel will not be in shadow, otherwise it will be shaded.
Pretty simple, but it may take a minute or so to compute.
The main bottlenecks in this technic are:
-CameraPick
-EntityVisible
Now, after some heavy brainstorming I had 2 revolutionary, weird and crazy ideas to speed things up:
I replaced Camerapick by something I call "Distance by Fog".
Every mesh in the scene is fullbright and white (may be a temporary state). I added some black fog to the main camera.
Based on some formulas and tricks I was then capable to read a pixels RGB and calculate the 3D position of the underlying mesh from it. It really works with an accuracy of about one pixel.
But EntityVisible, that had to be performed for every picked pixel was still endless slow.
This was exactly where my good ol ICU trick came in handy: I created a mesh containing (in theory) one triangle for every pixel (practically I reduced it to 160*120 Tris for speed reasons). Every Triangle gets its individual Vertex Color (ICU=Identification by Color Uniqueness).
Since I already know the 3D coords of all pixels, given by my homebrew quick camerapick, I only got to position the corresponding Tris at those coords. Now I position a second camera (no fog) at the lights coordinate and take a 6-sided (cubemap) render. Using Readpixelfast it allows me now to determine the visibility of each Triangle, from the viewpoint of the light source. Hence I can check this way if a given Pixel is lit or not.
It really works. There are still many problems to solve:
rounding errors due to the limited range of fog levels. This results in tiny inaccuracies in the determination of the "picked" coordinates. No biggie, unfortunately the tris have to be very small to make sure they won't cover one another and therefor some of them end up under the scenes mesh surfaces and get kind of missed in the ICU check. This is the main problem right tnow.
However, I'm real happy that it works at all. These two tricks are real weird IMHO. Maybe somebody can get more out of it.
One warning: don't expect too much: this ist still very bugous and far away from being useful. It's an experiment, still not finished, maybe some people want to join as long as there is something to explore.
Here's the source:
; Realtime Shadow Raytracing Approach by Dieter Marfurt ; This Demo is using the ICU Method (Identification by Color Uniqueness) to ; determine what pixel blocks are lit by the light source. ; it also replaces slow Camerapicks by a weird Technic named "Distance by fog". ; This demo is far from being ready to be used in a game etc. Noless it shows that ; there may be a way to do fast realtime raytraced shadows very similar to pixel shaders ; in software. ; Maybe someone's gonna pick it up and make something useful out of it. Graphics3D 640,480,32,2 ;Graphics3D 320,240,32,2 SetBuffer BackBuffer() Global screenwidth=GraphicsWidth() Global screenheight=GraphicsHeight() Global screen_bk=CreateImage(screenwidth,screenheight) ; By now the amount of shading elements is reduced to 160*120, mainly for speed reasons ; (When this is increased then the ICU encoding should be edited as well because now it's ; using the lower 2 bytes of the RGB colors to encode X and Y) Global tow_w=159,tow_h=119 Global tow_whalf#=tow_w/2.0,tow_hhalf#=tow_h/2.0 Global tow_wrel#=GraphicsWidth()/(tow_w+1),tow_hrel=GraphicsHeight()/(tow_h+1) Dim buf(tow_w,tow_h) Dim grey(tow_w,tow_h) ; main cam, also used for distane by fog check Global cam=CreateCamera() TranslateEntity cam,0,0,-8 CameraFogMode cam,1 CameraFogColor cam,0,0,0 CameraFogRange cam,0,25.5 CameraProjMode cam,1 ; cubemap cam for ICU check Global raycam=CreateCamera() CameraViewport raycam,0,0,tow_w,tow_h CameraRange raycam,0.001,256 CameraProjMode raycam,0 ; some meshes middle=CreatePivot() num=2 Dim obj(num) For i=0 To num obj(i)=CreateCylinder() ScaleEntity obj(i),Rnd(.2,.5),Rnd(1,2),Rnd(.2,.5) EntityPickMode obj(i),2 PositionEntity obj(i),Rand(-3,3),Rnd(1,2),Rand(-3,3) RotateEntity obj(i),Rand(360),Rand(360),Rand(360) EntityParent obj(i),middle EntityFX obj(i),1 Or 16 Next ground=CreateCube() ScaleEntity ground,4,0.2,4 TranslateEntity ground,0,-2,0 EntityPickMode ground,2 EntityFX ground,1 Global dummy=CreatePivot() ; helper pivot Global light=CreateLight(2) PositionEntity light,0,30,0 ; dynamic light ; Single Surface Mesh using VertexColor for ICU method Global tow_mesh=CreateMesh() Global tow_surf=CreateSurface(tow_mesh) Global di#=0.1 ; this will be the size of the ICU tris! should not be too small or too big. For j=0 To tow_h For i=0 To tow_w v0=AddVertex(tow_surf,di*i ,di*j ,0) v1=AddVertex(tow_surf,di*i+di ,di*j ,0) v2=AddVertex(tow_surf,di*i ,di*j+di ,0) VertexColor tow_surf,v0,0,j+10,i+10 ; set ICU colors (max 245*245 by now) VertexColor tow_surf,v1,0,j+10,i+10 VertexColor tow_surf,v2,0,j+10,i+10 tri=AddTriangle(tow_surf,v0,v1,v2) Next Next EntityFX tow_mesh,1 Or 2 Or 16 HideEntity tow_mesh Color 0,0,0 While KeyDown(1)=0 t1=MilliSecs() For i=0 To num TurnEntity obj(i),1,2,3 Next TurnEntity middle,0,-1,0 TurnEntity ground,0,1,0 RenderWorld() do_shadows() t2=MilliSecs() Color 0,255,0 Text 0,0,t2-t1 Flip Wend End Function do_shadows() ; save the rendered screen because we'll perform cubemap rendering on the backbuffer CopyRect 0,0, screenwidth, screenheight,0,0,BackBuffer(),ImageBuffer(screen_bk) Dim buf(tow_w,tow_h) ; erase sunlight array LockBuffer BackBuffer() For j=0 To tow_h For i=0 To tow_w rgb=ReadPixelFast(i*tow_wrel,j*tow_hrel) And $ffffff ; determine distance to object under pixels (by fog brightness) grey(i,j)=((rgb Shr 16) +((rgb Shr 8) And $ff)+(rgb And $ff))/3 Next Next UnlockBuffer BackBuffer() For j=0 To tow_h For i=0 To tow_w If (grey(i,j)>0) ; camerapick replacement (lots faster): dis#=(255.0-grey(i,j))/10.0 ; distance calced by fog amount PositionEntity dummy,EntityX(cam),EntityY(cam),EntityZ(cam),1 RotateEntity dummy,EntityPitch(cam),EntityYaw(cam),EntityRoll(cam),1 xoff#=((i-tow_whalf)*dis/tow_whalf) yoff#=-((j-tow_hhalf)*dis/tow_whalf) MoveEntity dummy,xoff,yoff,0 MoveEntity dummy,0,0,dis ; dummy is now positioned at the "camerapicks" coordinate ; so we put this screencoords ICU triangle there VertexCoords tow_surf,TriangleVertex(tow_surf,((j*(tow_w+1))+i),0),EntityX(dummy)-di, EntityY(dummy)-di, EntityZ(dummy)-di VertexCoords tow_surf,TriangleVertex(tow_surf,((j*(tow_w+1))+i),1),EntityX(dummy)+di, EntityY(dummy)+di, EntityZ(dummy)+di VertexCoords tow_surf,TriangleVertex(tow_surf,((j*(tow_w+1))+i),2),EntityX(dummy)+di, EntityY(dummy)-di, EntityZ(dummy)-di Else ; assuming not picked any mesh: move tri out of view. VertexCoords tow_surf,TriangleVertex(tow_surf,((j*(tow_w+1))+i),0),16000+EntityX(dummy), 16000+EntityY(dummy), EntityZ(dummy) VertexCoords tow_surf,TriangleVertex(tow_surf,((j*(tow_w+1))+i),1),16000+EntityX(dummy)+di, 16000+EntityY(dummy), EntityZ(dummy) VertexCoords tow_surf,TriangleVertex(tow_surf,((j*(tow_w+1))+i),2),16000+EntityX(dummy), 16000+EntityY(dummy)+di, EntityZ(dummy) buf(i,j)=1 EndIf Next If KeyDown(1)=1 Then End Next ; now we position a non fog camera at the lights coordinate and take 6 renders (cubemap) ; whenever a ICU color will appear on the renders, we'll know that "pixel" is lit, where the Color holds the pixel coordinate. CameraProjMode cam,0 CameraProjMode raycam,1 PositionEntity raycam,EntityX(light),EntityY(light),EntityZ(light),1 ShowEntity tow_mesh w=256 ; size of cubemap, the smaller, the less accurate h=256 ; check every pixels 3d content: can it be seen by the light? ; (do this on 6 sides) check_icu(0,0,0,w,h) check_icu(90,0,0,w,h) ;Flip ; used for visual debugging check_icu(180,0,0,w,h) check_icu(270,0,0,w,h) check_icu(0,90,0,w,h) check_icu(0,-90,0,w,h) HideEntity tow_mesh ; restore main render CopyRect 0,0, screenwidth, screenheight,0,0,ImageBuffer(screen_bk),BackBuffer() darken_shadows2() ;darken shady pixels (blocks by now) CameraProjMode cam,1 CameraProjMode raycam,0 End Function Function check_icu(pitch#,yaw#,roll#,w,h) CameraViewport raycam,0,0,w,h RotateEntity raycam,pitch,yaw,roll WireFrame 1 ; not sure if this is a good idea... RenderWorld() WireFrame 0 LockBuffer BackBuffer() For j=0 To h-1 For i=0 To w-1 rgb=ReadPixelFast(i,j) And $ffff j_=((rgb Shr 8) And $FF)-10 ; decode possible ICU color / coodinate i_=(rgb And $FF)-10 If j_>=0 And j_<=tow_h If i_>=0 And i_<=tow_w buf(i_,j_)=1 ; ICU detected, sun is shining here! EndIf EndIf Next Next UnlockBuffer BackBuffer() End Function Function darken_shadows2() Color 0,0,0 For j=0 To tow_h For i=0 To tow_w If buf(i,j)=0 ; no sunshine here? Rect i*tow_wrel,j*tow_hrel,tow_wrel,tow_hrel,1 ; paint shadow (well a simple placebo thing) EndIf Next Next End Function

