Shadows

Visualize supports two different kinds of shadows: simple shadows and shadow maps.

Simple Shadows

HOOPS Visualize supports simple shadowing of a model using a single light source and plane. This type of shadowing is sometimes referred to as fake shadowing, because it does not rely on light interpolation. Rather, the shadow effect is achieved through the use of texture maps. Simple shadows are limited to one light source “projecting” the shadow onto a simple plane. Note that the shadows are not projected onto any geometry except for the plane you specify. Shadowing is enabled through the use of the HPS::VisualEffectsControl:

    // enables simple shadows
    mySegmentKey.GetVisualEffectsControl().SetSimpleShadow(true);

    // sets the plane where the shadows are projected
    mySegmentKey.GetVisualEffectsControl().SetSimpleShadowPlane(HPS::Plane(0, 1, 0, 0.425f));

    // color of the shadow
    mySegmentKey.GetVisualEffectsControl().SetSimpleShadowColor(HPS::RGBAColor(0.2f, 0.2f, 0.2f));

    // light direction
    mySegmentKey.GetVisualEffectsControl().SetSimpleShadowLightDirection(HPS::Vector(0, 1, 0));
../_images/turbine.png

A model rendered with simple shadows enabled

You can modify several shadow options, such as shadow resolution, blurring, and whether or not transparency is ignored during the calculation of the shadow. These are specified during the SetSimpleShadow call:

    mySegmentKey.GetVisualEffectsControl().SetSimpleShadow(true, // enabling simple shadows
                                                           256, // resolution of the shadow
                                                           16, // blurring amount
                                                           false); // ignore transparency is false

Simple shadows are supported in all drivers.

Shadow Maps

Under the DirectX11 and OpenGL2 drivers, Visualize supports shadow maps. In shadow mapping, rendering a scene takes two drawing passes. First, the scene is rendered from the light’s (distant and spot lights) point of view. Then, the z-buffer is extracted and stored as a texture. This texture is used as a shadow map. In the second and final drawing of the scene, each pixel is rendered in the light or shadow based on information from the shadow map. Unlike simple shadows, shadow maps are not limited to planes. They can be used to render shadows cast by faces onto any geometry that can be lit. To enable this functionality:

In the code below, shadow maps are enabled for the pictured model by enabling shadows as shown:

    mySegmentKey.GetVisualEffectsControl().SetShadowMaps(true, // enables shadow maps
                                                         16, // samples
                                                         2048, // resolution
                                                         true, // view dependence
                                                         true); // jitter

    mySegmentKey.GetVisibilityControl().SetShadows(true);
../_images/enable_shadow_maps.png

A model rendered without shadow maps (left) and rendered with shadow maps enabled (right)

Using shadow maps can create a more realistic scene, but there are memory and performance costs. Rendering a scene with shadow maps can result in a significant slowdown in rendering performance. However, the shadow map options provide several parameters you can modify to help you balance the level of visual detail with performance. For instance, you can determine the width and height of your shadow map by setting the resolution parameter to a value between 512 and 2048. A higher resolution shadow map can increase the visual quality of your shadows, especially around the edges. However, it also means a larger memory footprint. The default resolution is 512. The images of the racecar in this section show the results of applying each option.

../_images/shadow_map_resolution.png

A scene rendered with a shadow map resolution of 512 on the left and 2048 on the right - both scenes have a sample count of 4

You can also select the number of samples used to determine the amount of light projected on each pixel in the final scene. Valid values for the sample option are 1, 4, 9 or 16. If the sample parameter is equal to 1, Visualize calculates each pixel value to render the scene, then looks up one corresponding value in the shadow map to determine whether a pixel is in shadow or not. When the sample count is greater than one, it takes the specified number of samples from the shadow map and averages them to create a percentage value representing how much light should be applied to the pixel. If a pixel is clearly in light or shadow, taking more samples will have no effect on the rendered scene but for pixels that lie on shadow edges, more samples can reduce aliasing effects and soften shadows. Note that increasing the number of samples taken for each pixel requires more processing cycles during render time.

../_images/shadow_map_samples.png

A scene rendered with a sample count of 1 on the left and 9 on the right - both scenes have a shadow map resolution is 1024

Visualize provides another parameter under the shadow map option called jitter. When jitter is enabled, the system will perform an additional calculation when taking samples from the shadow map. Instead of taking samples that directly correspond to a pixel’s location in the shadow map, a stochastic function is applied to the process of determining where to take samples. If shadow edges in your scene are particularly jagged and sharp, jitter may improve the scene visually by creating a white noise effect around these areas.

../_images/jitter.png

A scene rendered with jitter off in the left image and jitter enabled in the right image. Both images have a sample count of 4 and shadow map resolution of 512.

By default, shadow maps are recalculated in a view dependent fashion. This means shadow maps are recalculated every time the camera changes position, taking into consideration the new view frustum. This improves the quality of the shadows especially if the camera is zoomed in, but incurs some extra per-update calculation time. If the camera will not be zoomed in and you have non-camera relative lights, the ‘view dependent’ suboption of shadow map rendering option can be disabled for a small performance improvement.

../_images/not_view_dependent.png

This pair of images shows a model with shadows rendered without the view dependent option. Notice that the shadow resolution remains the same even in the zoomed image.

The following image shows the rendering difference when using view-dependent shadows:

../_images/view_dependent.png

This pair of images shows the same model as before, but this time the second image is rendered with view dependent shadows. Notice in the zoomed right image, the shadows are sharper than in the left image.

Once you have enabled the shadow map rendering option, you will need to enable the shadow option using the HPS::VisibilityControl to see shadows in your scene. This can be done on a per segment basis. The shadow visibility options have three parameters: casting, receiving and emitting. Setting the shadow visibility to true is shorthand for turning on casting, receiving and emitting. The casting parameter lets you determine if a piece of geometry will cast a shadow (only faces can cast shadows). The receiving parameter determines whether a piece of geometry can receive a shadow. This parameter can be applied to any geometry that can be lit. Finally, the emitting parameter lets you determine if a light can cause a shadow to be cast. The emitting parameter only applies to distant lights, and therefore should be set on the segment containing the light. In addition to limitations created by lighting, it is also important to note that translucent surfaces do not cast shadows. There are also a number of restrictions on when objects can cast shadows.

    mySegmentKey.GetVisibilityControl().SetShadowCasting(true);
    mySegmentKey.GetVisibilityControl().SetShadowEmitting(true);
    mySegmentKey.GetVisibilityControl().SetShadowReceiving(false);

Shadow maps and simple shadows can be set up such that a light casts these shadows in a consistent fashion. This can be achieved by simply setting the shadow light direction of the simple shadow to match the light’s vector. For example, if the distant light’s direction vector is (1, 1, 1), then one would set the simple shadow light direction as follows:

    mySegmentKey.GetVisualEffectsControl().SetSimpleShadowLightDirection(HPS::Vector(1, 1, 1));

NOTE: When using backplane culling with shadow maps, you should be aware that backplane culling is performed relative to the camera. Therefore, mixing backplane culling with shadow maps may produce unexpected results, such as shadow casting of parts of the model that are invisible to the camera.

Ambient Occlusion

Ambient occlusion is a shading method which adds a photorealistic effect to local reflection models by taking into consideration light attenuation due to occlusion. Ambient occlusion requires global lighting calculations around every vertex of a model. The result is a soft shadowed appearance offering a better perception of the model’s shape. However, to achieve a good result, an additional operation that subdivides that model needs to be performed. Thus, these calculations can be extremely expensive.

HOOPS Visualize simulates ambient occlusion in a more efficient way by processing the rendered images, including the z-buffer, instead of operating on the geometry directly. It applies a filter to search for small valleys in the depth buffer and then slightly darkens these areas. This operation is extremely low-cost computationally because it is unrelated to the size of the model. Thus it can be used on even the largest model. In addition to having little to no negative impact on performance, the approximation of ambient occlusion has no impact on the memory footprint of the model.

Ambient occlusion is part of the HPS::PostProcessEffectsControl. There is a suboption called strength which lets you determine the overall darkening effect of the occlusion calculation. The code sample below shows how you can enable ambient occlusion and set the strength to 2.

    myWindowKey.GetPostProcessEffectsControl().SetAmbientOcclusion(
        true, 2.0f, HPS::PostProcessEffects::AmbientOcclusion::Quality::Nicest);
../_images/RacecarAmbientOcclusion.png

In the left image, ambient occlusion is turned off; in the right image, it’s enabled with a strength of 2.

In the figure above, we can see the understated effects of ambient occlusion. Dimpled areas, edges, and regions where two or more surfaces meet are rendered with a slight shadowing. As a result, there is more of a sense of depth and realism in the scene.

If you turn lights completely off but keep ambient occlusion on, you can create a non-photorealistic shading effect commonly found in cartoon and other graphics media.

Please note that transparent geometry is unaffected by ambient occlusion.