WebGL Background Bump Mapping

May 2012

Screenshot of WebGL Background Bump Mapping

An texture image and WebGL are used to create a real-time bump mapping effect.

What is it?

Background textures have always been fairly flat and static things. I set out to increase their (apparent) depth.

Back when I created this, it wasn’t very practical to use, because a majority of browsers and devices couldn’t run WebGL very well.

This project featured WebGL, bump mapping, and the Phong reflection model to enhance a websites background.

Screenshot of WebGL Background Bump Mapping

Any tile-able image can be used to create a 3D effect.


The website’s background (0,0)x(viewportX,viewportY) is mapped to a square (-1,-1)x(1,1). Right about where your face is, a virtual light is pointed towards the screen. In practice, it seems that hanging this light from 0..2 units above the screen yields good results. At 2 units, the light provides a fairly even coverage of the square. Less than this, it seems to provide for an easy vignette effect. You provide a background (diffuse) color, and your image is consulted for the texture of the surface.

Subtle tiling textures can be found at Subtle Patterns.

Graphics Model

The image is represented in the XY plane. The normal is simply (0,0,1). This allows us to easily place the light somewhere in the (0,0,+z) area, and use the mouse to control the X and Y positions of the light. The Phong reflection model is used for shading. The specular component is messed up, as you’ll see if you increase rho. Since the viewport is defined to be between (-1,-1)x(1,1), we can easily reason about the position of the light in this space. (-1,-1) being the bottom right, (1,1) being the top right. An offset can be applied to the mouse’s modifier, allowing us to position the light off-screen but still have the mouse affect the shading; this can make the effect subtler.

Bump mapping

Forward differencing (via 3 texture lookups) in X (1,0,0) and Y (0,1,0) is performed on the provided pattern, and a magnitude is obtained. The step size can be controlled by delta_s and delta_t, and may need to be adjusted as the texture is scaled for better results. Currently, only the blue channel is consulted. This magnitude, modulated by a user specified scaling factor (bump_scale), is used to perturb the normals in the X and Y directions (or s and t).

Viewport Adjustments

It is very rare that a browser will have a viewport aspect ratio of 1:1. The canvas is stretched to the browser viewport dimensions, but internally has a square of aspect 1:1. As such, we’d notice stretching on any textures due to this aspect ratio difference. So, on draw/resize, the texture coordinates are modulated by the browser’s viewport aspect ratio. This results in square textures displaying as they were intended.

WebGL and non-POT textures

As I was experimenting with the textures, I noticed that a lot of the tiling textures that exist in the wild are non-power-of-two (non-POT) in dimension. Furthermore, almost none of the textures are perfectly square.

WebGL only currently supports power-of-two pixel dimensions, so the image is redrawn using a temporary 2D canvas if it isn’t POT. This is fine for getting the texture to simply display, but there are still rescaling problems (mapping an arbitrary rectangle into a perfect square squashes the texture). Ultimately, this is due to a deficiency in the simplified model I constructed, but it is easily circumvented: the texture coordinates are further modulated by the texture’s aspect ratio. This results in textures of any aspect ratios rendering as the author intended.

Texture Coordinate Scaling

As the dimensions of the images used for textures vary, so must their scaling factor -- some of the patterns you may try might be outright invisible due to their scaling factor. This is also performed with a straight-forward multiplication to the texture coordinates; gl.REPEAT in the texture wrap parameters handles the rest.

All texture coordinate modulations are done via vertex shader uniforms, rather than modulating and re-uploading the texture coordinates. This was done to keep the implementation simple. Since there are only two triangles, I don’t anticipate this as being particularly inefficient.

Limitations & Concerns

The light is less circular the further away the window viewport is from 1:1. Smart semi-automatic setting of texture coordinate scale and bump scale would be nice, but for now it is a manual tweaking process.