Sunday, 31 October 2010

Bluestone GPU - Light volumes

Following on from my previous post I've been working with some of my old test scenes to see how well light volumes stack up against light maps. Obviously the resolution is no where near the same but the results are still pretty good.

Here I'm using the street scene again with three 32x64x32 volume textures to store indirect lighting for the entire scene. Light leaks do occur so I've had to resort to additional "blocker" geometry to help solve the problem. Thin double sided geometry like the hanging shades in this scene also pose a problem I've yet to tackle. Sample points close to this sort of geometry will need to be handled slightly differently.

Unlit scene showing offset volume samples.

The screenshot above shows the scene and each of the repositioned volume samples. Each line represents a volume sample which has been moved to a more suitable location. This also gives a good indication of the resolution of the light volume. In this scene, only the exterior surface geometry is set to distort the lighting volume. For more info see my previous post.

The video below shows the scene being rendered. Direct light is first rendered into lightmaps. This direct light is then sampled when rendering the indirect light volumes. Render times are as follows:

Direct light (light map): ~1.5 sec
Indirect light 1st pass (light volume): 3 seconds (512 rays)
Indirect light 2nd pass (light volume): 15 seconds (2512 rays)

Some additional screenshots as the video quality isn't the greatest...

Monday, 25 October 2010

Light volume sampling

With Bluestone now running nicely under SlimDX I've turned my attention back to light volumes. After seeing how well the technique worked on the sponza scene I've decided to go with volumes over lightmaps for the project I have in mind.

The issue I've been looking at recently is how best to sample a light volume for a scene containing angled or curved geometry. This wasn't an issue with the sponza scene as it fit nicely inside a rectangular volume, it was enough to simply sample the scene at the centre of each light volume texel.

However, you quickly run into trouble with scenes containing curved or angled geometry. You can see in the image below that if we simply sample each volume texel centre we'll end up sampling “outside” the scene. This often leads to streaks and blotches and ends up looking pretty naff.

The problem is exacerbated by linear texture filtering when sampling the light volume. Incorrectly sampled volume texels can adversely affect their correctly sampled neighbours. It's identical to the problem caused by incorrectly padding lightmap textures, only now the problem is in three dimensions.

Overcoming sampling and linear filtering issues

The above two images show a cross section of a light volume texture. Imagine the red line is a wall, on one side the lighting values are dark, on the other, the light volume texels are not occluded and are now lit by the sky. The dots show the location of each sample. The image on the left looks fine, the geometry is completely covered by valid samples, on the right, we're already in trouble.

 Let's see what happens when the texture is filtered during lookup.

The light volume is leaking all over the place. If we were to view the scene in game the interior wall would have a noticeable blue glow but how do you fix that? In a lightmap it's fairly easy, you just copy and expand the border pixels of each UV chart. In a light volume though it's a bit harder.

In short, the way I've overcome the issue is to move the sample location of any invalid samples.

With the invalid samples moved to valid locations the light leaks have been removed. What constitutes an invalid sample and where to then move it is the topic of a whole other blog post :) But to wrap things up, here are some before and after shots. The lighting and AO for this scene was rendered in Bluestone in about 15 seconds. Direct light and AO are rendered into lightmaps, indirect light is rendered into the light volume.

Sample positions left untouched:

Sample positions fixed:

Another example (click for fullsize)

Friday, 15 October 2010

Bluestone in action

Porting Bluestone over to SlimDX has been a lot easier than I anticipated so I thought I'd post a video of it up and running. There are still a few systems yet to be ported but the core is fully functional.

The video shows a couple of scenes being rendered although I've edited out the sections of video where it's rendering. The render times are quick but still, I figure you have better things to do :) I've noted the actual render times on screen wherever an edit has taken place.

A few words about the technique I use in the videos. I basically start out with a direct light pass and make sure that everything is as it should be. Once I'm happy with that I do a few quick low quality GI bounces to illuminate the shadowed areas. Usually about 256 - 512 rays, depending on the scene. Once that's looking good and I can see that light is getting into all the corners I do a final pass at about 2000 - 4000 rays to get the final result.

I'm really keen to hear peoples thoughts on Bluestone so send me an email or leave a comment. I especially want to hear from you if you're working on something and think Bluestone would be useful as I'm looking for testers.

Wednesday, 13 October 2010

Bluestone GPU lightmapper update

2.1 million sample points rendered in 6 seconds (Bluestone screenshot)

The GPU lightmap renderer I've been developing (Bluestone) is written in XNA. It's always been a concern that the remit of XNA most definitely isn't GPU lightmap renderers but until recently that hasn't been an issue.  Unfortunately, the changes made to the framework in XNA 4.0 (link) mean that using XNA for this type of application is probably no longer practical. The biggest issue for me is the loss of control over render targets and depth buffers. In XNA 4.0 render targets are automatically assigned depth buffers. Meaning it's no longer possible to reuse a single depth buffer among multiple render targets, which is what Bluestone requires.

In order to move the project forward I needed to look elsewhere. There were a few different options but I've gone with SlimDX, for several reasons. I can stick with C# for one but it also has the advantage of opening up a lot of very nice DirectX 10 features not available in XNA. So far the porting has gone well and both the direct and indirect lighting systems are in place.

In related news, an unexpected win was a decrease in global illumination render times. I made some alterations to the GI shader which have worked wonders. I need to test it more widely but the improvement seems to be about 30% in most cases.

Monday, 4 October 2010

Untextured lighting stills

Some plain diffuse volume light shots. Click for high res versions.


Animation blending

I've done some more work on the animation system recently so I thought I'd change gears slightly and write about that today. 

You will have noticed in the previous video I posted that the robot's animation transitions are very sudden and don't look very natural (even for a ridiculously proportioned robot).  The remedy to this is animation blending, whereby two or more animations are smoothly blended during a transition phase. This has been on my to-do list ever since implementing skeletal animation so I've finally gotten around to it.

The first place I turned to for implementation details was the book Game Engine Architecture by Jason Gregory (which I highly recommend). Chapter 11 contains a wealth of practical information on implementing animation systems. 

The first change required was to store keyframe data in scale/rotation/position format (also known as "SQT") instead of matrix format.  The reasons for this are; a) it's smaller, b) it's impractical to blend between transform matrices. The second was to implement a global time line scheme when dealing with the animation clips (also detailed in the same chapter). I won't go into my implementation as it's still quite rough at this stage and bound to change but definitely check out the book if your interested. 

With that in place, a short transition between animations made a big improvement but you can go one better. Linearly blending between animations doesn't take into account any of the twelve principles of animation and makes for frowny animators (in this case, me). By blending in additional transition clips you can create much more interesting animation behaviour. In this case I've added a transition clip to the start and finish of the walk cycle.

Left: idle to walk. Center: walk loop. Right: walk to idle.
  • idle to walk transition: This one's very quick as the character needs to start walking straight away. It basically consists of  a slight anticipatory body dip and also starts the limbs moving.
  • walk to idle transition: A longer animation used to help settle the character back into it's idle pose. There's a slight body sway as the torso comes to rest and the arms swing forward to follow through after the motion of the walk. This helps to convey a sense of weight to the character.
The in game results can be seen in the video below.