9.4. Shadows


You can create real-time shadows using the class SoShadowGroup( C++ | Java | .NET ). This group node performs real-time shadow casting. Each shape within this group may be included in the shadow casting computation. A shape may cast a shadow on other shapes or on itself (self-shadowing), and/or may be shadowed by other shapes within the group. The current shadow style is used to determine if shapes cast a shadow and/or can be shadowed (see SoShadowStyle( C++ | Java | .NET )).

SoShadowGroup( C++ | Java | .NET ) defines two shadowing methods which can be selected with the method field:

In this mode, shadows are not filtered and will display aliasing (jagged edges). All directional lights and spotlights (but not point lights) above the SoShadowGroup( C++ | Java | .NET ) node are used for computing the shadows. Adding lights as children of this group node will give incorrect results (lights must be defined above this node). Even when using the best quality setting (see quality field), some aliasing artifacts will appear if you zoom in very close to the scene. Another way to increase the shadowing quality (decrease the aliasing effects), is to specify a spherical region centered at the camera position within which the shadowing will be computed (see visibilityRadius field). The smaller the radius of this sphere, the better the shadow quality. This parameter is particularly useful for walk-through scenes.

[Warning]

In this mode VolumeViz do not cast or receive shadows.

[Note]

In this mode, increasing the number of lights decreases the performance.

VARIANCE_SHADOW_MAP:

This method supports soft shadows (see image below). The smoothness field can be used to increase or decrease the softness. Unlike the SHADOW_MAP method, aliasing is not a problem, but on large scenes, shadows will progressively fade away. Increasing the quality value will reduce this problem. Only one directional light, the last one traversed before the SoShadowGroup( C++ | Java | .NET ), is used to compute shadows with this method, other lights are ignored. In this mode VolumeViz nodes can cast and receive shadows.

[Warning]

Lights that are children of SoShadowGroup( C++ | Java | .NET ) are ignored and can give unexpected or incorrect results.

[Warning]

The shadow casting feature is not yet compatible with the bump mapping feature. Trying to combine these two features will give you an incorrect visual result.

The fields of the SoShadowGroup( C++ | Java | .NET ) node allow you to specify the quality, precision, and intensity of your shadowing:

isActive (SoSFBool( C++ | Java | .NET ))

Activates/deactivates shadowing. If deactivated, this node behaves like an SoGroup( C++ | Java | .NET ). Default is TRUE.

method (SoSFBool( C++ | Java | .NET ))

Specifies the shadowing technique to use. The default method (SHADOW_MAP) supports multiple lights but gives hard shadows and does not support VolumeViz. The VARIANCE_SHADOW_MAP method only supports a single light, but provides soft shadows and supports VolumeViz.

smoothFactor (SoSFInt32( C++ | Java | .NET ))

Sets the smoothness of shadows. Higher values give smoother shadows. Default is 1.

Used only for VARIANCE_SHADOW_MAP method.

quality (SoSFFloat( C++ | Java | .NET ))

Specifies the quality of the shadow. Increasing the quality will reduce performance and increase memory consumption. For SHADOW_MAP, 0 is lowest quality and 1 is highest quality. For VARIANCE_SHADOW_MAP, quality may be greater than 1 (texture used for shadowmap rendering will be of size 2*quality*ViewportSize)

Increasing quality decreases performance because more textures must be transferred to the graphics board, particularly when the lights or objects in the scene move. However, when the objects and lights in the scene are static, then even if the camera changes, the quality has no impact on the performance but only on the amount of texture memory consumed.

On the left, the quality is set to 0 and blocky shadow edge artifacts appear on the shadow, whereas on the right, the quality is set to 1 and the artifacts have disappeared.

lightBlendingReduction (SoSFFloat( C++ | Java | .NET ))

Used only for VARIANCE_SHADOW_MAP. In some cases, a halo may appears around shadows intersecting each others. Increasing this value will decrease this effect. Default value (0.1) is safe for most scenes.

minVariance (SoSFFloat( C++ | Java | .NET ))

Used only for VARIANCE_SHADOW_MAP. Increasing this value will decrease possible self-shadowing artifacts but will make shadows fade away. The default value (1.e-5) is safe for most scenes. Increasing the quality field is preferable before tweaking this value.

isShapeBefore (SoSFBool( C++ | Java | .NET ))

Indicates that there are shapes before this group. Setting this value to TRUE may slow down the rendering because it causes a depth buffer save at each frame.

shadowCachingEnabled (SoSFBool( C++ | Java | .NET ))

Indicates if a cache should be used for computing the shadows. Using a cache will improve rendering performance if the lights of your scene and your scene under this group node do not change.

intensity (SoSFFloat( C++ | Java | .NET ))

Specifies the intensity of the shadow. 0 is the lowest intensity (the shadow is invisible), 1 is the highest intensity (the shadow is solid black).

precision (SoSFFloat( C++ | Java | .NET ))

Specifies the precision of the shadow. 0 is the lowest precision, 1 is the highest precision. With lower precisions, some parts of the scene may be unlit where they should be lit, or vice versa. Increasing the precision will reduce performance.

On the left, a low precision is selected, on the right, a high precision. You can see in the left picture that the shadow of the cup’s handle is not complete.

visibilityRadius (SoSFFloat( C++ | Java | .NET )) visibilityFlag (SoSFEnum( C++ | Java ))

For large scenes, and typically for walk-through scenes where you can zoom in very close to the scene, some blocky shadow edge artifacts can still appear even if quality is set to 1.

In this case, one way to increase the shadowing quality (decrease the aliasing effects) is to specify a spherical region centered at the camera position within which the shadowing will be computed. The smaller the radius of this sphere, the better the shadow quality.

This radius equals:

  • the longest edge of the bounding box of the scene multiplied by visibilityRadius if visibilityFlag equals LONGEST_BBOX_EDGE_FACTOR.

  • visibilityRadius if visibilityFlag equals ABSOLUTE_RADIUS.

The above two pictures show the use of visibilityRadius.

On the left, the trees and the house are outside the sphere of visibility, so shadow casting is not applied on these objects. On the right, the eye is closer, so shadow casting of the trees and the house is visible. In others words, the shadow casting is only computed for 3D objects that are near the point of view.

The SoShadowStyle( C++ | Java | .NET ) property node is used to determine if subsequent shape nodes cast a shadow and/or can be shadowed.

The field SoShadowStyle::style is a bit mask combination of the following values:

  • NO_SHADOWING: Shapes neither cast a shadow nor can be shadowed,

  • CASTS_SHADOW: Shapes cast a shadow (but can’t be shadowed),

  • SHADOWED: Shapes can be shadowed (but don’t cast a shadow),

  • CASTS_SHADOW_AND_SHADOWED: Shapes cast a shadow and can be shadowed (default value).

[Tip]

You can set NO_SHADOWING for your 2D/3D text strings!

In Figure 9.9, “ Example of use of SoShadowStyle”, the cylinder can cast a shadow and can be shadowed (CASTS_SHADOW_AND_SHADOWED), the sphere only casts a shadow (CASTS_SHADOW), and the cube can only be shadowed (SHADOWED).


Only the VARIANCE_SHADOW_MAP method is available with custom shaders. In order to use shadows with user defined shaders, a set of functions are available for GLSL shaders under a SoShadowGroup( C++ | Java | .NET ):

  • void OivSetupShadowVertex(): Must be called in the vertex shader only.

The following functions are provided for use in fragment shaders only:

  • float OivComputeShadow(): Return a scalar in [0-1]. 0 means fragment is fully shadowed, 1 means no shadows.

  • void OivGenerateShadowMap(): Must be called during the shadowmap pass in order to create the shadowmap. During this pass, the shader must not write to gl_FragColor.

The following uniform is available in vertex and fragment GLSL shader:

  • uniform bool OivShadowPass: Is true if the shadowmap is currently being rendered.

The following code can be used as a skeleton for a custom shadowed shader:

The vertex shader:


C++
void OivSetupShadowVertex();
void main()
{
  ..userCode..
  //Needed for shadowing
  OivSetupShadowVertex();
}
    

The fragment shader:

// If true we are in shadowmap generation pass
uniform bool OivShadowPass;

//Generate the shadowmap
void OivGenerateShadowMap();

//Compute shadow for the current fragment
float OivComputeShadow();

void main()
{
  if ( !OivShadowPass )
  {
    ..compute fragment color here..
    // Define the final color
    gl_FragColor.xyz = fragColor.xyz * OivComputeShadow();
    gl_FragColor.w = fragColor.w;
  }
  else
  {
    //Output the shadowmap during the shadow map pass
    OivGenerateShadowMap();
  }
}

See OIVHOME/src/Inventor/examples/Features/Shaders/shadowShader for a complete example.

Depending on your hardware capabilities, shadow casting may not be available. Shadows should be supported on any graphics board that support OpenGL 1.4 or higher, but can also work with previous OpenGL versions if specific OpenGL extensions are available. The static method SoShadowGroup::isSupported will indicate if shadow casting is supported by your graphics board.

[Warning]

On some older graphics boards, e.g., GeForce2, you will need to set the environment variable OIV_FORCE_DUAL_TEX_SHADOWto 1.

[Warning]

On some older graphics boards, e.g., Rage 128, you will need to set SoShadowGroup::quality <= 0.25.