11.7 - Overlays

WebGL 3D graphics are always rendered into a HTML canvas element. The canvas is always a rectangle. That said, it is possible to render arbitrary areas of a canvas to make a rendering appear non-rectangular. And it is possible to combine the 2D elements of a HTML page and a WebGL 3D rendering in interesting ways. This lesson demonstrates how to do the following:

Overlays using CSS Properties

Cascading Style Sheets (CSS) define a “stack ordering” property called the z-index property. Elements with a z-index value are ordered from smallest to largest and they are rendered in that order. This is identical to the “painter’s algorithm” we discussed in lesson 11.2. Because the elements are rendered in a particular order, the last element rendered will cover the previously rendered elements. In this way you can render 3D graphics behind, or in-front-of, HTML elements.

The z-index property only affects elements that are rendered on top of each other. Therefore, the z-index ordering only works on elements whose position property is absolute, relative, or fixed, which are defined as follows:

  • absolute: The element is positioned relative to its first positioned (not static) ancestor element.
  • relative: The element is positioned relative to its normal position.
  • fixed: The element is positioned relative to the browser window.

If an element’s position property is static, initial, or inherit, which are defined as follows, the z-index property has no affect.

  • static: Elements render in order, as they appear in the document flow. (Default)
  • initial: Use its default value.
  • inherit: Inherits this property from its parent element.

The following examples are created by setting appropriate position, width, height, top, bottom, left and right properties to make elements overlap and then setting the rendering order using the z-index property. The alpha component of the background color is also used to implement transparency.

Positioning HTML elements can be very tricky. The following examples use this strategy:

  • Create a div “container” to hold the overlapping elements. Set its position property to relative and its height property to the the total height of the overlapping HTML elements. This allows elements after this “container” to follow the normal flow of the web page (which is left-to-right, top-to-bottom).
  • Create your overlapping elements and make their position property to be absolute or relative to their parent element. Set each element’s width, height, top, bottom, left and right properties appropriately.

Refer to the CSS properties defined for each of the HTML demo programs below.

3D Graphics Behind Text

To create 3D graphics behind HTML elements, position a canvas and the HTML elements in the same relative position on a web page. Then give the canvas a z-index value less than the HTML element’s z-index value.

You can experiment with this canvas that renders "under" a HTML element.

When you create an overlay like this demo, you need to understand the following:

  • In all previous code demos, event callbacks to convert mouse drags into model rotations have been assigned to the canvas that contains the rendering. Since the canvas is totally covered by a div element, the events are never activated. You need to add the mouse events that manipulate the canvas to the top HTML element of the overlay.
  • The alpha value of the color used to clear the background can be used to blending the canvas with the web page’s background. If you set the alpha value of the “clear color” to 0.0, the background of the scene will be the color of the web page’s background. This allows you to render non-rectangular scenes to a web page.
  • Using the browser’s development tools, carefully examine the CSS style sheet for this example program. The styles are what makes all this work. (Go to the “Network tab”, reload the web page, and open the overlay_behind.css file.

3D Graphics in Front of Text

To render 3D graphics over the top of HTML elements, you simply change the z-index property of the overlapping elements so that the canvas has the largest z value – thus making it be rendered last.

You can experiment with this canvas that renders over a HTML element.

The style sheet, overlay_in_front.css is the critical part of this demo.

Multiple Overlapping Canvases

Given the overlaying examples we have just discussed, you could use these techniques to render one or more smaller canvases inside a larger canvas. This might be advantageous for the layout of a game where you want to display a small “world map” in the corner of your “game play” window.

You can experiment with this canvas overlay demo program.

The style sheet, overlay_canvases.css is the critical part of this demo.

Full Screen Rendering

Functionality to allow an HTML element to be rendered in full screen mode has been developed but it has not yet been standardized. The proposed standard can be found at Fullscreen API. Most browsers have implemented full screen functionality, but they use variations of the function names described in the proposed standard.

Fullscreen requests must be called from within an event handler. If they are called anywhere else they will be denied. This means that a user must initiate fullscreen mode by some user action. A user can always exit from fullscreen mode by hitting the escape (ESC) key.

You typically want to redefine the size of your HTML elements when fullscreen mode is initiated and when it is terminated. There is only one event associated with fullscreen mode, the fullscreenchange event. You can establish a callback function to make appropriate changes like this:

/** ---------------------------------------------------------------------
 * Set an event handler for any changes to "full screen mode."
 * @param id String The ID of the HTML element that is to fill the screen.
 */
self.setFullScreenChangeCallback = function ( id ) {
  // Add an event callback for changes to full screen mode.
  // When the standard is approved and universally implemented, only the
  // first callback will be needed.
  $( '#' + id ).on( 'fullscreenchange', self.onFullScreenChange )
               .on( 'webkitfullscreenchange', self.onFullScreenChange )
               .on( 'mozfullscreenchange', self.onFullScreenChange )
               .on( 'MSFullscreenChange', self.onFullScreenChange );
};

The event handler needs to recognize which mode the screen is in and then take appropriate actions. Here is an example event handler:

/** ---------------------------------------------------------------------
 * Processing for a change in the full screen mode. This resets the size
 * of the affected HTML elements and re-renders the scene.
 */
self.onFullScreenChange = function (event) {

  if (document.fullscreenElement ||
      document.webkitIsFullScreen ||
      document.mozFullScreen ||
      document.msFullscreenElement) {
    // We are in full screen mode, so make the elements larger
    self.updateElementSizes( Math.min(screen.width, screen.height) );
  } else {
    // We have gone back to normal mode, so make the elements smaller.
    self.updateElementSizes(400);
  }
  self.render();
};

To enter full screen mode a user event must initiate the action. Here is a typical function that could be called on a user event to start full screen mode:

/** ---------------------------------------------------------------------
 * Initate full screen mode, where the identified HTML element takes up
 * the entire screen.
 * @param event Event object, The event that initiated the request
 * @param id String The ID of the element that will be made full screen.
 */
self.goToFullScreen = function (event, id) {

  // Get the element that will take up the entire screen
  var element = document.getElementById(id);

  // Make sure the element is allowed to go full screen.
  if (!element.fullscreenElement &&
      !element.mozFullScreenElement &&
      !element.webkitFullscreenElement &&
      !element.msFullscreenElement) {

    // Enter full screen mode
    if (element.requestFullscreen) {
      element.requestFullscreen();
    } else if (element.msRequestFullscreen) {
      element.msRequestFullscreen();
    } else if (element.mozRequestFullScreen) {
      element.mozRequestFullScreen();
    } else if (element.webkitRequestFullscreen) {
      element.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
    }
  } else {
    window.console.log("The element " + id + " can't go into full screen mode.");
  }
};

The above examples will become much simpler and cleaner when all of the browsers standardize on a single set of naming conventions (and jQuery adds support for full screen mode.)

This is a good time to discuss the relationship between a canvas’s size on the screen and it’s associated WebGL frame buffer. (This is crazy stuff, but this is how it works.) A canvas element can have two different sizes:

  • a HTML size, defined by an element’s width and height properties, and
  • a CSS (cascading style sheets) size, defined by an element’s width and height style properties.

Here is an example canvas that has two different sizes:

<canvas width="100" height="100" style="width:200px; height:200px;"> </canvas>

If you don’t specify the CSS properties of width and height then the HTML properties determine the element’s size. However, the CSS properties always override the HTML properties. Therefore, this canvas will have a size of 200x200 pixels on the screen. WebGL always creates its frame buffer size from the HTML properties. In the example above, the canvas on the screen will be 200x200 pixels, but the WebGL frame buffer will be 100x100 pixels. When the frame buffer is copied to the screen it will be stretched to fill the canvas, but the image will be blurry because each pixel in the frame buffer will be used to color 4 pixels in the canvas. If you are changing the size of a canvas you should change the CSS properties and then copy their values into the canvas’ properties. The CSS properties are called clientWidth and clientHeight. Here is an example function that updates the sizes of a “containing” element and its two overlaid canvas elements:

/** ---------------------------------------------------------------------
 * Reset the size of the affected elements after a fullscreen event.
 * @param size Number The new size for the HTML elements.
 */
self.updateElementSizes = function (size) {

  var new_css_properties = { width: size, height: size };

  // Change the size of the "container" element.
  $('#W4_my_overlay').css( new_css_properties );

  // Change the CSS size of the main canvas window.
  $('#W4_main_canvas').css( new_css_properties );

  // The CSS (readonly) properties of the element have now been changed.
  // Now copy the clientWidth and clientHeight into the canvas's
  // width and height so the frame buffer will be re-sized to the current
  // window resolution.
  var canvas = document.getElementById('W4_main_canvas');
  canvas.width = canvas.clientWidth;
  canvas.height = canvas.clientHeight;

  // Change both the size of the element and the size of the WebGL frame buffer.
  var small_size = Math.round(size * 0.25);
  var left_offset = Math.round(size * 0.75);
  $('#W4_small_canvas').css( {width: small_size, height: small_size, left: left_offset} );
  canvas = document.getElementById('W4_small_canvas');
  canvas.width = canvas.clientWidth;
  canvas.height = canvas.clientHeight;
};

You can experiment with full screen mode using this demo program.

Glossary

overlay
The rendering of more than one element in the same location on a web page.
stack ordering
The back-to-front ordering of a set of HTML elements.
z-index
A value that specifies the position of an element in a “stack ordering”. The ordering is from smallest to largest. The element with the largest z-index is rendered last.
full screen mode
Render an HTML element (and all of its children) to a window that covers the entire screen. All window borders and title bars disappear. You can always exit full screen mode by hitting the ESC key.
Next Section - 12.1 - WebGL Shader Language