Volumetric fog?
Blitz3D Forums/Blitz3D Programming/Volumetric fog?
| ||
So here I am working on this Underwater Demo, when I hit my first road block in blitz, traveling near the speed of light. I want to make the sea have that all so cool looking hazy feel using fog. However, Blitz does not seem to support this directly ( witch I always took for granted in OpenGL with Gui and Glut I guess. Anyway, Ive created a simple Fake fog system useing 10 spheres at 10% opacity. Its fast on my system and looks good enough, not as smooth on the transitions between layes as Id like but does do well for water type affects, I imagine it would work for ground fog too. The desire to not directly manipulate each entities color based on distance was my main reason for making this. pfog1 = CreateSphere(8,p\pivot) pfog2 = CreateSphere(8,p\pivot) pfog3 = CreateSphere(8,p\pivot) pfog4 = CreateSphere(8,p\pivot) pfog5 = CreateSphere(8,p\pivot) pfog6 = CreateSphere(8,p\pivot) pfog7 = CreateSphere(8,p\pivot) pfog8 = CreateSphere(8,p\pivot) pfog9 = CreateSphere(8,p\pivot) pfog10 = CreateSphere(8,p\pivot) FlipMesh pfog1 FlipMesh pfog2 FlipMesh pfog3 FlipMesh pfog4 FlipMesh pfog5 FlipMesh pfog6 FlipMesh pfog7 FlipMesh pfog8 FlipMesh pfog9 FlipMesh pfog10 pfogtex = LoadTexture ("textures\fog.bmp") EntityTexture pfog1, pfogtex EntityTexture pfog2, pfogtex EntityTexture pfog3, pfogtex EntityTexture pfog4, pfogtex EntityTexture pfog5, pfogtex EntityTexture pfog6, pfogtex EntityTexture pfog7, pfogtex EntityTexture pfog8, pfogtex EntityTexture pfog9, pfogtex EntityTexture pfog10, pfogtex ; since the alphas tend to add up we try to keep them change at each dispance the same ; finally reaching opaque at the tenth box EntityAlpha pfog1,.10 EntityAlpha pfog2,.10 EntityAlpha pfog3,.10 EntityAlpha pfog4,.10 EntityAlpha pfog5,.10 EntityAlpha pfog6,.10 EntityAlpha pfog7,.10 EntityAlpha pfog8,.10 EntityAlpha pfog9,.10 EntityAlpha pfog10,.10 ; scale the spheres to cover the range we need ScaleEntity pfog1,15,1,1 ScaleEntity pfog2,15,5,5 ScaleEntity pfog3,15,10,10 ScaleEntity pfog4,15,15,15 ScaleEntity pfog5,20,20,20 ScaleEntity pfog6,25,25,25 ScaleEntity pfog7,30,30,30 ScaleEntity pfog8,35,35,35 ScaleEntity pfog9,45,45,45 ScaleEntity pfog10,55,55,55 The fog texture is just a 256x256 BMP file of solid dark blue. Yea I know I could have used a type or array, but this is so simple I don't see anyone having trouble with it. I started with boxes to try and keep poly count down, but it looked bad, so I switched to spheres. If you dont want eveything shaded by the first sphere either rescale it or just do away with it. Also, to get varios effects of fogging its all about the scaling, I susgest in most cases that you keep the Y scale much tighter between layers than I do here. A full demo will be uploaded soon. |
| ||
Heres the hole thing so far, just comment out the texture loads or supply your own.; The Aquatic Demo, a demo to experiment with underwater effects. ; ; Would like to grow this into a mer based game, in this demo the goals are: ; ; 1) Creat a pleasing underwater experiance in sight, sound and movement ; 2) Support multiple player access to allow it to be shared ; 3) incorperate the 3D tile system based using a 2D tile system to layout the sea floor ; 4) for comparison, try alternate terrain builds using other tool. ; 5) experement heavily with strong lighting affects and LOD in relation to intensity and fog. ; 6) create models and animation sequances that match the movement to produce a smooth integrated feel. ; 7) Get pats on the back from my Blitz Pals. Uhh, huh, thats it yea. LOL ; ; ; Keeping it simple ; Where possible I will beg barrow and umm barrow as mutch from other peoples work ( with their permisions ) as possible ; I will also use the refinement process for development, that is, first get it all working, then make it look good and ; optimize as needed. ; Step 1 the Player type needed for the Network thingy Type pdata Field playerID ; The NetID of this player Field name$ ; the Name of this player shown over head. Field x# ; Now if Blitz supported Vectors this would be so much easier Field y# ; these are movement variable not positional ones Field z# Field wx# ; world postion These on the otherhand, are postional variables Field wy# Field wz# Field yaw# ; ya know this 3D world stuff is gona really take some experimenting to get these right Field pitch# Field roll# Field speed# ; yea see, its like this, things underwater do not instantly start and stop like in most FPSs. Field accell# ; so we need to know how fast things can speed up Field decell# ; and slow down Field mesh ; give you one guess what goes here, if you guess the mesh Entity handle, you'd be right. Field pivot ; used for movemement End Type Type timer Field timerID Field tdelay Field lasttime Field mode ; 0 off, 1 + number of times to triger, -1 allways on. Field triggered ; 1+ = number of times triggered, should be cleared to 0 when tested. max value will never exceed 32000 End Type ; So Im going to make some assumptions on screen size and modes here, I like windowed ; but if you one of those full screen types, go ahead, change it. ; Why globals, Well sure I can get this info from inside blitz, but to set the data this is how I like to do it. ; no constants this time, its nice to be able to define defaults that can be changed. :) Global viewwidth = 800 Global viewheight = 600 Global viewmode = 2 Global viewcolors = 32 ;Never sure if this is needed or not, but I do it anyway Global ppivot Global pcammera Global pfog1 Global pfog2 Global pfog3 Global pfogtex Global plight1 Global plight2 Global surfacedownplane Global surfaceupplane Global seafloorplane Global surfacedowntex Global surfaceuptex Global seafloortex ;Timers? We don't need no stinking timers! Well maybe just a few ; Global fratetimer ; this will hold the frame rate timer handel Global frdelay = 1000/60 ; this is sort of 60FPS adjust to taste Global gamestatus ; used to track the return status codes from functions ; Main Loops should be very clean, no uneeded logic, no variables that are not predefined above ; easy and clean. ;============= MAIN GAME LOOP ================= gamestatus = Init_Game() ; make a non-stop timer for FPS and game controle If gamestatus = 1 Then RuntimeError ("Could not INITIALIZE GRAPHICSMODE") If gamestatus = 2 Then RuntimeError ("PDATA type does not exist, not created") Init_World("demo") ; Load the game world sceen data, we make the basice in init_game, this loads ; models for details in terrain and sturctures. fratetimer = Init_Timer( frdelay, -1 ) While Not KeyHit(1) If TimerCheck(fratetimer) Then InputUpdate() ;NetHandler() CollisionHandler() MovementUpdate() RenderUpdate() ;NetUpdate() EndIf ;AIUpdate() Wend End ;============= END MAIN GAME LOOP ============ ; you will find Im function crazy, I think a game or any code should have a main section of almost purely function ; calls and the main game loop and thats it. ; Setup all the pre demo stuff and screen modes Function Init_Game%() AppTitle " The Merfolk Demo " If Not GfxMode3DExists(viewwidth, viewheight, viewcolors) Then Return (1) Graphics3D viewwidth, viewheight, viewcolors, viewmode SetBuffer BackBuffer() ;creat the first pdata so we can load up the player info p.pdata = New pdata If p = Null Then Return (2) ; ok lets set up the player pivot parent and camera ( going FP here ) p\pivot = CreatePivot () pcammera = CreateCamera(p\pivot) ;light? Yea the spot light its so handing to see things. ; but Blitz is do kind to give us a default one we simply need to change the color ; nice semi-dark blue for now CameraFogMode pcammera,1 CameraFogColor pcammera, 0,0,100 CameraFogRange pcammera, 1,55 ; about 100 meters ; Now we all know that the cammera fog is not Volumetric so we will emmulate this with a few large boxes. ; how this works is they are centered on us, and we flip them so we can see the texture on them from the inside. ; we then set the alpha levels from low to high as they radiate out from us. We use 10 such bozes with a .10 alpha ; each, the first box in placed right in front of the cammera, so everything is shaded 10% by that one, the others ; stretch out from there till 100% opacity is reached at max range. We can increase viewing range by simple ; decreasing the alpha as needed. Above water is would be reasonable to all layers to 0 opacity, or for those fogy ; surfuce days, the first 3 or so to 0 and the rest to propper levels for the fog desired. ;pfog1 = CreateCube(p\pivot) ;pfog2 = CreateCube(p\pivot) ;pfog3 = CreateCube(p\pivot) ;pfog4 = CreateCube(p\pivot) ;pfog5 = CreateCube(p\pivot) ;pfog6 = CreateCube(p\pivot) ;pfog7 = CreateCube(p\pivot) ;pfog8 = CreateCube(p\pivot) ;pfog9 = CreateCube(p\pivot) ;pfog10 = CreateCube(p\pivot) pfog1 = CreateSphere(8,p\pivot) pfog2 = CreateSphere(8,p\pivot) pfog3 = CreateSphere(8,p\pivot) pfog4 = CreateSphere(8,p\pivot) pfog5 = CreateSphere(8,p\pivot) pfog6 = CreateSphere(8,p\pivot) pfog7 = CreateSphere(8,p\pivot) pfog8 = CreateSphere(8,p\pivot) pfog9 = CreateSphere(8,p\pivot) pfog10 = CreateSphere(8,p\pivot) FlipMesh pfog1 FlipMesh pfog2 FlipMesh pfog3 FlipMesh pfog4 FlipMesh pfog5 FlipMesh pfog6 FlipMesh pfog7 FlipMesh pfog8 FlipMesh pfog9 FlipMesh pfog10 pfogtex = LoadTexture ("textures\fog.bmp") EntityTexture pfog1, pfogtex EntityTexture pfog2, pfogtex EntityTexture pfog3, pfogtex EntityTexture pfog4, pfogtex EntityTexture pfog5, pfogtex EntityTexture pfog6, pfogtex EntityTexture pfog7, pfogtex EntityTexture pfog8, pfogtex EntityTexture pfog9, pfogtex EntityTexture pfog10, pfogtex ; since the alphas tend to add up we try to keep them change at each dispance the same ; finally reaching opaque at the tenth box EntityAlpha pfog1,.10 EntityAlpha pfog2,.10 EntityAlpha pfog3,.10 EntityAlpha pfog4,.10 EntityAlpha pfog5,.10 EntityAlpha pfog6,.10 EntityAlpha pfog7,.10 EntityAlpha pfog8,.10 EntityAlpha pfog9,.10 EntityAlpha pfog10,.10 ; scale the boxes to cover the range we need ScaleEntity pfog1,15,1,1 ScaleEntity pfog2,15,5,5 ScaleEntity pfog3,15,10,10 ScaleEntity pfog4,15,15,15 ScaleEntity pfog5,20,20,20 ScaleEntity pfog6,25,25,25 ScaleEntity pfog7,30,30,30 ScaleEntity pfog8,35,35,35 ScaleEntity pfog9,45,45,45 ScaleEntity pfog10,55,55,55 ; more light out there in front of us for that cool distant glow feel ; Thats right our water will be a magical happy glowing kind of place ; not very realistic but hey we are merfolk, we can do this kind of stuff. :) plight1 = CreateLight(2,p\pivot) LightColor plight1, 0,100,100 LightRange plight1, 150 PositionEntity plight1, 0,20,200 ; and some more light, placed at point of view plight2 = CreateLight(3,p\pivot) LightColor plight2, 0,50,50 LightRange plight2, 200.0 ; Ok we need the Sea, well 3 planes, yea we will make a sky orb and use it when you get close to surface ; The top plane is the furface of the water as seen from above the water, hey we want to jump out of water too. ; this one is less transparant than the one from looking up. surfacedownplane = CreatePlane() PositionEntity surfacedownplane, 0,0.01, 0 ; minor adjustment over plane below it. surfacedowntex = LoadTexture("textures\water.bmp") EntityAlpha surfacedownplane,.75 EntityTexture surfacedownplane, surfacedowntex ; The next one down is the bottom a mostly transparant( hey isn't that translucent? ) plane that we can see the ; sky throuh. Also know that Fog is turned off when we are less than 100 meters from the surface, well in reality ; the closer we get the further the max range of the fog raises so its still there, when we are very need the surface ; it will extend well beyond the skysphere and thus appear non-present. Ahh, you ask what about if you look down while ; near the surface? Well silly, since your location in the sphere is very near the upper side of it, the fog will ; still be blocking the extreams of the toward the hurizon and bellow the surface, Genius I know, but as a friend ; said, why not just use a 5 sided sky box centered on the character and placed above the water? To witch I said, "umm". ;surfaceupplane = CreatePlane() ;PositionEntity surfaceupplane, 0,0,0 ;surfaceuptex = LoadTexture("texture\water.bmp") ; in future will be using 2 sepereate texture for up/down ;EntityAlpha surfaceupplane, .30 ;FlipMesh surfaceupplane ; of coarse we cant flip planes, what was I thinking, no big deal will simply make a surface mesh, that way I can ; animate it with waves and stuff anyway. So there. ; Finally the sea floor, yea, its a big flat sea with a sandy texture, dont like it? Me either so will be adding ; stuff down there real soon. seafloorplane = CreatePlane() PositionEntity seafloorplane, 0, -300, 0 seafloortex = LoadTexture("textures\seafloor.bmp") EntityTexture seafloorplane, seafloortex ;initialize the skybox, using my friend idea, see also move_skybox ;init_skybox() ;Set the starting info ; sence we don't have spawn points in our wourld yet we get the good ole 0,250,0 p\wx# = 0.0 p\wy# = -299.0 ;we starting out at close to 300 meters down, hows that For working under preasure? p\wz# = 0.0 p\yaw# = 0.0 ;p\mesh = LoadAnimMesh("merperson1.3ds", ppivot); Hey when I have a model I get to uncomment this. :) ; position the player in starting location. PositionEntity p\pivot, p\wx, p\wy, p\wz ;PositionEntity pcammera, p\wx, p\wy, p\wz ;PositionEntity pfog1, p\wx, p\wy, p\wz ;PositionEntity pfog2, p\wx, p\wy, p\wz ;PositionEntity pfog3, p\wx, p\wy, p\wz ;PositionEntity pfog4, p\wx, p\wy, p\wz ;PositionEntity pfog5, p\wx, p\wy, p\wz ;PositionEntity pfog6, p\wx, p\wy, p\wz ;PositionEntity pfog7, p\wx, p\wy, p\wz ;PositionEntity pfog8, p\wx, p\wy, p\wz ;PositionEntity pfog9, p\wx, p\wy, p\wz ;PositionEntity pfog10, p\wx, p\wy, p\wz Return (0) ; if not true our function was completed without a problem End Function ;setup collisions Function Init_Collisions() End Function ; Handle collisions Function CollisionHandler() End Function ; get input from player Function InputUpdate() p.pdata = First pdata p\yaw# = 0 ; lets make sure we only move if a key is down p\pitch# = 0; p\roll# = 0; p\z# = 0; p\y# = 0; p\x# = 0; If KeyDown( 203 ) Then p\yaw#=+1 If seq <> jump Then new_seq = idle End If moving=True EndIf If KeyDown( 205 ) Then p\yaw#=-1 If seq <> jump Then new_seq = idle End If moving=True EndIf If KeyDown( 208 ) Then p\z#=-0.4 If seq <> jump Then new_seq = walk End If moving=True EndIf If KeyDown( 200 ) Then p\z#=+0.4 If seq <> jump Then new_seq = walk End If moving=True EndIf If KeyDown( 57 ) Then p\y# =+0.4 EndIf End Function ; Basicly set up BlitzPlay the totally awesome network lib for Blitz. Woot BP rocks ; Mode is either Host 0 or client 1 ; port is the port to listen/connect on ; players is the max number of players ; ServerIP is the IP address of the game server(host) defaults to localhost at 128.0.0.1 ; return status codes 0 if all went ok. Function Network_Init%( mode%, port, players%, ServerIP$ = "128.0.0.1" ) Return (0) End Function ; update the network, basicly sends network data and recieves it from network ; while I use BlitzPlay for this, I use my own wrappers so the netcode ; stays independant of the game, incase I want to use something else later. ; returns 0 if all went as planed or errorcode otherwise. Function NetUpdate%() Return (0) End Function ;The heart of the network portion of the demo/game ; This function handles all incomming messages and sorts/stores/posts the data to the right variables used ; by other functions like ChatUpdate, MovementUpdate and so forth. Function NetHandler%() Return (0) End Function ; Called after input and NetHandler this function updates all movable objects in the world space ; basicly cycles through the pdata's and other similar types and adjust there postion to speed ratio ; based on the timer datas for each object. This will position all players in the game/demo Function MovementUpdate() For p.pdata = Each pdata TurnEntity p\pivot, p\pitch#, p\yaw#, p\roll# MoveEntity p\pivot, p\x#, p\y#, p\z# p\wx# = EntityX(p\pivot) p\wy# = EntityY(p\pivot) p\wz# = EntityZ(p\pivot) Next End Function ; This function calles all the updateworld and render functions, also handles the HUD information Function RenderUpdate() UpdateWorld RenderWorld Draw2DHUD() Flip End Function ;Create a new timer useing the Delay provided ; the mode is -1 non-stop, 1+ count down timer, 0 stopped( usually cause countdown has happened ) ; use countdown for cases where you want effects to last for a specific time but dont need to keep ; updating the effect after that. powerup durations for example durations for example. ; return the timer id from the timer type created Function Init_Timer%(timeDelay%, mode ="-1") utimer = 0 For t.timer = Each timer If t <> Null Then tiemid = t\timerID If timerid > utimer Then utimer = timerid ; lets get the highest timer ID so far End If Next utimer = utimer + 1; lets make this ID one greater than the highest ID so far and thus unique t.timer = New timer t\timerID = utimer t\tdelay = timeDelay t\lasttime = MilliSecs () t\triggered = 0 Return t\timerID End Function ;Checks the a given timer( by ID ) to see if it has triggered, clearmode 0 (default) will clear the timer if trigger and ; if so return the number of times it triggerd since last check. Setting the clearmode to none zero will ; simply return the timer triggered data and not clear it, regardless of if it triggered or not. ; the default is to clear the trigger data if triggered. Function TimerCheck%(timerID%, clearmode = 0) For t.timer = Each timer If tiemrID% = t\timerID Then If t\triggered Then ttemp = t\triggered If clearmode = 0 Then t\triggered = 0 t\lasttime = MilliSecs() Return (ttemp) End If End If Next Return (-101) ;Invalid trigerID End Function ; loop through all timers and update there triggered stats Function TimerUpdate() For t.timer = Each timer If t\mode > -1 Then ; is it non-stop or other? If t\lasttime + t\tdelay > MilliSecs() Then ; did the time expire? t\triggered = t\triggered + 1 ; yep, so increment triggered t\lasttime = MilliSecs() ; reset timer. If t\mode > 1 Then ; its a count down timer amd it hasnt counted down yet? t\mode = t\mode -1 ; yep, so count down If t\mode < 0 Then t\mode = 0 ; prevent it from any chance of crossing over into a non-stop timer End If End If Else ; so its a non-stop timer If t\lasttime + t\tdelay > MilliSecs() Then ; did the time expire? t\triggered = t\triggered + 1 ; yep, so increment triggered t\lasttime = MilliSecs() ; reset timer. End If End If Next End Function ; TimerResetMode lets you change the mode or count of a timer, so that for instance you can restart a stopped ; countdown timer. It does no other testing, so if you should use TimerCheck before a reset if you are interested ; in the timer status before the reset. Function TimerResetMode( id%, modecount% ) For t.timer = Each timer If id = t\timerID Then t\mode = modecount Return End If Next End Function ; Reset the delay setting of a timer, used for fine tuning a timer at run time, like when else silly? ; ID is the timer ID and tdelay is the delay for the timer. It does no other timer opperation, see also TimerResetMode Function TimerResetDelay( id% , tdelay% ) For t.timer = Each timer If id = t\timerID Then t\tdelay = tdelay Return EndIf Next End Function ; this handles the zone specific data for this area of the world, it loads all models and terrains ; repositions player to the propper zone entry point in relations to players present zone settings ; sets all lighting and rendering options and updates the network to let other players know that this ; player has left one zone and entered the one specified, including the new players starting location. ; of coarse in the Demo there is only one zone, but I figured what they heck, better to plan ahead. ; The data read comes from the zone file, named __zonename__.zon. These files are located in the zones directory ; there format???? well to be done still. But probably very INI in format. Function Init_World( zone$ ) zonefile$ = "zones\"+ zone$ +".zon" ; this is just a referance cube set out there in the sea cube = CreateCube () ScaleEntity cube ,5,5,5 PositionEntity cube,0,-295,20 cube1 = CreateCube() ScaleEntity cube1, 3,3,3 PositionEntity cube1, 3,-250,30 End Function ; draws some debugging data to screen. Will be using surfaces in 3D later, but for now this will do. ; called by RenderUpdate Function draw2DHUD() linenum = 0 For p.pdata = Each pdata linenum = linenume+1 Text 10,10+(FontHeight()*linenum),"Player: "+p\name+" at location: x:"+p\wx# +" y:"+p\wy# +"z:"+p\wz# Next End Function As for spelling, me? never happens. |
| ||
I like your commenting style. Come work for me and become a millionaire. |
| ||
I love comments, I beleive its as important to explain to yourself the concepts as well as the usage. Makes it so much easier to come back too later. :) Hmm well as I read over it, I can see where maybe a little spelling might help here and there, but the code works. Yep it does. |
| ||
just a small sidenote: witch I always took for granted in OpenGL with Gui and Glut I guess Blitz3D uses DirectX7 not OpenGL |
| ||
Really? lol I knew that, but OpenGL had these capabilities years before DirectX even existed. Thats why I was suprised to see it missing in the 7th version of a API that is itself the 2nd API from Microsoft targeted at graphics for windows. :/ Im fairly sure most modern graphics cards( ie. those since like 97 ) can do this in hardware now. Just the API seems to be lacking anyway to access it. Well when BMax hits and the 3D module is ready all this will be mudy water under the bridge. Normally I would have done the Apply a Alpha Mask to the zBuffer thing, but didn't see any clear or easy way to do this. That is, that would have been as fast as the technique above. Granted it would look alot smoother, but all that bit handeling would be SO SLOOOOOOOW. |
| ||
Made several updates to this, a newer version is in the code archives here. Working on turning the fog into a more controlable function for easy inclusion in other projects. Will end up with the ability to alter the fog in real time. goals: -Allow for color change on the fly -Easy range control ( one function to set min and max range ) -Layer control to set how many layers and at what opacity for each layer -SkySphere switching to turn a given layer into a skysphere with entityorder set and sky texture applied. -Seperate texturing option for each layer -Use types to contol layers -single command to initialize a simple fog system -Function call to manipulate all aspects of the fog. |