Der Schmale – David Lenaerts’s blog

Flash Platform Experiments

Another Take on Skin Rendering

Tags: , , , , ,

It’s been a while since I’ve made a blog post about something I’ve played around with just for the heck of it.  With Away3D 4.1 Alpha pushed out yesterday, I decided to spend the day revisiting an old friend: skin rendering. Remember the blog post of skin rendering when Away3D 4 was still codenamed Broomstick over a year and a half ago? (ugh, time flies!) A lot has changed in the engine, and many new features have been added. It made sense to therefore build a new but similar example showing some newer additions, and highlighting how things were put together. There’s two variations of the demo:

  • With a familiar face: Using the same Lee Perry-Smith model as before, for direct comparison (and because it’s free and I’m a stingy wretch)
  • With a unfamiliar face: Using a free (and insanely detailed) model from TurboSquid because I’m getting a bit sick of the old head (no offence intended to Mr. Perry-Smith) And again, it’s free and I’m a stingy wretch!

As usual, click+drag to move the camera.

There’s source right here. There’s a bunch of boiler plate in there, but the only thing of interest in this case should be the initMaterial method in Main.as. I’ll highlight the material setup here.

Multi-pass materials

The demo is using a multi-pass material for a few reasons. It allows the shadow mapping to more complex, and it prevents the non-casting lights from being masked by the shadow map, allowing for more dramatic lighting effects.

Soft Shadows

With a SoftShadowMapMethod assigned to the material, it’s possible to – as the name suggests – cast soft shadows on the skin. The class was updated in Away3D 4.1 along with some other shadow map methods, allowing more samples to be taken in the shadow map. This way, the results are much smoother which is especially important when tweaking the method’s range property. It sets the distance with which shadow map samples are taken, effectively creating softer shadows (a setting you can tweak in the demos). Of course, the more samples you take, the more demanding your shader will become.

Fresnel Specular Highlights

This is identical to the old demo. It uses FresnelSpecularMethod to achieve the fresnel effect, causing stronger highlights at glancing viewing angles. The fresnelPower can be tweaked to change the viewing angle fall-off: higher values increase the fresnel effect.

Subsurface Scattering Through Gradient Diffuse Lighting

By using GradientDiffuseMethod, you can acchieve a very crude approximation to subsurface scattering. It allows you to pass a gradient image to define the colour and strength of the diffuse reflection for each light/normal-angle. The left of the texture (x = 0) contains the reflection for surfaces pointing away from the light, the right is the reflection when completely facing the light. This allows darker lighting to be made a bit lighter. In this case, by introducing a little red in the mid-values (see “embeds/diffuseGradient.jpg”), we can simulate the addition of scattered light into the final shaded colour. While it doesn’t create true translucency effects similar to SubsurfaceScatteringDiffuseMethod, it does create an organic softness when viewing the surface head-on, an effect that is as subtle as it is effective.

Lighting Set-up

The light set-up is very traditional, a directional light coming from above and the front (by default) with a bright blue point light to the right and a red point light to the left. It’s worth noting however that I’ve set the directional light’s specular property to a much lower value to prevent it from creating strong highlights. Much like a softbox, I’d imagine.

Enjoy tweaking!

Leave a comment (7 comments)

Multi-pass Rendering and Cascaded Shadow Mapping

Tags: , , , , , , ,

If you’ve been paying attention to the Away3D blog, you probably already know that the 4.1 alpha has been released today, which has been my main fixation since September (when I wasn’t getting radiation poisoning). As mentioned in the release post, one of the new features is “multipass shading”. If you’re not into 3D rendering programming, you may be asking yourself: “¿Qué?”, so I would like to go a bit more in-depth into the whats, whys, and hows.

Multi-pass shading

Multipass rendering is simply executing different render calls (“passes”) for a single geometry. Strictly speaking, Away3D 4.0 has supported multiple passes since its inception; SubSurfaceScatteringDiffuseMethod caused a depth map pass to be added, and OutlineMethod introduces a new pass to render the outline seperately from the normal geometry. However, all lighting – and methods contributing to the colour of the final output pixel such as fog, rim lighting, etc – happened in a single pass. With shaders being limited in number of instructions and amount of registers, so are the amount of lights that can affect a surface as well as the complexity of any other piece of code, shadow mapping being a common victim. This is where 4.1 introduces multiple passes (hence multi-pass shading) in the form of TextureMultiPassMaterial and ColorMultiPassMaterial. For all intents and purposes, they work identical to their single-pass counterparts (TextureMaterial and ColorMaterial, respectively), but they automatically split up into different passes depending on the amount of lights and the use of shadow mapping. The results are blended together to form a correctly shaded surface. This way, there’s no strict upper limit for lights. Of course, drawing the geometry multiple times comes with its own cost, and you should take care not to go gung-ho just because you can!

Cascade Shadow Mapping

Having shadows in a separate pass greatly increases what you can do to increase shadow quality. Getting shadows to look great is always a bit of a challenge, and one of the techniques that’s popular is Cascaded Shadow Maps (a great explanation here). If you’re too lazy to read the msdn article, here’s the gist of it. Due to perspective projection, fragments close to the viewer generally suffer shadow aliasing, because a texel in the shadow map covers much more screen space closer to the camera than it does farther away. In fact, for distant fragments, the shadow map resolution is often too large since several shadow map texels map to the same screen fragment. So there’s not enough resolution in the front, and possibly too much in the back! Cascaded Shadow Maps try to solve this by splitting up the view frustum and rendering separate shadow maps for each segment. The closer to the viewer, the smaller the frustum segment should be so as to increase the projected resolution of the shadow map.

CSM in Away3D 4.1

As you’ll see in demo code, it’s pretty easy to get this technique to work. Just be sure to use a multi-pass material and assign CascadeShadowMapper to the light and a CascadeShadowMapMethod to the material (this is identical to how NearDirectionalShadowMapper and NearFieldShadowMapMethod work).

// can pass a value up to 4 in CascadeShadowMapper
cascadeShadowMapper = new CascadeShadowMapper(3);
cascadeShadowMapper.lightOffset = 10000;
directionalLight = new DirectionalLight(-1, -15, 1);
directionalLight.shadowMapper = _cascadeShadowMapper;
scene.addChild(_directionalLight);

multiMaterial = new TextureMultiPassMaterial(texture);
multiMaterial.lightPicker = new StaticLightPicker([light]);
// you can also use HardShadowMapMethod, SoftShadowMapMethod, DitheredShadowMapMethod as base method
baseShadowMethod = new FilteredShadowMapMethod(light);
multiMaterial.shadowMapMethod = new CascadeShadowMapMethod(baseShadowMethod);

Away3D splits up the view frustum automatically, but it’s often a good idea to play around with different split ratios yourself. The values with the best results depend heavily on the scene, how close you allow the camera to get to shaded surfaces, and so on. This is done by simply specifying the ratio in the view frustum for each cascade level to reach, “0″ being the frustum’s near plane, “1″ being the far plane. Generally, you’ll want the last cascade level to reach to the far plane, thus passing “1″.

For example, when using 4 cascade levels, you could specify the splits as follows:

cascadeShadowMapper.setSplitRatio(0, .1);
cascadeShadowMapper.setSplitRatio(1, .2);
cascadeShadowMapper.setSplitRatio(2, .4);
cascadeShadowMapper.setSplitRatio(3, 1.0);

A small detail to note: because texture memory is a precious resource, Away3D internally only uses a single texture for all (up to 4) shadow maps.

The demo and source

The demo that was built to demonstrate the multi-pass materials features the Sponza model built and made available by Crytek. You can find the original at their download site. It’s a bit of a behemoth, so give it some time to load ;) It allows you to play around with some of the shadow map settings, which should be pretty straightforward. At least, they will be when I get around to writing an upcoming blog post about shadow map filter types. Source for the demo can be found on Github.

If you were at my presentation at Reasons to be Creative (what were you doing, missing out on the great weather outside!) you may recognize the set-up. It’s the same as I used for the deferred rendering demo; however, this is not deferred rendering: we merely re-purposed the demo :)

Get the Away3D 4.1 alpha version in the dev branch on Github.

Leave a comment (3 comments)

Away3D 4.1 (dev) Dynamic Reflections

Tags: , , , , , , , ,

R2D2*2One of the features we considered important for the next release of the Away3D engine (4.1) were real-time dynamic reflections, allowing for more realism and precision than the common static environment maps. In the dev branch of the engine, you can now find two flavours: reflections based on dynamic environment maps, and planar reflections.

Dynamic Environment Maps

This technique simply uses cube maps that are rendered to on the fly. Usually, they’re used for any non-planar surfaces, and while they can look convincing enough for complex models they do suffer the same flaws as normal environment maps. Since the scene is rendered for each face of the cube from a single point, the calculated reflections would obviously only be correct for that single point in space, but using it for relatively small and complex objects can look convincing enough. However, since it renders the scene 6 times, it can be slow for more complex situations.

The necessary functionality is exposed through CubeReflectionTexture, a class that can be used wherever a CubeTextureBase is expected: EnvMapMethod and variations, or even as a Skybox texture. I have yet to come up with a use for the latter case, though ;)  To get the best results, it’s usually a good idea to set the CubeReflectionTexture’s position to the centre of your reflective object. The cube map will be generated from this point and on average will yield the best results for all other points.

Check out the demo.
Source in the “dev” examples repository

Planar Reflections

For planar surfaces, a much cheaper approach and one that is very precise can be used. This means normal flat mirrors, polished floors, water, … which are quite common in games can be rendered much more effectively. Since the rules for reflection for the entire surface are the same, we can simply render the scene from a mirrored camera perspective. The only thing we need to make sure is that objects (or parts of objects) behind the mirror aren’t being rendered in this way. Usually, in OpenGL or DirectX, you’d simply introduce a new user-defined clip plane. Flash, however, doesn’t support anything of the sort. Instead, the projection matrix needs to be adapted so that the near plane becomes oblique; aligned with the mirrored plane, effectively clipping any straddling geometry along the mirror. Unfortunately, this also wreaks havoc on the far plane, which will starting cutting geometry that is in the mirrored view due to being at a different angle. Eric Lengyel effectively describes the issue and how he cleverly solves it in his Oblique View Frustum paper. (And while I’m at it, his book is awesome too.)

The texture target is provided as PlanarReflectionTexture. Similar to the cube maps, they need some information about where their respective reflective surfaces are. In this case, it’s the plane property; referencing a Plane3D object. Furthermore, it has a “scale” property that lets you define how much the texture should be scaled down to control quality vs rendering speed. Due to the different math and texture types involved, PlanarReflectionTexture can only be used with specific material methods. Currently, these are PlanarReflectionMethod and FresnelPlanarReflectionMethod. Except for internals and texture type, these function pretty much identical to their EnvMap counterparts.

Check out the demo
Source in the “dev” examples repository.

In closing, this is of course only in the dev branch, which means it’s still subject to change!

Leave a comment (13 comments)

Speaking at Reasons to be Creative 2012!

Tags: , , , , , , , , , , , ,

Oh boy, it shames me to see my last post has already been from December last year! Not that I haven’t been keeping busy; some binned personal projects, some that aren’t quite “there” yet, and a 3-month stint near San Francisco (what a city!). So yes, I’m happy to have a reason for another update, and what a Reasons it is!

After presenting at the awesome Flash on the Beach last year, I have the unexpected honour of having been asked to come back for another round this year with the conference being restyled as Reasons to be Creative. Sweet! My session this year is called “A Trick of Light”, and will primarily deal with 3D shading: simulating lighting for 3D rendering. I touched upon this subject briefly last year, but I felt such an interesting subject deserves full-time attention. I’m working hard on explaining things in an intuitive way, something many books forget to do, while still providing some maths. Just a little and in a very unrigorous way, so don’t be afraid if you’re a mathophobe ;) I won’t be digging into any actual code as I find that relatively pointless compared to understanding the actual concepts. Furthermore, I wish to present these in as much a platform-independent way as possible. However, all my examples will obviously be built using Away3D and I’ll be plugging that now and then, or what did you expect ;)

The line-up is looking great again this year and I’m glad to see many friends represented in the speaker list! Check out  the likes of Rob “I like turtles” Bateman, Frank “wob-wob” ReitbergerEugene “buy-me-a-beer” ZatepyakinJoa “who needs semi-colons anyway” Ebert, Jon HowardAndre Michelle, Mario Klingemannet al… Do yourself a favour and don’t miss it! I myself am looking forward to hanging out with old and new friends and get some inspiration shots, so do come over and have some beers with us!

Well, it’s time for me to lock myself up inside again with some 80s thrash metal and get that presentation done! See you all in September!

 

Leave a comment (0 comments)

Convolution Light Probes in Away3D 4.0

Tags: , , , ,

As mentioned in the previous blog post, the dev branch of Away3D 4.0 now has light probes. Light probes are essentially a special sort of light source that contain global lighting info for the scene location they’re placed at, encoded as cube maps. For diffuse lighting, these cube maps are simply indexed by the surface normal of the shaded fragment. For specular lighting, the view reflection vector is used. As such they’re a fast basic way of faking global illumination. The fact that it’s using cube maps means that the lighting would only be physically correct at the precise point where the light was sampled, but the results can still be convincing regardless. Several light probes can be placed in a scene; the material’s light picker will assign weights to each to determine the importance in the lighting step based on the rendered object’s position relative to each.

You can use normal lights alongside the light probes, and in case of the default material system, you can specify which lighting component (diffuse and specular) is affected by which light types (material.diffuseLightSources and material.specularLightSources accepting LightSources.LIGHTS, LightSources.PROBES,  or LightSources.ALL). This can be useful if you want to use light probes for diffuse global lighting, but want specular lights from traditional light sources without those affecting the diffuse light.

 

Pre-process step

A downside of this approach over conventional light sources is that it requires a pre-process step to generate the cube maps. To start with, you need to generate (HDR) cube map renders for every point where you’ll want to put a light probe. From this, two process steps can be applied: a diffuse convolution and a specular convolution, depending on which type of lighting the probe needs to support. These can be generated in the eminent Paul Debevec’s HDRShop. I’ll warn you, however, that these convolutions can be very time-consuming – but the results are very accurate. If you’re willing to sacrifice some accuracy and get your hands dirty, you can write your own “convolutor”. Be warned however, that Flash doesn’t support HDR in BitmapData or textures, so if you’re using BitmapData and Stage3D, you’ll end up with under-lit results due to the low dynamic range of “bright” light. Having said that, I’ll quickly outline the principles behind it, just in case it interests anyone.

Lighting functions are spherical functions (the domain is the unit sphere, being mapped to scalar values), so you’ll need to integrate the diffuse or specular lighting function over the unit sphere of surface normals/view reflections. In fact, integrating over the hemisphere is usually enough since there’s no light coming from inside objects in our case. But as I was lazy and it was much easier to just implement it for the entire sphere I’ll use that approach ;) For basic (Lambertian) diffuse reflections, this semi-formally boils down to:

Where N is the surface normal, “ω_i” is the incident light vector, and L(ω_i) is the light colour coming from the direction ω_i. The part “max(N.ω_i, 0) L(ω_i)”, is in fact just the same as the diffuse lighting calculation done for point and directional lights (where L is a constant function). In this case, L(ω_i) is simply a sample from the HDR cube map we rendered before. The integral just means that we’re taking the diffuse contributions for all directions (ie: all vectors ω_i in the unit sphere S) and accumulating them. Finally, to be able to sample the incoming light for each normal in an environment map, we would need to perform this calculation for every point in that map (remember, cube maps coordinates are simply 3D vectors, in this case representing the surface normal N).

In theory, the integral means we’d be summing up infinite amounts of infinitesimal samples. In practice, we’ll be using discrete steps to solve this. That’s okay, since we only have finite amount of samples to work with anyway; 6*n² pixels to be exact, n being the size of the HDR cube map. So, the most accurate way of doing things would be to add up all the contributions from the source map, and do that for each pixel in the destination map. If we’re rendering to a cube map of size m, that means it’s an order of 6*m² * 6*n² = 36m²n² (6 sides of m*m, each pixel sampling 6 sides of n*n). This can easily become a very expensive operation. Luckily, there are some numerical methods to solve this faster (at the cost of accuracy, or what did you expect). The most useful in this case is definitely Monte Carlo integration, which allows less samples from the source material per pixel. There are many articles written about the subject, and a great introduction is found in this article by Robin Green.

Enough to get you cracking, I think ;)

 

Demo

See the Cornell Boxness of it all!

Use the arrow keys to move the head, space bar to toggle the texture (shows the lighting impact), and finally click and drag to rotate the head.

The head model and textures are still by Lee Perry-Smith (who by now must feel pretty awkward appearing in random tech demos). Source for the demo can be found in the texture-refactor branch of the examples repo (which will be merged to master together with the dev branch).

The environment maps were made as outlined before using a custom generation tool, and placed close to each corner of the box where they were generated.  I was using Stage3D (and thus a low dynamic range) to generate them, so I had to do some touch-ups in Photoshop to get them to look right. I’m not done investigating the options for further development of such map generation tools, but who knows they’ll make it public some day. In any case, HDRShop is more reliable! Diffuse lighting is set to use light probes only, while specular lighting uses the point light only.

The demo also shows how to use point light shadows, rim lighting and light mapping for added static shading.

Leave a comment (8 comments)

© 2009 Der Schmale – David Lenaerts’s blog. All Rights Reserved.

This blog is powered by Wordpress and Magatheme by Bryan Helmig.