With the popularity of the concept of meta-universe, 3D rendering related technologies are frequently mentioned, and Three.js is a framework encapsulated by WebGL API to simplify the development of 3D scenes, which is a good starting point for 3D. Today, we will introduce Three.js.

We implemented a petal rain effect based on three.js.

The foundation of the Three. Js

Three. Js is used to render a 3D scene, in which there are many objects, such as cubes, cylinders, rings, cones and other kinds of Geometry, as well as basic objects such as Points and Line Sprite. How do you manage all these objects?

It’s carried by a Scene, and all objects are added to the Scene.

So there is an API like this:

 const scene = new THREE.Scene();
 
 scene.add(xxx);
 scene.add(yyy);
Copy the code

Of course, objects can be grouped into groups. Objects in the Group can be managed uniformly and then added to the Scene.

const scene = new THREE.Scene();

const group = new THREE.Group();

group.add(xxx);
group.add(yyy);

scene.add(group);
Copy the code

This concept of scenes, objects, groups, there are similar apis in many game engines, and everyone manages it this way.

In addition to Geometry, points, lines, and planes, you can add objects to the Scene with auxiliary tools such as the Coordinate System Tool (AxisHelper). In fact, these tools are also enclosed with geometry, points, lines and planes, but are added to the Scene temporarily as tools.

const axisHelper = new THREE.AxisHelper(max);
scene.add(axisHelper)
Copy the code

How do you render the scene and the various objects in the scene?

Call Renderer, the class responsible for rendering various objects in the Scene.

But there is still a question, how can the three-dimensional scene be rendered on a two-dimensional screen?

If you look at the world in three dimensions from a point, or if you look at the world in three dimensions from a plane, you see two dimensions.

The first is called perspective and the second is called orthogonality.

To generate a two-dimensional image, just like a Camera, so this concept is called a Camera.

PerspectiveCamera and OrthographicCamera are PerspectiveCamera and OrthographicCamera in three. js, which correspond to the above two 3d to 2d modes respectively.

The parameters of these two cameras are quite many, but it is also quite easy to understand:

new Three.PerspectiveCamera( fov, aspect, near, far )
new Three.OrthographicCamera( left, right, top, bottom, near, far )
Copy the code

First look at the perspective camera, it wants to see the three-dimensional world, that has to have a nearest and farthest two positions, and then from a point in the past will have a field of view Angle, see the picture has an aspect ratio.

That’s why PerspectCamera has near, far, FOV, and aspect.

The parameters of an orthogonal camera are the same thing, but because you’re not looking at a point, you’re projecting from a plane, you don’t have angles, you have up, down, left, right, four plane positions.

The top, bottom, and left positions of orthogonal cameras are not arbitrary, and the ratio should be the same as the width to height ratio of the picture, so it is generally calculated as follows:

const width = window.innerWidth; const height = window.innerHeight; // window width/height const k = width/height; // display range of 3d scene const s = 200; // OrthographicCamera = new THREE.OrthographicCamera(-s * k, s * k, s, 1, 1000); // OrthographicCamera = new THREE.Copy the code

In the above orthogonal camera parameters, the distance can be set to 1 and 1000, the top and bottom can be set to 200, and the left and right can be calculated according to the aspect ratio. This is the range of two-dimensional images that the camera can see.

So you have all the objects in the Scene, you have the Camera, and you can use the Renderer to render the picture.

const renderer = new THREE.WebGLRenderer();
// Set the render area size
renderer.setSize(width, height)

renderer.render(scene, camera)
Copy the code

However, instead of rendering a single frame, animation effects will be rendered frame-by-frame using the requestAnimationFrame API.

function render() {
    renderer.render(scene, camera)

    requestAnimationFrame(render)
}
render();
Copy the code

This is the general process of three. js: There are various objects such as Geometry, points, lines and planes, and auxiliary tools in the Scene. Objects can also be grouped, and then two-dimensional images can be set through orthogonal or perspective cameras, and Renderer will be used to render them. For animation effects, requestAnimationFrame is used to render frames.

Now let’s do a rain of petals.

Petal rain implementation

First we need to create the objects in the Scene, namely the various petals, and this needs to display a plane, you can use Sprite.

Sprite stands for Sprite, and in three.js, it’s a two-dimensional surface that always faces the camera.

Let’s just texture the Sprite with petals.

Let’s first prepare some petals, like this:

There are many petals, we generate 400, add them to the group of petals, and add them to the scene:

const scene = new THREE.Scene();
/** ** petals grouped */
const petal = new THREE.Group();

function create() {
    var texture1 = new THREE.TextureLoader().load("img/h1.png");
    var texture2 = new THREE.TextureLoader().load("img/h2.png");
    var texture3 = new THREE.TextureLoader().load("img/h3.png");
    var texture4 = new THREE.TextureLoader().load("img/h4.png");
    var texture5 = new THREE.TextureLoader().load("img/h5.png");
    var imageList = [texture1, texture2, texture3, texture4, texture5];

    for (let i = 0; i < 400; i++) {
        var spriteMaterial = new THREE.SpriteMaterial({
            map: imageList[Math.floor(Math.random() * imageList.length)],// Set the Sprite texture map
        });
        var sprite = new THREE.Sprite(spriteMaterial);
        petal.add(sprite);

        sprite.scale.set(40.50.1); 
        sprite.position.set(2000 * (Math.random() - 0.5), 2000 * Math.random(), 0)
    }
    scene.add(petal)
}

create();
Copy the code

Each of the 400 Sprites was randomly textured with different petals, then scaled down, and then randomly positioned in the scene.

Let’s add the coordinate aid to the Scene to look at the coordinates:

const axisHelper = new THREE.AxisHelper(1000);
scene.add(axisHelper)
Copy the code

Red is the X-axis, increasing to the right, and green is the Y-axis, increasing up. We’re not going to use the z axis right now.

So, according to the code, the petal’s x range is random -1000 to 1000, and its y range is 0 to 2000.

Then, we create an orthogonal camera:

const width = window.innerWidth;
const height = window.innerHeight;
// Window width ratio
const k = width / height;
// The upper and lower range of the 3d scene display
const s = 200;
const camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1.1000);
Copy the code

Set the camera position and orientation:

camera.position.set(0.200.500)
camera.lookAt(scene.position)
Copy the code

When we create the camera, we specify a two-dimensional display range, and the camera can be anywhere within that range.

Then create the renderer, set the size and background color, and insert the canvas element into the DOM.

const renderer = new THREE.WebGLRenderer();
// Set the render area size
renderer.setSize(width, height)
// Set the background color
renderer.setClearColor(0xFFFFFF.1)
// Insert the canvas in the body element
document.body.appendChild(renderer.domElement)
Copy the code

After that, you can render it continuously frame by frame using the requestAnimation.

function render() {
    petal.children.forEach(sprite= > {
        sprite.position.y -= 1;
        sprite.position.x += 0.5;
        if (sprite.position.y < -400) {
            sprite.position.y = 800;
        }
        if (sprite.position.x > 1000) {
            sprite.position.x = -1000}}); renderer.render(scene, camera) requestAnimationFrame(render) }Copy the code

Before each rendering, we change the position of the petals to create a falling effect, and if we fall out of range, we move to the top and start falling again, so that is a continuous rain of petals.

The complete code is as follows:

<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>petals</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
    </style>
    <script src="js/three.min.js"></script>
</head>
<body>
<script>

    const scene = new THREE.Scene();
    /** ** petals grouped */
    const petal = new THREE.Group();

    const width = window.innerWidth;
    const height = window.innerHeight;
    // Window width ratio
    const k = width / height;
    // The upper and lower range of the 3d scene display
    const s = 200;
    const camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1.1000);

    const renderer = new THREE.WebGLRenderer();

    function create() {
        // Set the camera position
        camera.position.set(0.200.500)
        camera.lookAt(scene.position)
    
        // Set the render area size
        renderer.setSize(width, height)
        // Set the background color
        renderer.setClearColor(0xFFFFFF.1)
        // Insert the canvas in the body element
        document.body.appendChild(renderer.domElement)

        // const axisHelper = new THREE.AxisHelper(1000);
        // scene.add(axisHelper)

        var textureTree1 = new THREE.TextureLoader().load("img/h1.png");
        var textureTree2 = new THREE.TextureLoader().load("img/h2.png");
        var textureTree3 = new THREE.TextureLoader().load("img/h3.png");
        var textureTree4 = new THREE.TextureLoader().load("img/h4.png");
        var textureTree5 = new THREE.TextureLoader().load("img/h5.png");
        var imageList = [textureTree1, textureTree2, textureTree3, textureTree4, textureTree5];

        for (let i = 0; i < 400; i++) {
            var spriteMaterial = new THREE.SpriteMaterial({
                map: imageList[Math.floor(Math.random() * imageList.length)],// Set the Sprite texture map
            });
            var sprite = new THREE.Sprite(spriteMaterial);
            petal.add(sprite);

            sprite.scale.set(40.50.1); 
            sprite.position.set(2000 * (Math.random() - 0.5), 2000 * Math.random(), 0)
        }
        scene.add(petal)
    }


    function render() {
        petal.children.forEach(sprite= > {
            sprite.position.y -= 1;
            sprite.position.x += 0.5;
            if (sprite.position.y < -400) {
                sprite.position.y = 800;
            }
            if (sprite.position.x > 1000) {
                sprite.position.x = -1000}}); renderer.render(scene, camera) requestAnimationFrame(render) } create() render()</script>
</body>
</html>
Copy the code

conclusion

Three.js is a framework for simplifying 3D rendering. It provides an API of Scene, which can contain all kinds of renderable objects: cubes, cones and other kinds of Geometry, points, lines, planes, coordinate systems and other auxiliary tools. These objects can also be managed through groups.

Sence needs to specify a camera for rendering, which can be divided into PerspectiveCamera viewed from point and OrthographicCamera projected from plane. Only by understanding their principles can we understand Camera parameters.

Renderer is then used to render it, and if there is an animation, requestAnimationFrame is used to render it frame by frame.

This is the rough rendering flow of three.js.

Then we implemented a petal rain case. I used an object like Sprite, which is a flat surface that always faces the camera, which is perfect for this effect.

Of course, there are a lot of things in Three.js. This article is just a step into the door. Later we will go further and make more interesting 3D scenes and effects.