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;
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 ReflectionUsing 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);
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);
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 |
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.
MirrorsA 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 surfaceglass.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 worldMatrixglass.computeWorldMatrix(true);var glass_worldMatrix = glass.getWorldMatrix();//Obtain normals for plane and assign one of them as the normalvar 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 valueglassNormal = new BABYLON.Vector3.TransformNormal(glassNormal, glass_worldMatrix)//Create reflector using the position and reflected normal of the flat surfacevar 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.
In this case an object behind glass or under water for example can have its position and size changed by the refraction of light.
RefractionRefraction 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 surfacevar surface = BABYLON.MeshBuilder.CreatePlane("surface", {width: 15, height: 15}, scene);//Create the refraction materialvar 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