Cube map environmental texturing component

This component defines nodes for using cube map textures. Such textures generate a color based on a direction. They are one of the common methods for environment mapping (that is, simulation of mirror-like surfaces). ComposedCubeMapTexture and ImageCubeMapTexture allow loading a cube map texture from file(s). GeneratedCubeMapTexture allows to create and use an environment map capturing actual environment in your virtual 3D world, thus making true realtime mirror.

See also X3D specification of the Cube map environmental texturing component.

Water reflections by optimized GeneratedCubeMapTexture
Teapot with cube map reflections

Contents:

1. Demos

For demos and tests of these features, see the cube_environment_mapping subdirectory inside our VRML/X3D demo models.

2. Tutorial: How to make a mirror

The GeneratedCubeMapTexture node is a ready solution to simulate mirror-like surfaces. It should be coupled with texture coordinates to reflect in world space to produce a mirror effect in a really easy way.

  1. Choose a shape in your VRML/X3D model that should act like a mirror.

  2. As it's texture set GeneratedCubeMapTexture node.

    This texture will be automatically generated to represent the environment around the given shape (the shape itself is not rendered into this texture, it doesn't mirror itself). Set GeneratedCubeMapTexture.update to specify when this texture should be generated. Two sensible values are ALWAYS (if the world around is dynamic) or NEXT_FRAME_ONLY (if the world around is completely static).

    The texture is actually kept as six square 2D textures inside the graphic card. You can control the size of these squares by GeneratedCubeMapTexture.size: larger values mean better quality, but also worse speed.

    Note that if your shape already uses some texture, you will have to convert it's textures into MultiTexture node, with the 1st item containing the old texture, and the 2nd item containing the new GeneratedCubeMapTexture. See "Texturing" component for multi-texture documentation.

  3. As the texture coordinates set TextureCoordinateGenerator node with mode field set to WORLDSPACEREFLECTIONVECTOR.

    Note that if your shape already uses some texture coordinates, you will have to convert them into MultiTextureCoordinate, see notes above about MultiTexture.

    Note that in our engine all 3D geometry nodes have the texCoord field, so you can do this really with every shape. Even with the primitives like Box / Cone / Cylinder / Sphere.

  4. Cubemaps are great for mirrors on a curved object (like a sphere, or a teapot). To display mirrors on a flat surface, it is better to use Castle Game Engine extensions for mirrors on flat objects.

As an example, consider this teapot, with bold text to emphasize the mirror stuff:

Shape {
  appearance Appearance {
    material Material { }
    texture GeneratedCubeMapTexture {
      update "ALWAYS"
      size 512
    }
  }
  geometry Teapot {
    texCoord TextureCoordinateGenerator { mode "WORLDSPACEREFLECTIONVECTOR" }
  }
}

Place this in some interesting environment to see the nice mirror :) This approach works best for curvy surfaces (perfectly flat surfaces usually look bad unless you use really large size), and only if the mirror object is small compared to the surrounding enviroment (as there are are no self-reflections).

3. Supported nodes

  • ComposedCubeMapTexture(Pascal API: TComposedCubeMapTextureNode)

    Orientation notes: The images are expected to be oriented just like for the VRML/X3D Background node. This is suggested by the drawing in the spec, although the spec doesn't specify exact orientation of the images. We use Background node orientation, as this is definitely sensible. See Background node spec, paragraph with words "... when viewed from the origin looking down the negative Z-axis ...".

    Size notes: Texture size for cube maps is automatically adjusted to be power of two, square, and within OpenGL limits (GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB). So your textures do not necessarily have to be provided with required sizes (if you don't mind a little quality loss because of scaling).

    You still must provide equally sized images for all six cube map sides. Our engine makes sure to scale them to be square and power-of-two, but we currently do not attempt to make all six textures equal — so you have to provide textures already satisfying this.

    We add textureProperties field to the ComposedCubeMapTexture node, intended for TextureProperties child, working just like in other texture nodes (you can use it to set minification / magnification filter, anisotropy and such). Although X3D 3.2 specification doesn't mention this, it seems natural, and instantreality also uses this. We support for cube maps all normal texture filterings, including mipmaps.

  • ImageCubeMapTexture(Pascal API: TImageCubeMapTextureNode)

    DDS file format to specify cube maps (including S3TC compressed cube maps) is supported.

  • GeneratedCubeMapTexture(Pascal API: TGeneratedCubeMapTextureNode)

    Texture is rendered from the middle 3D point of bounding box of the shape using this texture. You cannot reUSE the same GeneratedCubeMapTexture node for various shapes (as then we would not know from which shape to generate).

    The texture before generation (e.g. if you have update = 'NONE' at the start) has pink color (RGB(255, 0, 255)), so you can easily recognize it.

    All the generated textures are rendered in a separate pass before actual rendering, and during this generation other shapes use existing values of their textures. This means that recursive mirrors, i.e. mirror that can be seen in another mirror, works to any level (each frame rendered uses textures generated in the previous frame). You can see recursive mirrors in our VRML/X3D demo models (see cube_environment_mapping/cubemap_generated_recursive.x3dv cube_environment_mapping/cubemap_generated_in_dynamic_world.x3dv).

    Provided size will automatically be adjusted to be power of two, and within OpenGL limits (GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB).

    Current player camera doesn't affect how cube map is generated. This is good, it means that generated texture is usable for as long as the scene remains unchanged, and doesn't have to be regenerated each time when the player moves.

    • When update = "ALWAYS", this optimization is automatically used under the hood. Texture is internally not updated every frame — when we know nothing visible changed on the scene, we do not regenerate the texture (since it would be generated the same). Note that using the headlight, or any other geometry/light following the player, makes this optimization less effective (as then every camera move changes the look of the scene, so rendered textures have to be regenerated on every camera move).

    • This also means that generated cube map texture is similar to static (from ImageCubeMapTexture and ComposedCubeMapTexture), and you usually want to use "WORLDSPACEREFLECTIONVECTOR" texture generation to simulate mirror. When using cube maps with GLSL shaders, this often forces the need to transform directions from eye-space to world-space, you can obtain appropriate matrix easily by Viewpoint.cameraRotationInverseMatrix output event.