I found a nice 3D museum model online, so I decided to post some screenshots of how it looks rendered in 3DWorld. The model was taken from Challenge #17 from the challenge page on 3DRender.com. Note that I didn't actually participate in this challenge. I also spent little time preparing. I don't actually have any real image editing or 3D modeling software, and I wanted to render this in realtime, so it's not the highest quality rendering and there are no fancy special effects. However, I still think it looks good.
This museum scene contains around 750K triangles. 3DWorld can handle scenes up to around 12M triangles, so I could have 16 of these museums in my scene and still draw it in realtime. The original file was untextured, with no normals or texture coordinates. I had to use textures that I found online and create the normal maps using a trial version of CrazyBump. The textures are applied using standard {X, Y, Z} triplanar texturing where the texture coordinates are determined by the dominant direction of the normal. It's difficult to generate automatic texture coordinates for scenes with curved geometry without using a 3rd party tool. Vertex normals are generated from the face normals by 3DWorld. Some of the triangle faces in the model have the wrong clockwise vs. CCW winding order, which causes black spots and smeared textures, but it's not too noticeable.
There are several light sources in this scene:
Sun direct and indirect lighting with shadows
Sky/cloud indirect lighting
Local diffuse + indirect indoor light sources (2 yellow lanterns and 4 blue ceiling lights)
There is a one time lighting precomputation of around 10 min., and after that the scene can be drawn at around 100 FPS (frames per second). It takes 10 seconds or so to update the sun indirect lighting component when the sun position is changed. Here are some screenshots of the museum scene.
Museum scene from the front stairs.
Museum scene from the ground floor in the back.
Museum scene from above near the ceiling.
This is a fully collision and physics enabled scene in 3DWorld. The player can walk around and collide with the geometry, shoot things, and play in game mode against the smiley AIs. There are a few holes in the floor that the player can fall though, but I think I mostly fixed those problems.
That's it for now. I'll write something longer next week when I have more time.
It's been awhile since my last post, but I haven't really been working on much that I can show in screenshots and videos. Most of the effort has been spent on continued improvements to movable object physics and related tasks. I had it working well enough to show some videos last month, but it took a lot more effort to make the system stable and fix all of the strange bugs, including the crazy large water splash when underwater boxes were destroyed. I'm working on object rotational physics and that sort of thing now, but it's difficult to get right, and I don't have anything to show yet. I may end up using a third party physics library such as Bullet for the more complex effects.
I also spent a few hours improving the 3DWorld leaf wind effect and making it work on more vegetation types, so I'll make this the topic of today's post. This post won't be as long as the previous one, which hopefully means that I'll have time to make another post soon. I already had a leaf wind system for trees that was entirely on the CPU, and it's always bothered me that this method is slow and I find myself disabling it at times to improve the frame rate. In 2015 everyone seems to be doing leaf wind on the GPU rather than the CPU, so I decided to try this approach. I've actually been thinking about and experimenting with GPU wind for years, but I was never able to figure out how to update the leaf normals for lighting as the leaf moves in the wind. The problem is that the vertex shader can normally only see one vertex at a time, and it can't determine the orientation (rotation axis and angle) of the leaf from a single vertex. The leaf needs to bend/rotate at the point where it's connected to the tree branch, a point that the vertex shader doesn't know. Sure, I could pass this extra info as additional vertex data, but that takes more GPU memory and hurts the frame rate. I could use a geometry shader, but that turned out to be very complex and also slow.
A few weeks ago I read an online article on leaf wind. The author ran into the same problem with updating the leaf normals, but he went ahead and implemented it anyway with the normals and lighting remaining constant. He claimed that it looked just fine this way - and I agreed! So all this time I had been worried about what was really a minor issue. The solution: do something simple per-leaf that doesn't need to know about the leaf orientation and just use the original normals. I used a simple time-varying sum of two sine waves and that looked pretty good. Even with fixed normals, the lighting can still be somewhat dynamic. Shadows and indirect lighting are computed per pixel, and don't depend (much) on the normal, so these components of the lighting will still change dynamically as the leaves move in the wind.
I still had to deal with the leaf attachment point to the branch. The leaf tip should move, but not the base part where the stem attaches to the tree branch. How can the shader know which vertex is at the tip vs. the base? It can use the texture coordinate, which is 0.0 and the base and 1.0 at the tip. This data is already available since it's used to texture the leaf. The next problem is how to keep the leaf size constant by moving both vertices of the tip (the two vertices that form the far edge of the leaf quad/rectangle near the tip). I solved this by using the world space coordinates of the leaf to determine the magnitude of the wind displacement. Since leaves are small, the coordinate values of their tip vertices are close together, making their wind displacement values similar, therefore keeping the distance between those points relatively constant.
This system worked so well that I ended up using it for the small branches and leaves/needles of pine trees, which previously had no wind effects. Then I enabled it for plant leaves. I was never quite able to get the wind effect to work for the berries on some plant types, so I simply disabled this effect on those plants. Grass was already using a similar wind system, so I left that code alone. [Grass is easier since each blade is drawn as a triangle, where only the tip vertex moves, and it's a lot simpler to animate a single vertex rather than an edge formed from two vertices for leaves. Also, grass blades tend to have a more uniform length and orientation, so the vertex shader can guess at the positions of the other two base vertices.]
Here is a screenshot of some vegetation moving in the wind. Oh, right, you can't actually see it moving. Well, it's still a pretty screenshot.
Trees, plants, and grass moving in the wind. This is just a high resolution image from the video below.
Here is a video of the same scene. You can see the tree leaves, plant leaves, flowers, and grass blades all moving in the wind. The wind speed and direction can be interactively changed with hotkeys and an onscreen slider UI widget (not shown). The video starts with a small amount of wind and gradually increases wind intensity to an extreme value. I had to choose a strange camera angle in the corner of the scene to get the pine trees, deciduous trees, plants, and grass all in the same view.
Here is another video showing that wind works on all vegetation in infinite tiled terrain mode as well. This scene with it's high detail trees ran at only ~50 FPS (frames per second) with CPU wind, but runs at ~90 FPS with GPU wind. In fact, the wind computation time has almost no effect on frame rate. In addition, wind is applied even to distant trees, rather than being limited to only nearby trees in the old mode. It may be hard to see the wind effect in this video, especially since I couldn't stand still and had to run around at high speed just to show off that it was possible. The max speed in tiled terrain mode is very high and when the player is on the ground everything is a blur. The minor lag near the end when I went in the water is probably due to loading the water sound.
Right, back to the wind. I'll note that the new wind system doesn't completely replace the old mode. I still need CPU mode for player interaction with leaves. For example, player collision with trees moves the leaves, leaves can be shot off using projectile weapons, etc. Oh, all right, I'll add a video of that too. I guess every post must now contain at least one video of me shooting or otherwise destroying something. Keep in mind that the particle effects are only a placeholder for better particle effects that I'll likely show off in some future post.
Did you notice that I added new sounds such as the "click" when switching weapons? I'll try to continue to add more sounds and other effects as time allows.
That's about all I have to say about leaf wind and related topics. Next time I'll probably make a post about full-screen postprocessing effects, unless something else interesting comes up. I was considering making a post about snow accumulation and rendering, but I think I'll save that for winter time - not that we actually get snow here in CA.
This time I'm not going to talk about graphics, at least not very much. I'm going to talk primarily about physics - in particular, the new movable objects I have been working on over the past few weeks. This post is going to be less technical than usual, since I'm not going to get into the details of how I implemented object physics. I'm just going to explain what the system can do and show lots of short videos. It's just too hard to capture physics in screenshots.
But, for those of you who like to see pretty screenshots, here is the latest version of my trees in tiled terrain mode. They look a bit different from the last tree post a few months back, and in my opinion they look better. I increased the lengths of the smallest level branches so that the leaves are more spread out, rather than being clustered together.
The latest version of tile terrain mode trees, now with more leaf detail.
Okay, back to movable object physics. Movable objects are a new type of physics object in 3DWorld that are somewhere between standard static objects and dynamic objects. They're really pretty close to the way platforms (such as elevators, crushers, and moving doors) are implemented. Here are the basic types of objects supported by 3DWorld:
Static scene objects. Most objects are of this type, since they are the most efficient to process: No dynamic updates, no physics, can use a static draw buffer, precomputed lighting, etc.
Destroyable objects: Static objects that don't move but can be destroyed (removed)
Platform objects: Move along a predetermined path when activated, no physics, some precomputation is still possible.
Movable objects: Movement controlled by the player, AI, gravity, and other movable and platform objects. Movement is generally sparse/rare so physics is only applied when needed, but can't precompute anything.
Dynamic objects: Objects that typically have a limited lifetime and are assumed to move each frame (players, weapons, projectiles, pickup items, fragments, particles, etc.) Limited to collision spheres only for simple and efficient collision/physics.
Movable objects are more general than dynamic objects since they can have any standard collision shape, but they are less efficient. 3DWorld can efficiently handle thousands of dynamic objects but probably only a few hundred to a thousand movable objects.
The supported basic primitive shapes for movable objects are {cube, sphere, cylinder, cone, truncated cone, capsule, polygon, ramp, and extruded (cookie cutter) polygon}. Yes, I have implemented exact collision detection between all of these shape classes, some 30 functions in total. It's not entirely complete and stable for all combinations, but it works well enough. This has some advantages over the standard polygon meshes used in most games:
More efficient (an analytical sphere is much faster than one formed from a thousand triangles)
Curved surfaces can be rendered at near pixel accuracy using a smooth LOD transition
Seamless texture coordinates can be auto generated with simple code in most cases
Collision can be exact, and generally involves solving some equations rather than iteration
Objects have true volume, which means their mass can be calculated for physics
Objects always form a closed surface and inside vs. outside is clear (except for polygons)
I have implemented quite a few "effects" where movable objects interact with the rest of the world in interesting ways. Most of these can be classified as physics or collision effects. They have been implemented using object-object collision detection and almost nothing else that must be special cased on the object shape type.
One of the important effects I wanted to have was stacking. Movable objects can be stacked on top of each other and the whole stack moved around. Stacks can be created statically in the scene object file, or dynamically by dropping objects off ledges onto each other. Stacks can also be pushed over by standing on something to get at the top object on the stack. I also added horizontal stacking (like a train of objects) after recording the videos so you won't see them in action yet.
The remainder of this blog post consists of 11 short videos of movable objects in action. I feel that this topic is much easier to demonstrate using video rather than static images and walls of text like most of my previous posts. Since the free version of Fraps limits video recording to 10 seconds, none of them are longer than that, which is why I had to create so many. Keep in mind that there was no special setup for making these videos, I could have done all of these events in one 3DWorld run. Some of the videos have sound, in particular the ones involving weapons. I really need to add more sound effects for pushing, falling, colliding, etc. [Update: I did add more sounds! But I'm not going to re-record all of these videos.]
Ramp 1
This video demonstrates sliding a stack of two wooden crates (cubes) up and down a ramp and over the grass. Note how the crates crush the grass in their path. In addition, it shows how a sphere and a cylinder can be pushed over the edge of the wall. If you look closely, the sphere smoothly rolls over the wall (though the texture doesn't move to show that it's rolling).
Ramp 2
The previous ramp video was truncated at the 10s mark, so I had to split it into two parts. This time I'm sliding a crate up a ramp that's also a movable object. When I push them together, both the crate and ramp slide on the ground, until the ramp hits the wall of the fountain and stops. At this point, I can finish pushing the crate up the ramp and into the water. Then I push a wooden barrel (horizontal cylinder) up the ramp and into the water. The "boing" sound is the placeholder sound for player jumping.
Elevator 1
Here I push a stack of two metal boxes (Borg cubes!) and a brick cube into the elevator, which goes up and then down. On the way down I push the metal cubes off at one floor and then push the bricks off the back of the elevator.
Elevator 2
The first elevator video doesn't use real physics. Can you spot the error? The cubes were directly controlled by the elevator's velocity, which is a hack. In the next video I have fixed the problem so that the cubes are in free fall when the elevator is going down. The video starts with the elevator stopped on the top floor. You'll notice a small jump in each cube when the elevator starts its descent - what is that? The jumps are due to the unrealistic constant velocity of the elevator. When it starts moving at a fixed velocity, it has an infinite downward acceleration. Since the cubes fall due to true gravity, they can't accelerate to the elevator's velocity instantly. Instead, they accelerate due to gravity under free fall from rest and only collide with the elevator when they exceed its velocity and catch up. The bottom cube has to catch up to the elevator, and the top cube has to catch up to the bottom one. After that, the cubes descend with the elevator at a constant velocity. I'm not entirely sure it's correct now, but it looks plausible for an elevator with infinite acceleration.
Falling Bricks
This video demonstrates falling object and stacked object physics. It starts with a brick cube resting on a wooden board. I shoot the board with the M16, shattering it, and the bricks fall to the ground below. Then I accidentally fall off the edge when trying to look at the bricks and land with a bloody splat. Also note the shell casings and the bullet holes that stay on the bricks as they fall. Since bullet hole decals are independent world entities, it took a bit of extra trickery to bind them to a known position on the object so that they track the object's position as it falls. Oh, and yes, I finally gave the M16 some recoil, which is why I keep falling off of things when I use it.
Ball Push
Here I push a rolling ball with a glass cube, then push it off the edge with a movable wooden ramp. The ball is a dynamic object, rather than a movable static object, so the rolling physics/texture rotation works with it. See how the glass cube isn't a uniform transparency? The glass is modeled as a scattering medium with real optical thickness, where each pixel's ray is intersected with the cube and the light attenuation is accumulated along the ray's optical path through the cube. Windows work the same way.
Shadows
In this video you can see that the local light source shadows are updated as the boxes are pushed on the floor. The shadows are only recreated when a dynamic object moves, to save CPU and GPU time. Technically, the movable boxes aren't dynamic objects, so they don't trigger the shadow updates themselves. But, the player is a dynamic object, and moving around near the light will trigger shadow updates. Since only the player can move objects (currently), everything should work well enough.
Indirect Lighting
3DWorld uses precomputed indirect lighting stored in a 3D volume texture. This allows objects that weren't present during lighting computation to still receive light by looking up the lighting texture data in the shader. However, the objects can't actually modify the indirect lighting in an efficient way. This video shows a white cube being pushed around through various lighting conditions, where indirect lighting dominates. The transitions from lit to dark areas are smooth. The hallway in the middle of the video has motion activated blue lights in the ceiling that contribute a direct lighting component as well.
Destroyable Crate
Here is an example of how nearby explosions can destroy moving objects. I push a wooden crate next to an exploding tank, then shoot the tank, causing it to explode and destroy the crate in the process. The shattering glass sound is from one of the lights on the ceiling that is also destroyed.
Floating 1
Physics is fun, especially when it includes water! At least I think so. I have implemented physically-based buoyancy in 3DWorld. Objects with density < 1.0 float, and objects with density > 1.0 sink. Float height and sink rate are functions of density. The video starts with the lake water in the corner of the office building scene frozen as ice, where I (the player) am standing on the
ice and all of the objects are resting on the ice. When I increase the temperature using a hotkey, the ice immediately
melts and I fall along with the other objects. Some of them float, and
some of them sink, depending on their density relative to water (1.0). The floating objects bob up and down with the waves on the surface of the water. The cube object materials are (from left to right in decreasing density):
Metal, density = 4.0 (typical metal density is ~2-8)
Marble, density = 2.8
Glass, density = 2.5
Brick, density = 2.0
Wood, density = 0.7 (wood density ranges anywhere from 0.4 to 0.9 so I picked a mid value)
Styrofoam, density = 0.2 (random guess)
A note on bricks: It seems odd to me that brick has only twice the density of water. I would have thought that bricks are heavier than that, but I guess not. Here is a fun experiment - try picking up a brick that's out of the water, then submerge it in water and try picking it up again. Does it feel like half the weight when it's in water? Maybe the next time you need to move a stack of bricks, you should flood the area first and it will be half the work. Of course then you'll spend extra energy moving yourself through the water, so it might not help that much:)
Floating 2
I forgot to enable the underwater bubbles in the first video, and I didn't want to have to move all of the objects into their correct place and redo the floating/sinking video. So I quickly put them in less orderly positions and recorded a new video that includes bubbles. This time I stacked the Styrofoam onto the bricks so that they separate in the water. If the bricks were on top they would both float, which may not be correct, but the water system treats each object independently and the bricks are technically out of the water. Also note that the underwater effect includes a full screen blur postprocessing pass, which is why the image looks a bit fuzzy.
Teleporter + Fall
This is my favorite video. I push a crate through the lobby teleporter onto the roof, then go through the teleporter myself. I push the crate over a skylight, stand on top, fire the shotgun at the skylight until the glass breaks, then the crate with me on top falls four stories back to the lobby level. The falling damage is nearly enough to kill me, but I survive and get to see the crate with shotgun shell casings sitting on the floor. I think the fall would normally be fatal but for some reason or other the crate reduces the damage. Don't try this at home!
Bonus: Water Fun
Did I say 11 videos? Well, here is #12. It was so much fun creating these videos that I decided to create one that mixed water, destruction, new sounds, and drowning. Here I have a metal box resting on top of a wooden crate on the ice. I push a large wood plank down the stairs and out onto the ice. Then I jump onto it and melt the ice into water. You can see that the metal box floats on top of the crate on the surface of the water. Then I shoot the crate out from under the box with the M16, causing the box to fall into the water. The crate explosion creates a huge wave that's probably a bit excessive, but that's okay. Then I shoot out the plank from under me and fall into the water, taking some damage from the sharp floating wood fragments. Finally, I destroy the metal box, fire some more M16 rounds under the water for good measure (and to show off the bubbles), then drown. That was fun!
Yes, this post is long. But the time taken for you to read it is small compared to the time I took to write it ... which is small compared to the time it took me to implement all of this ... which is small compared to the time it took me to write the thousands of lines of those ~30 primitive object intersection functions. Sure, I could have probably found the code for some of it online - I tried - but it seems like no one can get that sort of code stable and correct in all cases. So many opportunities for divide-by-zeros, so many places where epsilon tolerances are needed. Maybe I should open source that code some day.
Maybe I'll add more videos later. Keep looking for them.
Update:I added water buoyancy physics for two stacked objects that uses
their combined mass, the displacement volume of the bottom object, and
the water level of the upper object. I'm not sure it's physically
correct, but it looks right. A metal box on a styrofoam block now sinks
more slowly than the metal by itself. A wooden block on styrofoam floats
low in the water. Bricks on styrofoam sink until the bricks go below
the water, then sit there with the buoyancy of the styrofoam keeping the
bricks from sinking completely. Wood on bricks sinks faster than the
bricks alone until the wood goes under the water, at which point they
separate and the bricks sink more slowly while the wood floats. I haven't had a chance to make a video of this yet, but I'm sure you can guess what it would look like.
Awhile back I wrote a system to create and render a volumetric nebula for universe mode. Later I realized that a modified version of that effect also made a good debris/dust cloud for ship explosions. Then I used a more colorful version for teleporter graphics in ground gameplay mode. This week I decided to try this approach for rendering volumetric puffy clouds in infinite tiled terrain mode, and it works! Well, almost, they don't quite look like "normal" clouds from a distance but they look great up close. Plus, the player can fly through them.
Before I show you some pretty screenshots, I need to get some technical details out of the way. I'll try not to make it too bad for the nontechnical readers.
The volume cloud system I'm using isn't actually something I thought up myself. I saw it explained in a video that I found on YouTube through a Google search a few years ago. Unfortunately, I can't seem to find that video again, so I can't link it here. The video described how to create a nebula out of a number of randomly oriented 2D slices containing partially transparent plasma textures created in Photoshop. Since I needed one unique nebula per galaxy, in a game with an infinite number of galaxies, I had to do something a bit different. Instead of manually creating an infinite number of textures in Photoshop (which I don't own), 3DWorld generates the color and alpha (transparency) values directly on the GPU using random 3D noise textures to create ridged Perlin noise. The location of the nebula in space is the seed used to determine the starting and step direction/distance into the noise texture so that each nebula is unique.
Each particle cloud (nebula, etc.) consists of 13 2D quads (squares) that all intersect at their centers. Each quad can be viewed from either direction, so it's actually more like 26 2D billboards. The quad planes are oriented in uniformly distributed directions by taking all {x,y,z} values in {-1,0,1} and folding out the symmetric ones. This gives the following 13 directions before normalization: (1,0,0), (0,1,0), (0,0,1), (1,1,0), (1,0,1), (0,1,1), (-1,1,0), (-1,0,1), (0,-1,1), (1,1,1), (1,-1,-1), (-1,1,-1), (-1,-1,1).
The corners of the quads are attenuated to an alpha of zero (fully transparent) so that they appear as circles with a smooth falloff from opaque to transparent at their outer edges. In addition, quads that are nearly parallel to the view direction are faded to transparent to avoid ugly artifacts from looking at them edge-on. Then the color and alpha channels are modulated by the noise functions to give an interesting, cloud-like effect. The fragment shader evaluates the noise function at a position corresponding to the coordinate of each pixel on the quad in 3D world space. Therefore, the cloud looks correct and has proper parallax when the viewer/camera moves around and through it. That is, as long as the view doesn't pass directly through the center where the quads all intersect, which is a singularity where every quad is viewed edge-on and rendered invisible, causing the cloud to disappear. I guess that's what it's like to be in the eye of the storm.
Here are some of the fun things I can render using this system.
Nebulae
Yes, it can draw a nebula, what a surprise! This system was initially used for drawing nebulae in universe mode. I've shown this image in a previous blog post, and here it is again.
Closeup view of a procedurally generated nebula in universe mode. This was in a previous blog post.
A nebula is drawn using ridged noise (just Perlin noise with some extra math). Okay, if you're curious, here is the math that converts a Perlin noise value 'v' into ridged noise:
v = 2.0*v - 1.0; // map [0,1] range to [-1,1]
v = 1.0 - abs(v); // ridged noise
v = v*v; // square it
Yes, exciting, isn't it? The nebula shader uses two random noise color channels where the colors themselves are also randomly selected (I think it was pink and orange in the screenshot) + one alpha channel. The noise is mostly high frequency and the bounding shape is spherical.
Explosions
Who creates a space game without ship explosions? What is left after a big ship's explosion, anyway? A cloud of glowing dust and particles! This can be drawn with the same technique. Just use lower frequency noise to make it more irregular, make it even more ridged and wispy, use different colors for the interior vs. exterior of the volume, and you get this:
Volumetric debris and dust cloud from blue/white and red/orange ship explosions in universe mode.
Teleporters
That's enough particle clouds in universe mode. It looks good, but I don't want to overuse the effect. Where else can it be used? How about using it for the new teleporters in 3rd person shooter ("ground") mode? Since a teleporter doesn't exist in the real world I can make it look however I want. I decided to make it very bright so that it stands out, adds color to the dull grays of my office building scene, and looks like something that is definitely not natural. The colors and noise are animated as well so that it's clear to the player this thing isn't just a static decoration. It draws attention - this glowing object does something. Teleporters use 4 high frequency ridged Perlin noise channels in the shader: red, blue, orange, and alpha.
Closeup of a teleporter gameplay entity. The dynamic, animated, colored cloud casts light on the ground.
Here is a short video of the teleporter in action, where you can see the animated colored volume and how it actually moves the player to a different location in the scene. Note that teleporters don't just work with the player: they work with enemies, items, projectiles, particles, and any other type of dynamic game object. It's fun to throw grenades into the teleporter trying to hit someone you can't see on the other side. Just make sure you count to 5 before walking into it yourself. Sorry, I didn't record a video of this (yet).
Clouds
The obvious use for a volumetric particle cloud rendering system it for rendering ... clouds. There are a ton of competing ways to draw clouds in games. Sometimes clouds are meant to be viewed from below, for example when the player can walk or drive on the ground. Sometimes clouds are meant to be viewed from above or inside, for example in a flight simulator or space game. In 3DWorld, there's really no constraint on where the player can go. It's a game engine, not a game, so it could be used for a ground-based first person shooter or a flight simulator. These clouds need to look correct, with a good frame rate, when viewed from any location. Well, except from their exact centers (again).
Clouds are drawn in a base color of white but are modulated to match the lighting conditions so that they're red-orange during sunrise/sunset and dark gray at night. Their brightness decreases toward their center to simulate self-shadowing inside the cloud volume. I haven't yet tried to add light scattering to these clouds. In addition, they become opaque near the center where the noise value has less of an effect. They use regular Perlin noise rather than ridged noise for a more puffy and natural appearance, and have a mixture of high and low frequencies. The noise function offset varies slowly so that clouds change shape over time. Here are some clouds viewed from the ground below in infinite tiled terrain mode.
View of volumetric procedural cloud puffs from below. These clouds slowly change over time.
Tiled terrain mode actually draws three cloud layers (listed in the order in which I added them):
2D procedural noise cloud plane. The terrain and grass shaders ray cast into this layer for soft cloud shadows. I also have slow God-rays that ray march through the cloud density function. Density depends on weather conditions and atmosphere. Slowly animated/scrolling.
Static textured upper cloud layer. Provides a more interesting background than pure blue.
Puffy volumetric clouds. The new mode that's the subject of this post. Doesn't cast shadows yet - unclear how the terrain and grass shader can ray cast into these. Animated, but more slowly.
The sky looks a bit cluttered with all three cloud layers enabled now. Also, it's odd that the first layer casts shadows, but the second layer (which is denser and more apparent) doesn't cast shadows. Maybe I'll remove the first cloud plane layer later. Or maybe the set of enabled layers should be determined by atmosphere and weather conditions. For example, use a large number of puffy gray volume clouds when it's rainy, but only sparse/high 2D cloud cover when it's sunny.
This wireframe view of clouds from above shows how they are composed of multiple 2D quad billboards in various orientations arranged around a common center point, similar to flower petals.
Clouds drawn in wire frame mode. The structure of the 13 intersecting planes can be seen.
Here is a short video of the player flying through the cloudy sky over some islands. Since the clouds are rendered in 3D, they appear as real volumes when flown through. Most games seem to use 2D cloud billboards or skybox background images that are only meant to be viewed from a distance.
So far I've come up with four different uses of this particle cloud rendering technology. I wonder what else I can use it for? Rocket explosions? Plasma balls? Smoke effects? Insect swarms? Actually this might work well for smoke, and could be a good replacement for the existing billboard cloud system I use for smoke in 3DWorld. I guess we'll just have to wait and see.
I'm continuing to work on improving the dynamic lighting in 3DWorld. This post is a short update that builds on the previous two posts (indirect lighting and dynamic lighting + triggers): Indirect Lighting Lighting and Triggers
I was reading another blog post from The Witness and it gave me an idea. The contrast is too high between the lit and unlit areas of the basement scene. All of the lighting is coming from the spotlight direct illumination; the indirect reflected light is completely missing. This is why the orange balls look black on the sides facing away from the lights. The scene doesn't look very real.
Let me review how the indirect lighting in 3DWorld works. The scene is divided into a 3D grid of light volumes in {x, y, z} that are uploaded to the GPU and used in the fragment shader to individually light each pixel. During the offline preprocessing phase, each light source emits millions of rays, each of which is traced through the scene using multiple CPU threads. Each ray's weighted RGB (red, green, and blue) light contribution is added to each grid cell that it passes through. This means that the fragment shader can query any point in space within the scene bounds to get the indirect lighting contribution. This uses more memory per volume than lightmaps and therefore the lighting is stored at a coarser granularity. But, it has the advantage that dynamic objects (such as the orange balls) that weren't part of the original static scene can be correctly lit. This approach may also be simpler to implement and more efficient to compute. I haven't implemented lightmaps so I don't know for sure.
Okay, back to the problem. Dynamic lights can be turned on and off when their triggers (light switches) are activated,
so the indirect lighting isn't constant. It can't be baked into the
(single) global lighting volume of the scene. The indirect lighting can't even be stored per-trigger because it needs to be removed when an individual light is destroyed. What is needed is per-light source volumes that are generated on-the-fly when needed and merged into the final lighting solution when their intensity (or enabled state) changes. Since the light triggering is infrequent, most game frames have the same set of enabled lights. It makes sense to only merge in the new lighting values when they change, and re-upload the merged data to the GPU sparsely. This avoids having to read from multiple 3D lighting textures on the GPU each frame. I haven't actually tried this, but I assume it would have a significant effect on frame rate. The 4-5ms of CPU time updating the lighting every few seconds is negligible.
So what does it look like? Here is a screenshot of the direct + indirect lighting effects on the basement spotlight scene with some orange balls in motion.
Direct + Indirect lighting + Shadows in the basement spotlight scene.
The biggest difference is the reflection of the spotlight hitting the ceiling and floor near the light on the back wall. The sides of the balls facing away from the lights aren't completely black any more. Much better. Unfortunately, now there is a 6 second freeze when the player first turns on the lights as the CPU computes 5 million rays (1M per light source) with 4 bounces each. That really ruins gameplay. Who wants to sit there waiting for the lighting to be computed in the middle of playing the game? It takes longer than loading the scene at the beginning!
One solution is to compute the lighting of each light source once in a preprocessing pass, then write it to disk for later reuse. I modified the scene file reader to accept filenames attached to each light for caching indirect lighting on disk. This works well, reducing lighting computation time from 6s to a few milliseconds.
However, now I'm stuck with multiple 8MB files on disk, one per light source. These files together take up more disk space than the rest of the scene files combined. They need to be compressed. Fortunately, they're easy to compress. The RGB color data is mostly zeros, and 32-bit floating-point numbers have more precision than I need. 8-bit unsigned integers would work just fine - they get converted to 8-bit in the GPU texture later anyway. The first thing I did was to remove most of those zeros. Since these are small local lights, their radius of influence is pretty small. In addition, their light is confined to this one room in the basement. I first filter the lighting values so that any value smaller than 0.1% is clamped to 0. Then I compute the smallest bounding cube that contains all of the nonzero values. This provides a 100-200x reduction in file size and memory usage. The 8MB files are now only 40-80KB. The reduction is enough that it doesn't seem necessary to do the 32-bit => 8-bit data compression.
Here are some screenshots comparing the effect of the different lighting components. In my opinion, the new combined direct + indirect lighting looks much better than direct only. [Ignore the frame rate on the lower left corner - I froze the scene update so the framerate counter wasn't updating. It normally runs at over 200 FPS.]
Uniform lighting shows the base material colors and textures. Some crates were added to provide more interesting shadows.
No lighting. A few emissive objects are visible (light switch and sky visible through the window).
Direct lighting + shadows only. The spotlights themselves are lit by a separate small light. Similar to the previous blog post.
Indirect lighting only. Most of the direct light hits the floor and ceiling near the back wall, reflecting light onto the wall.
Direct and indirect lighting combined form a more realistic global lighting solution for the scene.
My previous blog posts were about features I had added to 3DWorld long before that post, because I had some catching up to do. This time I'm going to talk about the dynamic lighting and moving platform system in 3D world, along with the player trigger system that controls it. I've been working on these features recently and some of them are still unfinished. This post will be more technical than some of my previous posts, but I'll try to add some pretty screenshots at the end showing the lighting to make it more interesting.
Some of these ideas were based on the game Marathon from Bungie Software that I played on my Mac when I was an undergrad at CMU. The game came with a level editor that had lights and platforms as level objects that could be configured and activated by player and AI/enemy actions. I found this system a simple and intuitive method of configuring lights and scripted moving objects, so I've copied many of the parameters in 3DWorld. The scene text file specifies all of the lights, platforms, and triggers as keyword + values. I'll start with the triggers.
Triggers
Triggers are non-visible scene entities that can be used to enable and disable other scene objects such as lights and platforms. They are added to the scene text file and assigned to other objects, where a single trigger can be reused multiple times to control more than one object. A different trigger may be used to enable vs. disable an object. Triggers are activated and/or deactivated by distance (player and/or enemy proximity), player activation regions, or actionable objects (switches). A trigger can have auto on or off time delays. For example, a player can press a switch to turn a light on and it will turn itself off after a specified amount of time. Or a platform can automatically start moving when a player or enemy walks on it, and stop moving when the player or enemy steps off. Or a light can be assigned both an auto-on and auto-off time so that it blinks on and off in a loop. There is no limit to the number or usage of triggers in a scene.
Platforms
Platforms are really just a collection of objects that move together along a fixed path. So far I have only implemented translation, which allows creation of elevators, sliding doors, crushers, moving walkways, etc. Rotations are more difficult because they change the classification of some of the physics object types. For example, a rotated axis-aligned cube may no longer be axis-aligned. Platforms are specified using several parameters (similar to those used in Marathon):
Position delta {x, y, z} (determines max extension/end point)
Forward Speed
Backward Speed
Activation Delay
Reverse (Forward => Backward) Delay
A platform's motion is implemented as a finite state machine with the following sequence of states: {Inactive => Active Delay => Forward => Reverse Delay => Backward => Inactive}. For example, an elevator that starts on the bottom floor when inactive will travel up at some speed after an initial delay, reach the top floor, stop there for some time, travel back down at the same speed, then deactivate. I placed small black cylinders at each of the 5 floors of the building for use as elevator buttons. Each button has a trigger object bound to it, and all triggers control the elevator platform activation.
Any entities (the player, enemies, bombs, explosion debris, blood and bullet hole decals, etc.) resting on/attached to a shape or polygon that's part of the elevator platform moves with it. In addition, the player can be crushed to death under a platform in gameplay mode. Triggers can be bound to platform shapes so that they move with the platform. This means that a light switch can be placed inside the elevator and moves with it.
Lights
Lights are currently statically attached to the scene, but can be bound to scene objects so that the lights are disabled when the objects are destroyed. The exception are the player flashlight and candle. This allows the level editor to make light sources dynamically destroyable in-game. One or more triggers may be assigned to control the light. If no triggers are assigned, then the light is always on until it's destroyed. 3DWorld supports directional light sources for the sun and moon plus three types of user-placed dynamic lights:
Point Lights (position, radius, and color)
Spotlights (position, direction, beamwidth, radius, and color)
Line Lights (start and end position, radius, and color)
Point lights and spotlights are pretty standard in games, but line lights are a bit different from what I've seen before. I implemented line lights in the fragment shader using the analytical equation for the light contribution to the fragment (pixel) integrated along the length of the line. They were originally only used for laser beams, but I decided that line lights make nice looking fluorescent tubes.
Light sources can have both direct (diffuse and specular) and/or indirect contributions. The indirect lighting is baked into the scene in a preprocessing step and read from disk at load time, so it only makes sense to enable indirect lighting for static, always-on, non-destroyable lights. As explained in the dynamic lighting post, the shader system can handle a large number of light sources efficiently, currently up to 1024 lights in a scene.
Shadows
Currently, 3DWorld only supports dynamic shadow maps for the sun, moon, and spotlights. These types of lights are easy to do because a single square shadow map works well. Point lights are typically implemented with cube map textures in other engines, and I don't have cube map shadows working yet in 3DWorld. However, some of the point lights on the walls and ceiling still look good when treating them as spotlights for the purpose of shadowing - as long as shadow casters can't get between the light and the wall/ceiling.
The dynamic light shadow maps are currently all 512x512 pixel square textures of a 32-bit depth format, so each one takes 1MB of video memory. I prefer to use simple shadow maps rather than PSMs (perspective shadow maps), etc. because of their stability. I really hate it if the shadow edges flicker when the camera is rotated. I currently have a limit of 16 enabled, visible, shadowed spotlights on my old computer and 32 on the new computer. The limits are mostly there to keep the framerate high when there are many scene lights, by disabling shadow maps for some of them. None of my scenes hit this limit.
Shadow maps are only updated when objects within the light's FOV (field of view) move, or if the light itself moves. Only objects within the FOV are rendered into the shadow maps for efficiency. If all of the potential shadow casters and the light itself are static objects (or happened to not move this frame), the shadow map from the previous frame can be reused, saving some GPU work. Shadow maps for inactive or destroyed lights are also returned to a global pool for reuse. This way, the player can move to different rooms of a scene and the engine only needs to have a small number of shadow maps in GPU memory at any given time. The pool of shadow maps is stored in a single OpenGL texture array that doubles in size when necessary, to reduce the number of texture units used and the number of texture binds/state changes. [I originally used a separate texture + texture-unit for each one, but that caused problems with resource usage on the ATI card in my old computer even though my newer computer with Nvidia card was fine with it.]
The unshadowed lights cause significant light leaking problems. For example, if the lights are on in the basement, their large radius of influence can affect objects in the floor above them. Since light normally doesn't shine through floors (except for those who live in glass houses), this looks obviously wrong. My solution is to disable lights that produce no direct illumination that is visible to the player. Around 100 random, but approximately uniformly distributed, rays are sent out from the light source center within the field of view of the light (light cone for spotlights, hemisphere for lights on a wall/ceiling, etc.) These rays are representative photons. Their hit locations are points in the scene that are directly illuminated by the light source. Another ray is sent from each hit location to the camera, and if no scene objects are intersected then this point is visible to the player, the light must be enabled, and we're done. If none of the hit points have a line-of-sight to the camera, then the light source likely has no influence on the scene and can be disabled. It's not 100% correct since the rays are only sparse samples, but it works well in practice, especially if care was taken during scene/light layout. The scene bounding volume hierarchy provides an efficient acceleration structure for these ray tests, and hit locations can be cached. Overall this approach improves the framerate by reducing the number of active lights. For example, if a light is in a closet behind a closed door, all of the rays from the light source will hit the inside of the closet. None of those hit points will be visible to someone standing outside of the closet (all secondary rays will hit the closet wall and door) and the light can be skipped. The player will never know if the light is on in that closet unless they go inside.
Here are two screenshots of the building lobby with a single large point light source. This light is always on (static) and has indirect lighting enabled. You can see the shadows of the Chinese dragon on the counter top and the soft shadows from the counter and round table on the floor. The indirect lighting component is high because of the high surface albedo of the ceiling, walls, and floor, so the shadows aren't very strong.
The lobby scene illuminated by a single large point light source with indirect lighting. Note the shadow of the dragon statue on the table/desk/counter and the soft shadows around the edges.
Closeup of the golden dragon statue, which is mostly lit by specular light. It is self-shadowing and shadows the counter top.
Here are some screenshots taken in the basement of the building. I put a lot of scene lights in here since there are only two small entrances and very little outside light reaches this area. Thus it has good contrast between lit and unlit surfaces and the shadows are very pronounced. It's good for testing. The lights in the straight hallway are player motion activated. The spotlights along the top of the wall and the line light on the opposite wall are activated by the pink and yellow illuminated light switch. They are set to automatically turn off after 1 minute.
A variety of player-controlled colored point and spot lights in the basement scene.
Basement scene with line light and spotlights in the distance, including normal mapping.
Here you can see that the orange spheres (the "balls" that can be thrown in gameplay mode) create shadows on the ceiling and floor. It's hard to tell from this screenshot, but they also shadow each other. It's normally not possible to throw this many balls at once in gameplay mode, so I had to freeze time in order to do it. One ball is just in front of and above the light so it casts a large shadow on the ceiling. The player also casts a capsule-shaped shadow that's not seen below.
Spheres casting spotlight shadows on the walls, floor, ceiling, and each other.
This is a screenshot of the house map with line lights representing fluorescent tubes in the ceiling. They are controlled by a light switch behind the player and another switch at the top of the stairs. Both switches toggle the light on/off state. You can also see a bright circle from the player's flashlight beam, which is another example of a (moving) spotlight. I experimented with adding a flashlight shadow but it doesn't really add anything useful to the scene. Since the flashlight is mounted to the player's "eye" it's always in the center of the screen, and it's impossible to see anything in the flashlight's shadow.
House scene with line light sources in the ceiling and a player flashlight spotlight.
Here is a picture of the elevator with the light on and a big flashing red warning light on the ceiling.
Player controlled working elevator with lighting. The button the the right wall activates the elevator. Each floor has a button.
I'll end this post with a video of me running around the building basement, activating lights and the elevator, destroying stuff, and shooting out the lights. Much of the scene is destroyable, but I didn't have time to shoot at everything in this large scene in this video. Note that shooting the gas tank on the left causes it to explode, which also destroys the tank on the right and takes out the light on the ceiling. The elevator light only turns on when the player is in the elevator. Oh, and yes, the player shadow is a big capsule/ellipsoid shape. Here is the Blogger version of the video:
And here is the YouTube version:
Which do you think looks better? It looks like the YouTube version is better.