Reflections and Refractions

Using reflection textures can simulate mirror like material and refraction textures can simulate looking through glass or water.

Reflection

Reflections are created using the relectionTexture property of a material. A first use is in creating a sky using a skybox

This sets the relectionTexture to a CubeTexture and the coordinatesMode of the relectionTexture to SKYBOX_Mode as in

skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("PATH TO IMAGES FOLDER/COMMON PART OF NAMES", scene);
skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;

CubeTexture

By default six jpeg images are passed to a CubeTexture. The images are named in this form, commonPart_px.jpg, commonPart_nx.jpg, commonPart_py.jpg, commonPart_ny.jpg, commonPart_pz.jpg, commonPart_nz.jpg corresponding to the positions shown below.

When doing this for a skybox the box created is given a large size (1000 in the skybox example above) but CubeTexture can be used with any size box and is one way of applying different textures to each side of a cube. Notice that as we are dealing with a small box and we are viewing it from the outside backFaceCulling can be set to true. This is not possible when the camera is inside the large skybox since in terms of rendering the sky at the back will be still behind the fron portion and will not be rendered should backFaceCulling = true. However we still need to use reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE.

var box = BABYLON.MeshBuilder.CreateBox("Box", {}, scene);
var boxMaterial = new BABYLON.StandardMaterial("mat", scene);
boxMaterial.backFaceCulling = true;
boxMaterial.reflectionTexture = new BABYLON.CubeTexture("https://babylonjsguide.github.io/img/cubeSide", scene);
boxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
boxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
boxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
box.material = boxMaterial;
Different Reflections On Each Face

From Babylon.js v2.4 it is also possible to use High Dynamic Range Cube Textures

Reflecting on Skybox and a shape

Using different coordinatesMode with different shapes will reflect the skybox in the shape

Box and CUBIC_MODE ReflectionGround and PLANAR_MODE ReflectionSphere and PLANAR_MODE Reflection

Using local cubemap mode

Starting with Babylon.js v3.2, you can now use local cubemap mode when using cubemaps (with CUBIC_MODE). Please read this article, to get a precise understanding of what local cubemaps are.

CubeTexture and RenderTargetTexture (when in cube mode, like when used with probes for instance) can be switched to local mode by setting property named boundingBoxSize (by default cubemaps are in infinite mode):

material.reflectionTexture = new BABYLON.CubeTexture("/textures/TropicalSunnyDay", scene);
material.reflectionTexture.boundingBoxSize = new BABYLON.Vector3(100, 100, 100);

You can also specify a property named boundingBoxPosition if you want to define the center of the bounding box used for the cubemap (The place where the camera was set when generating the cubemap).

You can find an demo of local cubemaps here: Local Cubemap Example

HDRCubeTexture

High Dynamic Range (HDR) images are panoramic images that cover an entire field of vision.

Below is an HDR image of a room

Replace the following line

skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("PATH TO IMAGES FOLDER/COMMON PART OF NAMES", scene);

with

skyboxMaterial.reflectionTexture = new BABYLON.HDRCubeTexture("PATH TO HDR IMAGE", scene);
HDR Skybox

EquiRectangularCubeTexture

Equirectangular images are browser-canvas supported images like jpeg, png, and many more. A list of image support on browsers can be found here.

Below is an equirectangular image of a shop

Replace any of the following lines

skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("PATH TO IMAGES FOLDER/COMMON PART OF NAMES", scene);
skyboxMaterial.reflectionTexture = new BABYLON.HDRCubeTexture("PATH TO HDR IMAGE", scene);

with

cubemapDesiredSize = 512; // The cubemap desired size (the more it increases the longer the generation will be)
skyboxMaterial.reflectionTexture = new BABYLON.EquiRectangularCubeTexture("PATH TO EQUIRECTANGULAR IMAGE", scene, cubemapDesiredSize);
Equirectangular SkyboxEquirectangular Image On A SphereBoth Combined

Spherical Reflection Texture

Not only can a cube texture can be applied to a sphere so can a plane single image.

The above image was applied to each of four spheres, one as a diffuse texture and the other three with reflectionTexture but different coordinatesMode. The resuls are below.

Diffuse Texture
SPHERICAL_MODE
PLANAR_MODE
PROJECTION_MODE
Coordinate Modes Example

Mirrors

So far reflections have been of images, using MirrorTexture obects within the scene can be reflected as in a mirror. This is simulated by by setting the reflectionTexture to a MirrorTexture and applying it to a flat surface.

Mirrors

A real mirror is made of two parts glass and a reflected surface applied to the glass and a mirror simulated within BJS also contains to parts; a flat surface and a reflector. (For a reflective surface such as metal or still water - think metal plus shine and water plus air boundary).

In BJS the flat surface is a ground mesh or a plane mesh and the reflector is a Mathematical Plane which is infinite and lies on top of the flat mesh and reflects where the two overlap.

With a real mirror it is easy to tell if you are standing in front of it or behind it. For a BJS mirror an object is in front of the mirror if the normals of the flat surface point towards the object.

Constructing the Mirror Reflector

The flat surface should be constructed first from a ground or plane mesh. BJS can then construct the reflector using the position and normal of the flat surface. Since the reflection is on the opposite side of the mirror to the object being reflected the normal for reflection is in the opposite direction to that of the flat surface. For example a mesh of a plane created in BJS has a normal vector (0, 0, -1) at the time of creation and so the reflected normal will be (0, 0, 1).

The next thing to note is that renderings of meshes take place by applying transformations, the worldMatrix, to the original mesh values. It is therefore necessary the get this worldMatrix and apply it to the data from the flat surface in order to obtain the current and actual 3D data in world space.

An example of creating a 'glass' flat surface and obtaining the reflector is

var glass = BABYLON.MeshBuilder.CreatePlane("glass", {width: 5, height: 5}, scene);
//Position and Rotate flat surface
glass.position = new BABYLON.Vector3(0, 0, 4);
glass.rotation = new BABYLON.Vector3(Math.PI/4, Math.PI/6, Math.PI/8);
//Ensure working with new values for flat surface by computing and obtaining its worldMatrix
glass.computeWorldMatrix(true);
var glass_worldMatrix = glass.getWorldMatrix();
//Obtain normals for plane and assign one of them as the normal
var glass_vertexData = glass.getVerticesData("normal");
var glassNormal = new BABYLON.Vector3(glass_vertexData[0], glass_vertexData[1], glass_vertexData[2]);
//Use worldMatrix to transform normal into its current world value
glassNormal = new BABYLON.Vector3.TransformNormal(glassNormal, glass_worldMatrix)
//Create reflector using the position and reflected normal of the flat surface
var reflector = new BABYLON.Plane.FromPositionAndNormal(glass.position, glassNormal.scale(-1));

Constructing the Mirror

Once the reflector is obtained a MirrorTexture is made that can be applied to the flat surface.

var mirrorMaterial = new BABYLON.StandardMaterial("MirrorMat", scene);
mirrorMaterial.reflectionTexture = new BABYLON.MirrorTexture("mirror", 512, scene, true);
mirrorMaterial.reflectionTexture.mirrorPlane = reflector;
mirrorMaterial.reflectionTexture.renderList = [sphere1, sphere2];

A MirrorTexture has four parameters: name, size of the rendering buffer (should be a power of 2, the larger the number the better image quality but performance deteriorates); scene and and optional parameter, default value false, that will generate a MIP map when set to true. This increases quality durinng scaling.

The mirrorPlane is set to the constructed reflector. It is possible to directly set the mirrorPlane by directly using a BABYLON.Plane(a, b, c, d) where a, b and c give the plane normal vector (a, b, c) and d is a scalar displacement from the mirrorPlane to the origin. However in all but the very simplest of situations it is more straight forward to use the method above.

The renderList is an array of the meshes to be reflected in the mirror.

Finally the mirrorMaterial can be applied to the glass.

glass.material = mirrorMaterial;

Blurring the Reflection

MirrorTexture can support blurred rendering with either:

  • adaptiveBlurKernel: setting this value to something other than 0 will blur the texture with a specified kernel (the bigger the blurrier). The value will be adapted to the viewport size.
  • blurKernel: same as adaptiveBlurKernel property but the value is not adapted to viewport size.
Reflection Blur

In this case an object behind glass or under water for example can have its position and size changed by the refraction of light.

Refraction

Refraction is also achieved by taking a flat surface such as a plane or disc and adding, this this case, a refraction material applied to a flat mesh. The difference is that the object that is to be refracted is placed behind the flat surface, that is the normals of the mesh all point away from the object and the refracted normals are in the same direction.

The method used above to obtain the reflectionPlane could be used if necessary though in this case the normal of the flat surface is not reversed.

var refractor = new BABYLON.Plane.FromPositionAndNormal(glass.position, glassNormal);

The following example, however, uses a vertical plane for the mesh at the origin and so it is straight forward to obtain the normal (0, 0, -1) and displacement, 0, for the refractor plane.

//Create flat surface
var surface = BABYLON.MeshBuilder.CreatePlane("surface", {width: 15, height: 15}, scene);
//Create the refraction material
var refractionMaterial = new BABYLON.StandardMaterial("refraction", scene);
refractionMaterial.diffuseColor = new BABYLON.Color3(1, 1, 1);
refractionMaterial.refractionTexture = new BABYLON.RefractionTexture("refraction", 1024, scene, true);
refractionMaterial.refractionTexture.refractionPlane = new BABYLON.Plane(0, 0, -1, 0);
refractionMaterial.refractionTexture.renderList = [sphere];
refractionMaterial.refractionTexture.depth = 5;
refractionMaterial.indexOfRefraction = 0.5;
surface.material = refractionMaterial;

Two new parameters are apparent depth a property of the refractionTexture and indexOfRefraction a property of the refraction material/

The two examples below show the effect of changing these.

Note in both examples the surfaces are transparent so that the actual position of the sphere can be identified. It is the refracted sphere that changes psoition as the parameters are changed.

Refraction DepthIndex Of Refraction