Moment For Technology

Write a reflective ball with three.js

Posted on Aug. 12, 2023, 4:10 p.m. by Alex Hussain
Category: The front end Tag: The front end webgl three.js


In this article, we will use three.js to simulate a reflective ball. The effect is as follows:

Front knowledge

This is the third part of the three-js series. The first two are:

Write a rain animation with three.js

Write a small scene with three.js

The explanation of the basic knowledge of three.js is put in the first part: write a rain animation with three.js, which can be viewed. The following cases will not be repeated.

Geometry and materials

In the basic knowledge, we understand the Renderer (Renderer), Scene (Scene), Camera (Camera), coordinate system, objects, lighting and other basic use, here we mainly discuss the object related.

In three.js, when you create an object, you need to pass two parameters, the Geometry and the Material.

const object = new THREE.Mesh(geometry, material)
Copy the code


Geometry's function is to store information about the vertices of an object that determines its shape. To draw an object in space, using WebGL requires the programmer to specify the position of each vertex, whereas in three.js, you can directly declare geometric shapes such as cube, plane, sphere, cylinder, tetrahedron, octahedron, etc. You just need to pass in the parameters required to define these geometric shapes according to the document.

Some examples:

Const floorGeometry = new THREE.PlaneGeometry(800, 1000) Const sphereGeometry = new three.sphereGeometry (350, 50, 50) Const doorGeometry = new THREE.BoxGeometry(100,210,40) const doorGeometry = new THREE.BoxGeometry(100,210,40) const doorGeometry = new THREECopy the code

The material

The material is like the skin of the object, determining the appearance of the geometry. For example, a skin defines whether a geometry looks metallic, transparent, or appears as a wireframe.

The application of materials is very flexible, and many cool 3D effects are due to materials. There are many types of materials, such as:

  • MeshBasicMaterial: The color of the rendered object will always be the color of the material, and will not react to the light. No light or shadow effects will occur due to the light.
const geometry = new THREE.BoxGeometry(); Const Material = new three.meshBasicMaterial ({// create Basic material color: 0x00ff00 }) const cube = new THREE.Mesh(geometry, material); scene.add(cube);Copy the code
  • MeshLambertMaterial: Only consider diffuse reflection, without considering the effect of specular reflection, not applicable to metal, glass and other objects.If the object usesMeshLambertMaterial, you must add lighting to the scene, otherwise the object will not be displayed. Also, the final display color of the object is determined by the materialColor parametersAnd the light color.Here are some light types:

Below is a combination of directionLight and Lambert material, and you can see that each face of the cube has a different degree of light and shade.

const material = new THREE.MeshLambertMaterial({ color: 0x00ff00 }); // Create a Lambert material const directionLight = new three.directionAllight (0xffFFFF) Parameter is the color of light directionLight. Position. Set (10,10,10) / / define the direction of parallel light scene. The add (directionLight) / / add parallel light to the scenarioCopy the code
  • MeshPhongMaterial: Considering the effect of specular reflection, suitable for metal, glass, etc. In the same parallel light environment, set the cube's material toMeshPhongMaterialAs you can see, the cube creates a specular reflection of the light.
const material = new THREE.MeshPhongMaterial({ color: 0x00ff00, shininess: 100 // Determines the lightiness of the Phong material. When the shininess value is 0, it behaves like the MeshLambertMaterial});Copy the code

These are some basic materials to create, more materials and material parameters refer to the official documentation.


Previously, we passed in the color value when we created the material, so the material was created with a single color. More often, however, we need to generate materials based on images.

Let's create a cube with images pasted on all six sides. We use MeshBasicMaterial instead of light and shadow

const geometry = new THREE.BoxGeometry(); Const Texture = new three.textureLoader ().load('./images/pic1.jpg ' THREE.MeshBasicMaterial({map: texture}) const cube = new three.mesh (geometry, material);Copy the code

Note that the texture loader is usedTextureLoaderIf the image file is not in the same domain as the current HTML and cross-domain is not allowed, the loading of the image file will fail.

Instead of opening HTML by accessing local files (file:// / XXX), you can set up a server using live-server or webpack-dev-server.

How to build a simple server with live-server

This project will use webpack-dev-server to build a server, there is a need to see the source code of children's shoes.

Each cube faces a different picture

To attach a different picture to each cube, first, prepare six pictures.Use the texture loaderTextureLoaderLoad 6 images and set them to 6 textures:

Const geometry = new three. BoxGeometry() const materials = [] // i  6; Const texture = new three.textureLoader ().load('.. /.. /images/reflection-sphere/${i+1}.jpg` ); Push (new three.meshBasicMaterial ({map: texture})); } const cube = new three.mesh (geometry, materials)Copy the code

The effect is as follows :(red represents the X-axis, green represents the Y-axis, blue represents the z-axis, generated using AxesHelper)

As you can see, the materials in the material array map the faces of the cube in the order of x-positive, x-negative, y-positive, y-negative, z-positive, and z-negative.

The most basic use of a texture is as a map that is added to a material (a texture map). When you create a Mesh with such a material, the color of the object comes from the texture. Texture-based materials can better simulate the real world than pure colors.

Achieve 360 panorama

Going back to our little example, our first step is to implement a 360 panorama.

BoxGeometry scheme

How a panorama works: Create a container, usually a sphere or cube, attach an image to its inner surface, and place the camera in the center of the container.

Compared to the example above, let's do two steps:

  1. Inner surface mapping. We just need togeometrythescaleThe material is applied to the inner surface of the geometry.
Const geometry = new three.boxgeometry (10,10,10) // set scale.x // geometry. Scale (1, -1, 1) 1) Setting scale.y will cause the screen to be upside down. // geometry. Scale (1, 1, -1) // geometry. Scale. i  6; i ++) { const texture = new TextureLoader().load( `.. /.. /images/reflection-sphere/${i+1}.jpg` ); materials.push( new MeshBasicMaterial({ map: texture} )); } const cube = new Mesh(geometry, materials) cube.position.set(0, 0, 0)Copy the code
  1. Place the camera in the center of the container.
Camera. The position. The set (0, 0, 0.01) camera. LookAt (0, 0)Copy the code

Scene. The background

Another way to realize panorama is to use THREE.CubeTextureLoader to load six images, and then use the loaded image texture as the background of the whole Scene Scene, so as to form a 360-degree panorama.

// Prepare 6 images. The images to implement the panorama also need to meet the following order: // urls = ['posx.jpg', 'negx.jpg', 'posy.jpg', 'negy.jpg', 'posz. JPG ', 'posz. JPG ', 'posz. JPG ', 'posz. JPG ', 'posz. JPG ', 'posz. JPG ', 'posz. JPG ', 'posz. JPG ', 'negz.jpg'] // instantiate CubeTextureLoader const Loader = new three.cubetExtureLoader () // Load 6 images const cubeMap = loader.setPath('.. /.. /images/reflection- Sphere /').load(urls)Copy the code

As you can see, it has the same effect as the first option.

The application scenarios of the two schemes: The first scheme is based on BoxGeometry, which can better control the addition of objects at specific coordinate positions in the panorama. It is suitable for indoor scenes such as 3D house viewing, and can better meet the requirements of object clicking and selecting in the panorama.

In the second scheme, it is difficult to create an object and bind coordinates to a certain place in the panorama, so it is more suitable for a pure environmental panorama. But as long as the coordinates in the panorama are not involved, this scheme gives the whole scene and camera more freedom to operate. In our case, we will implement the panorama in the second way.

Achieving reflective balls

Add a sphere

Const sphereGeometry = new THREE.SphereGeometry(20, 30, // create a const sphereMaterial = new three.meshBasicMaterial ({color: Const sphere = new three.mesh (sphereGeometry, sphereMaterial) // Set sphere.position. Set (0, 0, 0) 0) // Add sphere to scene.add(sphere)Copy the code

Texture for reflection

Calculating specular reflection is very CPU intensive, and ray tracing algorithms are often used. In three.js you can still achieve the specular effect, just make a fake one. You can disguise specular reflection by creating a texture of the object's environment and applying it to a specific object.


Above, we created the textures with color and map respectively. Here we will use envMap (environment Map). EnvMap literally means the environment around the object. For example, if you are rendering a reflective object, the environment around the object will affect the rendering.

Here, we assign the panoramic texture to envMap to simulate the specular reflection of the surrounding environment.

In the previous 360 panorama implementation, we used three.cubetextureLoader to load 6 images. Three.js will combine these images together to create a seamless texture. The above panorama uses this texture as the background of the scene, and here we use this texture as the environment map (envMap) of the sphere.

Const sphereGeometry = new THREE.SphereGeometry(20, 30, 30) // make the image texture as the sphere environment map const sphereMaterial = new three.meshBasicMaterial ({envMap: cubeMap }) const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial) sphere.position.set(0, 0, 0) scene.add(sphere)Copy the code

This simulation only reflects the specified background, and if there are other objects in the scene, they will not appear in the reflective ball.


To reflect anything in real time, you can use cubeCamera.

CubeCamera constructs a cubeCamera containing six PerspectiveCameras and renders the scene to a WebGLCubeRenderTarget.

That is, we can use cubeCamera to take photos of objects in the scene in real time, and then use those live photos to create textures. Using these textures as the sphere's envMap (environment Map) simulates real time reflection.

/ / is going to create a texture object, some parameters of the defined target texture const cubeRenderTarget = new THREE. WebGLCubeRenderTarget (128, {format: RGBFormat, generateMipmaps: true, minFilter: LinearMipmapLinearFilter }); // create cubeCamera //1: the distance near the shear plane; 1000: distance to the far shear plane; Const cubeCamera = new three.cubecamera (1, 1000, cubeRenderTarget) cubeCamera.position.set(0, 0, 0) scene.add(cubeCamera)Copy the code

Next, use the generated texture as the envMap of the sphere:

const sphereMaterial = new THREE.MeshBasicMaterial({ envMap: }) const sphereGeometry = new THREE.SphereGeometry(20, 30, 30) const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial) phere.position.set(0, 0, 0) scene.add(sphere)Copy the code
About (Moment For Technology) is a global community with thousands techies from across the global hang out!Passionate technologists, be it gadget freaks, tech enthusiasts, coders, technopreneurs, or CIOs, you would find them all here.