tutorials

Reflections and Refractions


Mirrors and Glass

Using reflection textures can simulate mirror like material and recfraction 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.

CubeTexture Positions

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("http://babylonjsguide.github.io/img/tutorials/Materials/tutorials/Materials/tutorials/Materials/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;

Playground Example of Different Faces -


From BS 2.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

Playground Example of Box and CUBIC_MODE -



Playground Example of Ground and PLANAR_MODE -


Playground Example of Sphere and PLANAR_MODE -

HDRCubeTexture

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

Below is an HDR image of a room

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);

Playground Example of HDR Skybox -


Spherical Reflection Texture

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

Squares

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.

Reflecion on Spheres

Diffuse Texture SPHERICAL_MODE
PLANAR_MODE PROJECTION_MODE

Playground Example of Coordinates Modes -


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.

Playground Example of 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;

Refraction

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

Playground example of 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.

Playground example of changes in refraction depth from 0 to 50 -



Playground example of changes in index of refraction from 0.1 to 1.5 -