Realtime raytraced shadows: mission impossible?
Community Forums/Showcase/Realtime raytraced shadows: mission impossible?
| ||
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: ; 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 |
| ||
Isnt this similar/how shadow(cubemaps) are done,cept they use a depth buffer...but Didnt you just emulate a depth buffer with the fog trick? ;) It does work and it is realtime..2fps here. Sam |
| ||
yes, the depth buffer is emulated, but the visibility check of the lit zones is diffrent I guess. Not sure what you mean by shadow cubemaps. The renders I call cubemap here are not used as textures. This method is completely dynamic (lights and meshes), and selfshadowing. THIS was the idea. Switching on the light. Of course, it's not that fast, I get 5 fps here with my 50$ radeon 9200, but when you compare it with CameraPick and EntityVisible, then the speed gain is amazing. I just remember when I posted the ICU trick the first time, for a VIS Occlusion thing, Terabit came along and made it run 16 times faster by some quick optimations. So you never know what's next. Tho I agree, using shaders is probably a better way. But it's the challenge to do the impossible. Well I tried do that since a long time, but I never figured out how to calculate the 3D position. Now I simply use Blitz's MoveEntity Commands to do it. |
| ||
I just remembered it from a tutorial on how to do realtime shadow maps on a geforce256 . Ill see if I still have a link. Yeah, Compared to Camerapick and entityvisiable its many many times faster. Sam |
| ||
Can you post a screeny of what the demo is supposed to look like? It looks a real mess on my machine. |
| ||
yes, it looks like a real mess, of course. That's what I said in the text. I'd like to have every pixel traced, but right now I had to reduce it to 160*120 units. While the idea was to darken those units onscreen, for this test I simply draw a small black box instead. This demo was never thought to look cool. It's only here to proof there's a way to camerapick without to camerapick and to use a renderworld to recognize millions of objects in one step. some problms still exist as I said. you have to read the text,then you know what it's about and why it still looks like a mess. Nevertheless the critical steps are performed in a fraction of the time needed by a "common blitz3d raytacer". although this experment still looks like the output of some defect hardware, it has the potential of mostly automatied dynamic shadows that are raytraced. No stecil volume troubles, nothing. But of course, the described problems need to be solved. However, you should roughly see the shadows of 3 moving cylinders on a rotating platform, including all kinds of artefacts caused by rounding errors etc.. Ok, here's some pics This one shows the result of the above source. Of course, it looks horrible. Mainly because I made a quick n dirty test and due to the 64k vertices limit I used a 160*120 Tris ICU mesh. ![]() Now I have edited the source a bit and I tried to solve some problems. I have implemented 4 ICU meshes, so the new ICU resolution is 320*240. As you see the speed is not 4 times slower, btw. One important problem is also clear now: The triangles that are used to detemine the visibility of certain areas should be aligned to the surface, but this cannot be done, While camerapick would deliver the normals, this method doesn't know about normals. It's just the way it is and I'll try to find an other solution. Fact is, the tris should face to the (light-)camera, the more they face to the camera, the better are the results. a lot of tris are still missed for several reasons, that's why there are black parts where should be only light. However, these are practical problems, the theory works pretty well. So this is the current state: ![]() Note this reflects only the shadowing parts. In a real implementation this would be drawn to a texture that semitransparently covers the screen, preferably in a 1:1 pixel resolution. |
| ||
@Beaker He's trying to optimize the raytrace method, run this: @JFK, One thing that makes me wonder is how, in the low level, is achieved a fog effect in the Directx engine, I mean, there must be already something internally that keep track of the 3D position of each pixel (if not, how the fog effect is drawn?), and if that is the case couldn't we just retrieve the information some way directly from the engine ... this could optimize a lot the calc for the 3d position of each pixel ... am I wrong? Paolo. |
| ||
Paolo - yes, exactly! It's the Z-Buffer or depth buffer. We have no access to it in Blitz 3D. It's an old feature request, unfortunately never added. Ok, I'll try your source. EDIT tried it, it's a good example, also for how slow CameraPick and EntityVisible are. If you would use my replacement, it would be pretty fast. Unfortunately my Fog-by-distance method cannot tell you the picked normals, that is used in the source (and of course, it would also be useful in mine). Edit: actually access to a depth buffer containing 16 bit or 32 bit information would solve my rounding error problem due to the fog scales immediately. Considering the point that I started this yesterday evening it evolves pretty quickly, with some optimations this could be useful the sooner or later. |
| ||
So, we can not access the depth buffer because of a DirectX limitation or because of Blitz, if it is Blitz, then do you think the system properties added for the "Direct3DDevice7 and so... " could be used to access the depth buffer? Is this what that "Dx7.dll" made by Tom used to do? Paolo. |
| ||
I don't know, I only know I would love to have access to the depth buffer :) I really should learn this DX7 stuff, this might be useful. The only DLL by Tom I know it the memory lib: -peeking and poking absolute adresses -get camera properties -get texture properties -get entity properties -get buffer properties -get brush properties although there is one function "LockedDepth%(buffer%)", I doubt this gives access to the depth buffer since it takes only one parameter and not x and y. So DX7.dl is something diffrent? I'll try to locate it. EDIT Paolo: here it is: http://www.tomspeed.com/shadowvolume/ Well thank you Tom. THe decls file says: .lib "dx7test.dll" GetRenderTarget%(d3dDevice):"_GetRenderTarget@4" ;SetRenderTarget%(d3dDevice,buffer):"_SetRenderTarget@8" SupportsWBuffer%(d3dDevice):"_SupportsWBuffer@4" MaxTextureWidth%(d3dDevice):"_MaxTextureWidth@4" MaxTextureHeight%(d3dDevice):"_MaxTextureHeight@4" ZBufferBitDepth%(d3dDevice):"_ZBufferBitDepth@4" SetRenderState%(d3dDevice%,renderstate%,param%):"_SetRenderState@12" DeviceClear%(d3dDevice, dwFlags%, dwColorRGBA%, dvZ#, dwStencil%):"_DeviceClear@20" DeviceClear2%(d3dDevice, BBRect*,dwFlags%, dwColorRGBA%, dvZ#, dwStencil%):"_DeviceClear2@24" It seems this was used for the stencil shadows demo. Not sure if it let's you access the depth buffer. ZBufferBitDepth - what ever that means. You know, it would be cool if we could do something like "SetBuffer ZBuffer()" and then simly ReadPixelFast the depth. |
| ||
I am just curious why you're doing this. Is it for pleasure? There's zero practical use for this. You are better off using stencil buffer. |
| ||
I don't think so. You cannot compare stencil buffer with pixel shaders. stencil require shadow/projection volumes, where a pseudo pixel shader don't. It' a huge diffrence. The point is, if this is once working properly, all you have to do is push the button to have full shadows on everything, including self shadowing even of complex and recursive things. Pixel shader don't care too much about the polycount of the scene. Now I would agree if you said "why don't you use hardware pixel shaders". That's true and with a graphicscard that supports DX 9 and the right programming tools this would be my choice. But I have an old card an I also don't think you can use pixel shaders in Blitz3D. But basicly the reason why I do this is the challenge to do the impossible. It's hacking fever that may be compared to hunting fever, something a lot of people don't know, of course. Maybe this way of trying the impossible can be compared with the first wolfenstein that was running on weak hardware that was thought to be way to slow to do any liquid 3D presentation. I think a lot of people were asking too "why are they doing this" until it was finished and released. |
| ||
I think this is awesome, jfk. Go for it, and let us know how we can help. Who cares if it ever becomes practical, it's still cool to see how far you can bend the rules of blitz, and probably more good things will come out of it than you might think. good luck! roland |
| ||
Yeah, I think this is cool! Keep it going! |
| ||
nice progress jfk! keep us informed please :D |
| ||
I know this is simple and dumb, but please don't make fun. Why does this not work? By the way, it is very slow, so if all you see is a black screen, then wait a minute for it to render. |
| ||
Ok, never mind, I stole a snippet of Eurythmia's code and now it works. JFK, I am a bit confused about wich parts of your code do the camera picking and entity visible parts. Would you mind posting two functions like "MyCameraPick()" and "MyEntityVisible()"? |
| ||
Thanks for the comments everybody. I think I'll continue with this when I have found a way to access the depth buffer, because of the mentioned rounding error when using fog. I also need a better way to place the triangles to make sure they woll be seen by the ICU camera. Blitznerd - your method is exactly what I described in the very first few lines of this thread. Using Camerapick and ENtityvisible. Right now I ain't got the time to write generic functions, also because they are woven into the code, but let me try to explain it (although i already did!) The camera pick is replaced by the socalled "distance by fog" trick. All meshes are fullbright (fx) and white (may be a temporary setting). Then there's some black fog, so the bigger the distance, the darker they are. When picking a pixel this gives the Z distance to the camera, but not the 3D-coordinate of the pixel. So we place a pivot at the cameras position, move it in the x and y direction by pixel coord * Z (because the higher Z is, the more is pixel x and y in 3D!). If we once moved the pivot to the right X and Y position (Z is still equal camera Z) we only need to move it in direction Z by the previously calulated Z distance. FOr test purposes this method is executed parallely to the original method using Camerapick. Two cubes (red and blue, scaled 0.1) are poisitioned at the two coords, one of them rotated 45,45,45. Now you can see if the two methods return the same coords. The entityvisible is then replaced by something dramaticly faster. Instead of picking the light from every required pixels 3d coord, we simply perform one single renderworld (well 6 ones, 1 in every direction) from the viewpoint of the light and check if a certain 3D coordinate can be seen on the rendered pics. For this we position a triangle at every previously "picked" coordinate. Each triangle is colored in a absolutely unique, individual color. The color is generated by the pixels X and Y coordinate. So later when we detect a triangle on the rendered pics, its color can be reversly decoded to get the pixel coordinate it is representing. In this method it's very simple, the green channel is storing X and the blue channel holds Y (well, plus 10 to allow black unpickable background etc.) If you want to use a higher sampling resolution than 160*120, one byte per X/Y won't be enough. Colors in Readpixelfast are limited to 24 Bits so you cannot use 32 Bit for encoding. So you best use 12 Bits per X/Y, that would be: X_enc=((x+10) shl 12) Y_enc=(y+10) RGB_enc=X_enc or Y_enc So this color can be used with VertexColor Later we'll read a pixel and decode it this way: RGB=readpixelfast(x,y) and $FFFFFF x_enc=(rgb shr 12)-10 y_enc=(rgb and %111111111111) -10 now if they are within the sampled resoltion, a triangle was detected. There is a further problem. We are using a mesh and color its vertices, this way amounts of 160*120 colors can be rendered quickly (using the same amount of individual eg. cubes would almost freeze the machine). This works nicely and modern cards eat such amounts of triangles like an appetizer. But if you want to use a higher sampling rate (perfect would be a rate that is equal the screen resolution to get 1 shading evaluation for every pixel!), there will arise several problems: the max number of vertices in a surface is about 65535. A you see with 320*240 we would already go higher (one triangle needs 3 Vertices here). So we must split it up to a number of ICU meshes. I did this, 4 meshes as mentioned near the second screenie. It works, but it complicates things unneccessarily. Once again it's Microsoft (remember Bill Gates saying "None will ever need more then 64 KB Ram in his homecomputer"). Needless to say this all won't work in a 16 Bit color depth. I'll continue with this from time to time as I do since a year or so (mainly inside my head btw) and will let you know when there's some progress. |
| ||
Try this : |
| ||
This Source is Very Slow!!! |
| ||
This Source is Very Slow!!! 1) Which source, 2) Could you do faster? :) |
| ||
filax - your code is very interesting, tho the goal was to use raytracing in order to add shadows to blitz3d, so it must be able to access the blitz geometry etc. |
| ||
Dont mean to hijack the thread :P but maybe you could just wait for the stencil shadow system (how much longer) which will be good enough. This IS after all dx7 and as you say you dont have access to the depth buffer. Or actually, you DO have access to it, using the same commands i'm using in the stencil operations, but i dont know the dx7 sdk enough to tell you how to do it. I'm sure you could read up on it if you found some dx7 docs and use Tom's dll to do it... |
| ||
well, i tried to make a raytracing shadow lib for directx renderer(not realtime), but i didn't manage that. but you did, and you have got my full respect! |
| ||
the source of filax works VERY FAST for a raytracing engine!!!! i have seen a raytracer which needs hours for a pic for that his one^^ needs seconds! |
| ||
Braincell I asked Tom, tho no answer. This shadow experiment was funny, maybe it will be useful when we have terahertz puters, right now it's frozen. Any stencil shadow solution for BLitz3D is welcome, or a BMax 3D Engine by BRL. |
| ||
i have modifyed your version a little filax, and that is it now:Graphics 640, 480, 32, 2 SetBuffer BackBuffer() SeedRnd MilliSecs() ;Raytracer InitRaytracer() SetCameraPosition(0, -25, -130) SetLightPosition(35, 200, -35) SetAmbientLight(255) CreateScene() ;Loadscreen SetFont LoadFont("Arial", GraphicsHeight() / 5, True, False, False) Text GraphicsWidth() / 2, GraphicsHeight() / 2, "Please wait...", True, True Flip ;Render Raytrace() Flip WaitKey() End Function CreateScene() AddEntity("Plane", 1, 100, $857830) ;Floor AddEntity("Sphere", 100, -0, 200, 100, $660000) ;Red ball AddEntity("Sphere", -100, -80, 325, 100, $006600) ;Green ball AddEntity("Sphere", 0, -50, 410, 100, $000066) ;Blue ball End Function Type Plane Field nx#, ny#, nz# Field dis# Field argb End Type Type Sphere Field x#, y#, z# Field rad# Field argb End Type Dim normalx#(-1, -1) Dim normaly#(-1, -1) Dim normalz#(-1, -1) Global camerax#, cameray#, cameraz# Global lightx#, lighty#, lightz# Global ambientcol Function InitRaytracer() w = GraphicsWidth() h = GraphicsHeight() Dim normalx#(w, h) Dim normaly#(w, h) Dim normalz#(w, h) SetupNormals() End Function Function Raytrace() Cls LockBuffer() width = GraphicsWidth() height = GraphicsHeight() For y = 0 To height - 1 For x = 0 To width - 1 rgb = SendRay(camerax#, cameray#, cameraz#, normalx#(x, y), normaly#(x, y), normalz#(x, y), ambientcol) If rgb <> $000000 Then WritePixelFast x, y, rgb, BackBuffer() Next Next UnlockBuffer() End Function Function SendRay(ex#, ey#, ez#, evx#, evy#, evz#, c = 255, recursive_times = 4) If recursive_times < 0 Then Return Local z# = 10000 For p.Plane = Each Plane nx# = p\nx# ny# = p\ny# nz# = p\nz# dis# = nx# * evx# + ny# * evy# + nz# * evz# If dis# < 0 Then dis2# = -(p\dis# + nx# * ex# + ny# * ey# + nz# * ez#) / dis# ix# = ex# + evx# * dis2# iy# = ey# + evy# * dis2# iz# = ez# + evz# * dis2# lvx# = lightx# - ix# lvy# = lighty# - iy# lvz# = lightz# - iz# dis# = Sqr(lvx# * lvx# + lvy# * lvy# + lvz# * lvz#) lvx# = lvx# / dis# lvy# = lvy# / dis# lvz# = lvz# / dis# dis# = (nx# * evx# + ny# * evy# + nz# * evz#) * 2 rnx# = evx# - nx# * dis# rny# = evy# - ny# * dis# rnz# = evz# - nz# * dis# c1 = (nx# * lvx# + ny# * lvy# + nz# * lvz#) * 256 argb = p\argb z# = dis2# EndIf Next For s.Sphere = Each Sphere svx# = s\x# - ex# svy# = s\y# - ey# svz# = s\z# - ez# dis2# = svx# * evx# + svy# * evy# + svz# * evz# dis# = svx# * svx# + svy# * svy# + svz# * svz# - dis2# * dis2# If dis# <= s\rad# * s\rad# Then dis2# = dis2# - Sqr(s\rad# * s\rad# - dis#) If dis2# > 0 And dis2# < z# Then ix# = ex# + evx# * dis2# iy# = ey# + evy# * dis2# iz# = ez# + evz# * dis2# nx# = Float(ix# - s\x#) / s\rad# ny# = Float(iy# - s\y#) / s\rad# nz# = Float(iz# - s\z#) / s\rad# lvx# = lightx# - ix# lvy# = lighty# - iy# lvz# = lightz# - iz# dis# = Sqr(lvx# * lvx# + lvy# * lvy# + lvz# * lvz#) lvx# = lvx# / dis# lvy# = lvy# / dis# lvz# = lvz# / dis# dis# = (nx# * evx# + ny# * evy# + nz# * evz#) * 2 rnx# = evx# - nx# * dis# rny# = evy# - ny# * dis# rnz# = evz# - nz# * dis# c1 = (nx# * lvx# + ny# * lvy# + nz# * lvz#) * 256 c2 = (rnx# * lvx# + rny# * lvy# + rnz# * lvz#) ^ 10 * 256 argb = s\argb z# = dis2# EndIf EndIf Next If Shadowed(ix#, iy#, iz#) Then c1 = 30 c2 = 0 Else If c1 < 30 Then c1 = 30 If c2 < 0 Then c2 = 0 EndIf c3 = c1 * c Shr 8 r = (argb And $FF0000) * c3 Shr 8 + c2 Shl 16 g = (argb And $00FF00) * c3 Shr 8 + c2 Shl 8 b = (argb And $0000FF) * c3 Shr 8 + c2 If argb <> $000000 Then argb = SendRay(ix#, iy#, iz#, rnx#, rny#, rnz#, c - 32, recursive_times - 1) r = r + (argb And $FF0000) g = g + (argb And $00FF00) b = b + (argb And $0000FF) If r > $FF0000 Then r = $FF0000 Else r = r And $FF0000 If g > $00FF00 Then g = $00FF00 Else g = g And $00FF00 If b > $0000FF Then b = $0000FF Else b = b And $0000FF Return r Or g Or b End Function Function Shadowed(x#, y#, z#) evx# = lightx# - x# evy# = lighty# - y# evz# = lightz# - z# dis# = Sqr(evx# * evx# + evy# * evy# + evz# * evz#) evx# = evx# / dis# evy# = evy# / dis# evz# = evz# / dis# For s.Sphere = Each Sphere svx# = s\x# - x# svy# = s\y# - y# svz# = s\z# - z# dis2# = svx# * evx# + svy# * evy# + svz# * evz# dis# = svx# * svx# + svy# * svy# + svz# * svz# - dis2# * dis2# If dis2# > 0 And dis# < s\rad# * s\rad# Then Return True Next End Function Function SetCameraPosition(x#, y#, z#) camerax# = x# cameray# = y# cameraz# = z# End Function Function SetLightPosition(x#, y#, z#) lightx# = x# lighty# = y# lightz# = z# End Function Function SetAmbientLight(col) ambientcol = col End Function Function AddEntity(typ$, p1$ = "", p2$ = "", p3$ = "", p4$ = "", p5$ = "") Select Lower(typ$) Case "sphere" s.Sphere = New Sphere s\x# = p1$ s\y# = p2$ s\z# = p3$ s\rad# = p4$ s\argb = p5$ Case "plane" p.Plane = New Plane p\ny# = p1$ p\dis# = p2$ p\argb = p3$ Default RuntimeError "Entity type not found." End Select End Function Function SetupNormals() width = GraphicsWidth() height = GraphicsHeight() nz# = 200 For y = 0 To height - 1 ny# = Float(height Shr 1 - y) * 240.0 / Float(width) For x = 0 To width - 1 nx# = Float(width Shr 1 - x) * 240.0 / Float(width) dis# = Sqr(nx# * nx# + ny# * ny# + nz# * nz#) normalx#(x, y) = -nx# / dis# normaly#(x, y) = ny# / dis# normalz#(x, y) = nz# / dis# Next Next End Function |
| ||
Nice work :) ! Any idea to add - Cube and cylinder ? - Multi light ? - Camera rotation ? - Object rotation ? Remember Devil :) on amiga the same image with DKB Trace take 30/40 hours of computing for a resolution of 320/240... I have try to add cone primitive but without good result : http://www.frontiernet.net/~imaging/raytracing.html Move control added : |
| ||
ok, i managed multi lights, and the shadows are oversampling! but i dont know how i can make 2 light flare reflections on the spheres! ---- edit: code deleted! i have managed multi lights and all the stuff arround the lights. especialy the flared on the spheres :) i'll post it in here when it is ready! |
| ||
Hi Devils :) I find you shadows sampling very good ! Very usefull with multi light :) I have find doc about Raytracing ! i hope to help you to find multi light reflection ! http://www.cescg.org/CESCG98/MDolezal/index.html http://www.devmaster.net/articles/raytracing_series/part3.php http://www.flipcode.com/articles/article_raytrace03.shtml Look this topic : Its a raytracer made with purebasic it work with multi light specularity ! http://www.purebasic.fr/english/viewtopic.php?t=15568&postdays=0&postorder=asc&start=45 ![]() |
| ||
have u managed multi lights? i have managed it a bit, but the spheres arent shadowed very much,...why?? Include "Raytracer.bb" Graphics 640, 480, 32, 2 SetBuffer BackBuffer() SeedRnd MilliSecs() ;Raytracer InitRaytracer() SetCameraPosition(0, -25, -130) SetAmbientLight(255) CreateScene() ;Loadscreen SetFont LoadFont("Arial", GraphicsHeight() / 5, True, False, False) Text GraphicsWidth() / 2, GraphicsHeight() / 2, "Please wait...", True, True Flip ;Render Raytrace() Flip WaitKey() End Function CreateScene() AddEntity("Light", 50, 1000, 800) ;Light1 AddEntity("Light", -200, 200, 400) ;Light2 AddEntity("Plane", 1, 100, $857830) ;Floor AddEntity("Sphere", 100, 10, 200, 100, $FF0000) ;Red ball AddEntity("Sphere", -100, -80, 385, 100, $00FF00) ;Green ball AddEntity("Sphere", 0, -50, 510, 100, $0000FF) ;Blue ball End Function Type Light Field x#, y#, z# End Type Type Plane Field nx#, ny#, nz# Field dis# Field argb End Type Type Sphere Field x#, y#, z# Field rad# Field argb End Type Dim normalx#(-1, -1) Dim normaly#(-1, -1) Dim normalz#(-1, -1) Global camerax#, cameray#, cameraz# Global ambientcol Function InitRaytracer() w = GraphicsWidth() h = GraphicsHeight() Dim normalx#(w, h) Dim normaly#(w, h) Dim normalz#(w, h) SetupNormals() End Function Function Raytrace() Cls LockBuffer() width = GraphicsWidth() height = GraphicsHeight() For y = 0 To height - 1 For x = 0 To width - 1 rgb = SendRay(camerax#, cameray#, cameraz#, normalx#(x, y), normaly#(x, y), normalz#(x, y), ambientcol) If rgb <> $000000 Then WritePixelFast x, y, rgb, BackBuffer() Next Next UnlockBuffer() End Function Function SendRay(ex#, ey#, ez#, evx#, evy#, evz#, c = 255, recursive_times = 4) If recursive_times < 0 Then Return z# = 10000 l.Light = First Light For p.Plane = Each Plane nx# = p\nx# ny# = p\ny# nz# = p\nz# dis# = nx# * evx# + ny# * evy# + nz# * evz# If dis# < 0 Then dis2# = -(p\dis# + nx# * ex# + ny# * ey# + nz# * ez#) / dis# ix# = ex# + evx# * dis2# iy# = ey# + evy# * dis2# iz# = ez# + evz# * dis2# lvx# = l\x# - ix# lvy# = l\y# - iy# lvz# = l\z# - iz# dis# = Sqr(lvx# * lvx# + lvy# * lvy# + lvz# * lvz#) lvx# = lvx# / dis# lvy# = lvy# / dis# lvz# = lvz# / dis# dis# = (nx# * evx# + ny# * evy# + nz# * evz#) * 2 rnx# = evx# - nx# * dis# rny# = evy# - ny# * dis# rnz# = evz# - nz# * dis# c1 = (nx# * lvx# + ny# * lvy# + nz# * lvz#) * 256 argb = p\argb z# = dis2# EndIf Next For s.Sphere = Each Sphere svx# = s\x# - ex# svy# = s\y# - ey# svz# = s\z# - ez# dis2# = svx# * evx# + svy# * evy# + svz# * evz# dis# = svx# * svx# + svy# * svy# + svz# * svz# - dis2# * dis2# If dis# <= s\rad# * s\rad# Then dis2# = dis2# - Sqr(s\rad# * s\rad# - dis#) If dis2# > 0 And dis2# < z# Then ix# = ex# + evx# * dis2# iy# = ey# + evy# * dis2# iz# = ez# + evz# * dis2# nx# = Float(ix# - s\x#) / s\rad# ny# = Float(iy# - s\y#) / s\rad# nz# = Float(iz# - s\z#) / s\rad# For l.Light = Each Light lvx# = l\x# - ix# lvy# = l\y# - iy# lvz# = l\z# - iz# dis# = Sqr(lvx# * lvx# + lvy# * lvy# + lvz# * lvz#) lvx# = lvx# / dis# lvy# = lvy# / dis# lvz# = lvz# / dis# dis# = (nx# * evx# + ny# * evy# + nz# * evz#) * 2 rnx# = evx# - nx# * dis# rny# = evy# - ny# * dis# rnz# = evz# - nz# * dis# xx = Shadowed(ix#, iy#, iz#) If xx < 255 Then xx = 1 Else xx = 2 c1 = (nx# * lvx# + ny# * lvy# + nz# * lvz#) * 256 * xx c2 = c2 + (rnx# * lvx# + rny# * lvy# + rnz# * lvz#) ^ 10 * 256 Next argb = s\argb z# = dis2# EndIf EndIf Next sh = Shadowed(ix#, iy#, iz#) If sh < 255 Then c1 = (255 - 10000.0 / Float(sh)) * .4 If c1 < 0 Then c1 = 0 If c1 > 255 Then c1 = 255 c2 = cs * 255.0 / Float(sh) Else If c1 < 30 Then c1 = 30 If c2 < 0 Then c2 = 0 EndIf c3 = c1 * c Shr 8 r = (argb And $FF0000) * c3 Shr 8 + c2 Shl 16 g = (argb And $00FF00) * c3 Shr 8 + c2 Shl 8 b = (argb And $0000FF) * c3 Shr 8 + c2 ;If argb <> 0 Then argb = SendRay(ix#, iy#, iz#, rnx#, rny#, rnz#, c - 32, recursive_times - 1) r = r + (argb And $FF0000) g = g + (argb And $00FF00) b = b + (argb And $0000FF) If r > $FF0000 Then r = $FF0000 Else r = r And $FF0000 If g > $00FF00 Then g = $00FF00 Else g = g And $00FF00 If b > $0000FF Then b = $0000FF Else b = b And $0000FF Return r Or g Or b End Function Function Shadowed(x#, y#, z#) col = 255 For l.Light = Each Light evx# = l\x# - x# evy# = l\y# - y# evz# = l\z# - z# dis# = Sqr(evx# * evx# + evy# * evy# + evz# * evz#) evx# = evx# / dis# evy# = evy# / dis# evz# = evz# / dis# ar = False For s.Sphere = Each Sphere svx# = s\x# - x# svy# = s\y# - y# svz# = s\z# - z# dis2# = svx# * evx# + svy# * evy# + svz# * evz# dis# = svx# * svx# + svy# * svy# + svz# * svz# - dis2# * dis2# If dis2# > 0 And dis# < s\rad# * s\rad# And ar = False Then col = col / 5 ar = True EndIf Next Next Return col End Function Function SetCameraPosition(x#, y#, z#) camerax# = x# cameray# = y# cameraz# = z# End Function Function SetAmbientLight(col) ambientcol = col End Function Function AddEntity(typ$, p1$ = "", p2$ = "", p3$ = "", p4$ = "", p5$ = "") Select Lower(typ$) Case "sphere" s.Sphere = New Sphere s\x# = p1$ s\y# = p2$ s\z# = p3$ s\rad# = p4$ s\argb = p5$ Case "plane" p.Plane = New Plane p\ny# = p1$ p\dis# = p2$ p\argb = p3$ Case "light" l.Light = New Light l\x# = p1$ l\y# = p2$ l\z# = p3$ Default RuntimeError "Entity type not found." End Select End Function Function SetupNormals() width = GraphicsWidth() height = GraphicsHeight() nz# = 200 For y = 0 To height - 1 ny# = Float(height Shr 1 - y) * 240.0 / Float(width) For x = 0 To width - 1 nx# = Float(width Shr 1 - x) * 240.0 / Float(width) dis# = Sqr(nx# * nx# + ny# * ny# + nz# * nz#) normalx#(x, y) = -nx# / dis# normaly#(x, y) = ny# / dis# normalz#(x, y) = nz# / dis# Next Next End Function |
| ||
It's strange you seem lost specularity ? and mirroring ? For the shadow its maybe a problem of values clamp ? when you compute shadow color ? Take a look on the purebasic raytracer (see up) the rendering is good but slower than you method.But personaly i haven't tested blitz source with multi light, it's a little bit too hard for me :) But you work and your method by entity is really good i hope that you continue ! |
| ||
Sorry for specularity i haven't see the : If argb <> 0 Then argb = SendRay(ix#, iy#, iz#, rnx#, rny#, rnz#, c - 32, recursive_times - 1) :) for the graphics glitch on reflection and shadow it seem dependency of the ambient light value !? may be your computing of the material + light value + ambiant value is wrong ? or not clamped ? Try this scene : Function CreateScene() AddEntity("Light", 400, 400, -400) ;Light1 AddEntity("Light", -400, 400, -400) ;Light2 AddEntity("Plane", 1, 100, $857830) ;Floor AddEntity("Sphere", 100, 10, 100, 50, $660000) ;Red ball AddEntity("Sphere", 0, 10, 125, 50, $006600) ;Green ball AddEntity("Sphere", -100, 10, 100, 50, $000066) ;Blue ball End Function i don't know why shadows squeeze the blend? |
| ||
well, thats ok... i think that if there are multi lights AND reflections, the balls looks to silver! i will stay at the version without the mult lights... |
| ||
Hi devils Any idea for camera rotation ? |
| ||
no, because i don't know how exactly your raytracer works... i don't see any rays which should be sended...?! |
| ||
but i have added multi lights succesfully now:Graphics 640, 480, 32, 2 SetBuffer BackBuffer() SeedRnd MilliSecs() ;Raytracer InitRaytracer() CreateScene() ;Loadscreen SetFont LoadFont("Arial", GraphicsHeight() / 5, True, False, False) Text GraphicsWidth() / 2, GraphicsHeight() / 2, "Please wait...", True, True Flip ;Render Raytrace() Flip WaitKey() End Function CreateScene() AddEntity("Light", 50, 1000, 800) ;Light1 AddEntity("Light", -200, 200, 400) ;Light2 AddEntity("Plane", 1, 100, $857830) ;Floor AddEntity("Sphere", 100, 10, 200, 100, $FF0000) ;Red ball AddEntity("Sphere", -100, -80, 385, 100, $00FF00) ;Green ball AddEntity("Sphere", 0, -50, 510, 100, $0000FF) ;Blue ball SetCameraPosition(0, -25, -130) SetAmbientLight(255) End Function Type Light Field x#, y#, z# End Type Type Plane Field nx#, ny#, nz# Field dis# Field argb End Type Type Sphere Field x#, y#, z# Field rad# Field argb End Type Dim normalx#(-1, -1) Dim normaly#(-1, -1) Dim normalz#(-1, -1) Global camerax#, cameray#, cameraz# Global ambientcol Function InitRaytracer() w = GraphicsWidth() h = GraphicsHeight() Dim normalx#(w, h) Dim normaly#(w, h) Dim normalz#(w, h) SetupNormals() End Function Function Raytrace() Cls LockBuffer() width = GraphicsWidth() height = GraphicsHeight() For y = 0 To height - 1 For x = 0 To width - 1 rgb = SendRay(camerax#, cameray#, cameraz#, normalx#(x, y), normaly#(x, y), normalz#(x, y), ambientcol) If rgb <> $000000 Then WritePixelFast x, y, rgb, BackBuffer() Next Next UnlockBuffer() End Function Function SendRay(ex#, ey#, ez#, evx#, evy#, evz#, c = 255, recursive_times = 4) If recursive_times < 0 Then Return Local z# = 10000 l.Light = First Light For p.Plane = Each Plane nx# = p\nx# ny# = p\ny# nz# = p\nz# dis# = nx# * evx# + ny# * evy# + nz# * evz# If dis# < 0 Then dis2# = -(p\dis# + nx# * ex# + ny# * ey# + nz# * ez#) / dis# ix# = ex# + evx# * dis2# iy# = ey# + evy# * dis2# iz# = ez# + evz# * dis2# lvx# = l\x# - ix# lvy# = l\y# - iy# lvz# = l\z# - iz# dis# = Sqr(lvx# * lvx# + lvy# * lvy# + lvz# * lvz#) lvx# = lvx# / dis# lvy# = lvy# / dis# lvz# = lvz# / dis# dis# = (nx# * evx# + ny# * evy# + nz# * evz#) * 2 rnx# = evx# - nx# * dis# rny# = evy# - ny# * dis# rnz# = evz# - nz# * dis# c1 = (nx# * lvx# + ny# * lvy# + nz# * lvz#) * 256 argb = p\argb z# = dis2# EndIf Next c2 = 0 For s.Sphere = Each Sphere svx# = s\x# - ex# svy# = s\y# - ey# svz# = s\z# - ez# dis2# = svx# * evx# + svy# * evy# + svz# * evz# dis# = svx# * svx# + svy# * svy# + svz# * svz# - dis2# * dis2# If dis# <= s\rad# * s\rad# Then dis2# = dis2# - Sqr(s\rad# * s\rad# - dis#) If dis2# > 0 And dis2# < z# Then ix# = ex# + evx# * dis2# iy# = ey# + evy# * dis2# iz# = ez# + evz# * dis2# nx# = Float(ix# - s\x#) / s\rad# ny# = Float(iy# - s\y#) / s\rad# nz# = Float(iz# - s\z#) / s\rad# For l.Light = Each Light lvx# = l\x# - ix# lvy# = l\y# - iy# lvz# = l\z# - iz# dis# = Sqr(lvx# * lvx# + lvy# * lvy# + lvz# * lvz#) lvx# = lvx# / dis# lvy# = lvy# / dis# lvz# = lvz# / dis# dis# = (nx# * evx# + ny# * evy# + nz# * evz#) * 2 rnx# = evx# - nx# * dis# rny# = evy# - ny# * dis# rnz# = evz# - nz# * dis# c2 = c2 + (rnx# * lvx# + rny# * lvy# + rnz# * lvz#) ^ 10 * 256 Next c1 = (nx# * lvx# + ny# * lvy# + nz# * lvz#) * 256 argb = s\argb z# = dis2# EndIf EndIf Next sh = Shadowed(ix#, iy#, iz#) If c2 < 50 Then c2 = 50 If sh < 255 Then c1 = sh c2 = 0 Else c2 = c2 * 2 If c1 < 30 Then c1 = 30 If c2 < 0 Then c2 = 0 EndIf c3 = c1 * c Shr 8 r = (argb And $FF0000) * c3 Shr 8 + c2 Shl 16 g = (argb And $00FF00) * c3 Shr 8 + c2 Shl 8 b = (argb And $0000FF) * c3 Shr 8 + c2 If argb <> $000000 Then argb = SendRay(ix#, iy#, iz#, rnx#, rny#, rnz#, c - 32, recursive_times - 1) r = r + (argb And $FF0000) / 8 g = g + (argb And $00FF00) / 8 b = b + (argb And $0000FF) / 8 If r > $FF0000 Then r = $FF0000 Else r = r And $FF0000 If g > $00FF00 Then g = $00FF00 Else g = g And $00FF00 If b > $0000FF Then b = $0000FF Else b = b And $0000FF Return r Or g Or b End Function Function Shadowed(x#, y#, z#) col = 255 For l.Light = Each Light evx# = l\x# - x# evy# = l\y# - y# evz# = l\z# - z# dis# = Sqr(evx# * evx# + evy# * evy# + evz# * evz#) evx# = evx# / dis# evy# = evy# / dis# evz# = evz# / dis# ar = False For s.Sphere = Each Sphere svx# = s\x# - x# svy# = s\y# - y# svz# = s\z# - z# dis2# = svx# * evx# + svy# * evy# + svz# * evz# dis# = svx# * svx# + svy# * svy# + svz# * svz# - dis2# * dis2# If dis2# > 0 And dis# < s\rad# * s\rad# And ar = False Then col = col / 2 ar = True EndIf Next Next Return col End Function Function SetCameraPosition(x#, y#, z#) camerax# = x# cameray# = y# cameraz# = z# End Function Function SetAmbientLight(col) ambientcol = col End Function Function AddEntity(typ$, p1$ = "", p2$ = "", p3$ = "", p4$ = "", p5$ = "") Select Lower(typ$) Case "sphere" s.Sphere = New Sphere s\x# = p1$ s\y# = p2$ s\z# = p3$ s\rad# = p4$ s\argb = p5$ Case "plane" p.Plane = New Plane p\ny# = p1$ p\dis# = p2$ p\argb = p3$ Case "light" l.Light = New Light l\x# = p1$ l\y# = p2$ l\z# = p3$ Default RuntimeError "Entity type not found." End Select End Function Function SetupNormals() width = GraphicsWidth() height = GraphicsHeight() nz# = 200 For y = 0 To height - 1 ny# = Float(height Shr 1 - y) * 240.0 / Float(width) For x = 0 To width - 1 nx# = Float(width Shr 1 - x) * 240.0 / Float(width) dis# = Sqr(nx# * nx# + ny# * ny# + nz# * nz#) normalx#(x, y) = -nx# / dis# normaly#(x, y) = ny# / dis# normalz#(x, y) = nz# / dis# Next Next End Function |
| ||
It's strange the render is really different ? more flat ? |
| ||
yes, in fact there are rays sended, and this is much more costly than your raytracer! but if you take the standart methode of raytracing there will come some errors... this^^ is a different kind of raytracing, and i'm trying to find out how it works! i don't know how it works, can you may explain me? |
| ||
ok, i am actually working on a version between direct x and raytracing! give me some time and i'll let you know when its ready :) |
| ||
?? DirectX + Raytracing ? :?? |
| ||
yes... here is a little test version: Graphics3D 160, 120, 32, 3 SetBuffer BackBuffer() AmbientLight 200, 200, 200 ;Camera Cam = CreateCamera() PositionEntity Cam, 0, 10, -10 RotateEntity Cam, 40, 0, 0 ;Raytracer SetShadowCam(Cam) ;Plane c = CreatePlane() EntityColor c, 200, 200, 255 SetShadowMesh(c) ;Cube Cube = CreateCube() ScaleEntity Cube, 1.3, 1.3, 1.3 EntityColor Cube, 255, 0, 0 PositionEntity Cube, 0, 3, 0 SetShadowMesh(Cube) ;Light Light = CreateShadowLight() PositionEntity Light, 0, 10, 5 While Not KeyHit(1) TurnEntity Cube, 1, 2, 3 RenderWorld Raytrace() Flip Wend End Type ShadowLight Field ent, div# End Type Global ShadowCam Function SetShadowMesh(ent) EntityPickMode ent, 2 End Function Function SetShadowCam(ent) ShadowCam = ent End Function Function CreateShadowLight() sl.ShadowLight = New ShadowLight sl\ent = CreatePivot() sl\div# = 2 Return sl\ent End Function Function Raytrace() w = GraphicsWidth() h = GraphicsHeight() Piv = CreatePivot() LockBuffer BackBuffer() For sl.ShadowLight = Each ShadowLight For x = 0 To w AppTitle Int(x * 100.0 / Float(w)) + " %" For y = o To h If CameraPick(ShadowCam, x, y) Then PositionEntity Piv, PickedX(), PickedY(), PickedZ() AlignToVector Piv, PickedNX(), PickedNY(), PickedNZ(), 2 MoveEntity Piv, 0, .00001, 0 EntityPickMode Piv, 1 If EntityVisible(Piv, sl\ent) = False Then rgb = ReadPixelFast(x, y, BackBuffer()) r = ((rgb And $FF0000) / $10000) / sl\div# g = ((rgb And $FF00) / $100) / sl\div# b = Float(rgb And $FF) / sl\div# WritePixelFast x, y, r * $10000 + g * $100 + b, BackBuffer() EndIf EntityPickMode Piv, 0 EndIf Next Next Next UnlockBuffer BackBuffer() FreeEntity Piv End Function any idea to get it faster? |
| ||
can you make your raytracer polygonal? :) that would be fine, because nobody ever made a polygonal raytracer in this or in an other community :/ |
| ||
I'm sorry but i have absolutly no idea about convert my raytracer for polygonal mode ?! :) But i have seen your test with bigger interest ! the method with camerapick is a really good idea ! But seem more slower than old method ? why ? |
| ||
i don't know :/ |
| ||
What I tried to do is put a camera where the light is pointing to object, then put the image to texture quad on the ground. I also moved the camera and object like 10000 units offset and make the object fullbright for the render. I couldnt get masking to work with vram, and it had many glitches, but with enough tinkering it might work. Here is screenshot in ideal conditions, and black border around the shadow edited away with paint. ![]() |
| ||
can you please give me the code of that :) ? |
| ||
Btw, the LockedDepth just tells the color depth (16/32), it has nothing to do with depthbuffer. But trust me, this doesn't work very well: ;Shadow System by nawi ;use numpad to move the light Global SS_Cam,SS_SceneCam Global SS_Width,SS_Height Global SS_Img Type SS_Light Field X#,Y#,Z# Field ShadowMesh,ShadowSurface Field ShadowTexture Field ShadowOffset End Type Graphics3D 800,600,32,2 SetBuffer BackBuffer() Cam = CreateCamera() SS_Init(512,512,Cam) t = CreateCube() MoveEntity t,0,1,3 light = SS_CreateLight(7,5,3) light2 = CreateLight() s = CreateSphere(8) EntityParent light2,s EntityColor s,255,255,0 ScaleEntity s,0.3,0.3,0.3 MoveEntity s,7,5,3 MoveEntity Cam,0,4,-7 Plane = CreatePlane() EntityColor Plane,200,200,200 Repeat If KeyDown(30) Then MoveEntity Cam,-0.3,0,0 If KeyDown(32) Then MoveEntity Cam,0.3,0,0 If KeyDown(17) Then MoveEntity Cam,0,0,0.3 If KeyDown(31) Then MoveEntity Cam,0,0,-0.3 If KeyDown(16) Then MoveEntity Cam,0,0.3,0 If KeyDown(18) Then MoveEntity Cam,0,-0.3,0 If KeyDown(75) Then SS_MoveLight(light,-0.1,0,0) MoveEntity s,-0.1,0,0 EndIf If KeyDown(77) Then SS_MoveLight(light,0.1,0,0) MoveEntity s,0.1,0,0 EndIf If KeyDown(72) Then SS_MoveLight(light,0,0.1,0) MoveEntity s,0,0.1,0 EndIf If KeyDown(80) Then SS_MoveLight(light,0,-0.1,0) MoveEntity s,0,-0.1,0 EndIf ;camera mx# = MouseXSpeed() my# = MouseYSpeed() mz# = MouseZSpeed() If MouseDown(2) Then TurnEntity Cam,my*0.1,-mx*0.1,0 RotateEntity Cam,EntityPitch#(Cam),EntityYaw#(Cam),0 EndIf If mz# Then zoom# = zoom# + mz#*0.1 CameraZoom Cam,zoom# EndIf FPS_C = FPS_C + 1 If MilliSecs() > FPSTimer + 999 Then FPSTimer = MilliSecs() FPS = FPS_C FPS_C = 0 EndIf SS_DrawShadow(light,t) UpdateWorld RenderWorld Text 0,0,"FPS: " + FPS Flip 0 Cls Until KeyHit(1) Function SS_CreateLight(SS_X#,SS_Y#,SS_Z#) SS_NewLight.SS_Light = New SS_Light SS_NewLight\X# = SS_X# SS_NewLight\Y# = SS_Y# SS_NewLight\Z# = SS_Z# SS_NewLight\ShadowMesh = CreateMesh() SS_NewLight\ShadowSurface = CreateSurface(SS_NewLight\ShadowMesh) SS_NewLight\ShadowTexture = CreateTexture(SS_Width,SS_Height,256+4) EntityFX SS_NewLight\ShadowMesh,1 EntityAlpha SS_NewLight\ShadowMesh,0.4 v0 = AddVertex(SS_NewLight\ShadowSurface,-1,0,1,0,0) v1 = AddVertex(SS_NewLight\ShadowSurface,1,0,1,1,0) v2 = AddVertex(SS_NewLight\ShadowSurface,-1,0,-1,0,1) v3 = AddVertex(SS_NewLight\ShadowSurface,1,0,-1,1,1) AddTriangle(SS_NewLight\ShadowSurface,v0,v1,v2) AddTriangle(SS_NewLight\ShadowSurface,v1,v3,v2) EntityTexture SS_NewLight\ShadowMesh,SS_NewLight\ShadowTexture Return Handle(SS_NewLight) End Function Function SS_Init(SS_ResX,SS_ResY,MainCam) SS_SceneCam = MainCam SS_Cam = CreateCamera() CameraViewport SS_Cam,0,0,SS_ResX,SS_ResY CameraProjMode SS_Cam,0 SS_Width = SS_ResX SS_Height = SS_ResY SS_Img = CreateImage(SS_ResX,SS_ResY) End Function Function SS_MoveLight(Light,SS_X#,SS_Y#,SS_Z#) SS_NewLight.SS_Light = Object.SS_Light(Light) SS_NewLight\X# = SS_NewLight\X# + SS_X# SS_NewLight\Y# = SS_NewLight\Y# + SS_Y# SS_NewLight\Z# = SS_NewLight\Z# + SS_Z# End Function Function SS_DrawShadow(Light,Obj) objx# = EntityX#(Obj) objy# = EntityY#(Obj) objz# = EntityZ#(Obj) EntityFX Obj,1 EntityColor Obj,50,50,50 SS_NewLight.SS_Light = Object.SS_Light(Light) MoveEntity SS_NewLight\ShadowMesh,0,0,-SS_NewLight\ShadowOffset# PositionEntity SS_Cam,SS_NewLight\X#,SS_NewLight\Y#,SS_NewLight\Z# PointEntity SS_NewLight\ShadowMesh,SS_Cam SAngle# = Abs(EntityPitch#(SS_NewLight\ShadowMesh)) RotateEntity SS_NewLight\ShadowMesh,0,EntityYaw#(SS_NewLight\ShadowMesh),0 FX# = SS_NewLight\X# - objx# FZ# = SS_NewLight\Z# - objz# de# = ((SS_NewLight\Y#)/Tan(Sangle#)-Sqr(FX#*FX#+FZ#*FZ#))*800 PointEntity SS_Cam,Obj PositionEntity SS_Cam,objx#,objy#,objz# MoveEntity SS_Cam,0,0,-5 HideEntity SS_NewLight\ShadowMesh PositionEntity SS_Cam,EntityX#(SS_Cam)+10000,EntityY#(SS_Cam)+10000,EntityZ#(SS_Cam)+10000 PositionEntity Obj,objx#+10000,objy#+10000,objz#+10000 CameraProjMode SS_Cam,1 CameraProjMode SS_SceneCam,0 UpdateWorld RenderWorld EntityFX Obj,0 EntityColor Obj,255,255,255 PositionEntity Obj,objx#,objy#,objz# ShowEntity SS_NewLight\ShadowMesh CopyRect 0,0,SS_Width,SS_Height,0,0,0,TextureBuffer(SS_NewLight\ShadowTexture) CameraProjMode SS_Cam,0 CameraProjMode SS_SceneCam,1 PositionEntity SS_NewLight\ShadowMesh,objx,0.01,objz ScaleEntity SS_NewLight\ShadowMesh,5,10,de# SS_NewLight\ShadowOffset# = -de#*0.17 MoveEntity SS_NewLight\ShadowMesh,0,0,SS_NewLight\ShadowOffset End Function |
| ||
ah, i see, textured shadows?? |
| ||
See nothing about shadow ? it's normal ? |
| ||
New version with camera |
| ||
New version with camera control : |
| ||
Understandably slow .. but very impressive!! |
| ||
Interesting reading all this. @jfk - going back to your original project, this might be a stupid idea but could you perhaps reduce your 'rounding errors due to the limited range of fog levels' by breaking up the fog bit into smaller steps? Something like: CameraFogRange cam,0,5 colour/distance check CameraFogRange cam,5,10 colour/distance check CameraFogRange cam,10,15 colour/distance check etc |
| ||
Hey Joe, thanks for reclaiming this plane :o) Not sure if this would work. Imagine each pixel must be tested for depth / z-distance to the camera. So you don't know the distance in the first place. The only solution I can think of right now would be to do an additional check with a lower fog bandwidth, as you suggested. Unfort this would require an additional renderworld. Probably it would be possible to refine the resolution by using something other than a simple greyscale, eg: fog color 255,128,64. Didn't test it, but maybe this will create more than 256 levels. |
| ||
I wrote this .dll that copies the zbuffer to the frontbuffer: http://members.home.nl/bramdenhond/zbuffer.zip Never tested it on any other machine than my own, though. I used BltFast to copy the zbuffer to the main buffer. I'm not sure how to convert the data to floats, mainly because I could only read 3 bytes using readpixel. |
| ||
This may be very useful. Right now I have no time to test it extensively, but maybe I will do so during new year. Tho, I think it could be a problem to have the zbuffer on the frontbuffer, the backbuffer would make much more sense to me (front buffer is the currently displayed Vram onscreen). converting to float may be easy when you use a bank: first read a 32 bit int using readpixel, then write this to a bank, then read it out as a float. You may also copy the zbuffer directly to a bank. |
| ||
wow, i got like 1FPS :Pits very nice tough!! |