By chance, I found a gorgeous 3D animation of the earth in the sixth column of didi’s official website. I tried to use there.js to achieve the animation effect, but there are still some problems. This animation looks simple, but it also uses good drawing methods and calculations. It is also a good example to learn webGL.

Look at the example: http://39.106.166.212:8080/webgl/t4 (because it is write demo project, the content didn’t do more optimization, load for the first time to ride slowly, need to wait a few more seconds)

Lice is also a very difficult screenshot tool to use, and every time you intercept mid-crust ╰ ╭

First, the construction of 3D rendering scene

To draw a 3D program, you first need to add the renderer, the scene, the camera, and a light;

1. Renderer

Start by creating a renderer that takes the canvas element in the page,

The purpose of a renderer is to render the contents of a 3D scene into a page with the camera,

Finally, set the canvas background to white.

const renderer = new Three.WebGLRenderer({canvas: this.$refs.thr});
renderer.setClearColor(0x000000);
Copy the code

2,

A scene, as the name suggests, is to add a field to which you play (draw), and all the elements drawn after that are added to the scene;

cosnt scene = new Three.Scene();

3. Camera

The camera is like a person’s vision or what Angle to look at the scene, the position of the scene and the direction of the eye determines the content rendered to the page. Therefore, it is generally necessary to set two parameters: camera position, line of sight direction lookAt, and in WebGL, there are actually three groups of parameters: viewpoint, observation point, and up direction. Thresjs seems to default the Y-axis as up, using perspective cameras.

const camera = new THREE.PerspectiveCamera(45, 500 / 500, 1, 1500);
camera.position.set(100, 100, 1000);
camera.lookAt(new THREE.Vector3(0, 0, 0));
scene.add(this.camera);
Copy the code

4, lighting

There are THREE.HemisphereLight lights, which make the outdoor lighting more natural;

let light = new THREE.HemisphereLight(0xffffff); 
light.position.set(0, 0, 200); 
scene.add(light)
Copy the code

The above basic drawing elements have been added. Now we start to draw the contents of each geometry.

Geometry drawing has three parts: create geometry, create materials, add mesh model;

Ii. Mapping of the Earth

Threejs provides sphere drawing, we just need to create a sphere, material using texture map to add map;

I got the stickers from the official website

const geometry = new THREE.SphereGeometry(this.radius, 100, 100); // sphere const textureLoader = new three.textureLoader (); Const texture = textureLoader.load(require("@/assets/map.jpg"),texture => {let material = new THREE.MeshLambertMaterial({map: texture,transparent: true,}); let mesh = new THREE.Mesh(geometry, material); scene.add(mesh); });Copy the code

Since the image load is asynchronous, the material can be created only after the image load is complete.

Here we have a model of the earth, and then we have to make it rotate; (Xyz axis in the middle)

Threejs provides basic 3D transformation of geometry, directly use rotateY(angleChange) to set the rotation Angle around the Y axis (green axis) according to the time;

3. The drawing of spherical coordinate points

1. When drawing spherical position points, it is necessary to first look at the spherical coordinate system to determine the position of points. The coordinate directions in WebGL are inconsistent with the following figure, but the points are represented in the same way.

Any point on a sphere can be represented by r,θ,φ, or transformed into a rectangular coordinate system by the following formula

X = rsin theta cos phi.

Y = rsin theta sine phi.

Z = rcos theta

But in practice we also use latitude and longitude for the position of the Earth… Let me write a way to convert latitude and longitude.

Threejs provides the method three.math. degToRad to convert latitude and longitude to θ,φ.

GetPosition (longitude, latitude, radius = this.radius) {// Let lg = three.math.degtorad (longitude, latitude, radius = this.radius); let lt = THREE.Math.degToRad(latitude); Let temp = radius * math.cos (lt); let x = temp * Math.sin(lg); let y = radius * Math.sin(lt); let z = temp * Math.cos(lg); return new THREE.Vector3(x, y, z); },Copy the code

2. After knowing the representation method of the position, start to draw the point representing the position

According to the example position point is composed of point and a circle, we first draw a point and an arc in a two-dimensional plane, and move to the target coordinate position point by setting its position and rotation mode.

(You can also draw geometric spheres here to simulate this.)

(1) Point drawing

THREE.Shape is used to draw the Shape in a two-dimensional plane. Set its Shape as an arc to realize an origin.

let shapePoint = new THREE.Shape();
shapePoint.absarc(0, 0, r - 4, 0, 2 * Math.PI, false);
let arcGeometry = new THREE.ShapeGeometry(shapePoint);
let arcMaterial = new THREE.MeshBasicMaterial({ color: 0x008080 });
let point = new THREE.Mesh(arcGeometry, arcMaterial);
Copy the code

(2) Arc drawing

let geometryLine = new THREE.Geometry();
let arc = new THREE.ArcCurve(0, 0, r, 0, 2 * Math.PI);
let points = arc.getPoints(40);
geometryLine.setFromPoints(points);
let LineMateri = new THREE.LineBasicMaterial({ color: 0x20b2aa });
let line = new THREE.Line(geometryLine, LineMateri);
Copy the code

(3) Setting of position

position.set(pos.x, pos.y, pos.z);
Copy the code

(4) Rotate the two-dimensional graph to the sphere

The three-.spherical () method, which converts coordinate points into the offset Angle of the Spherical coordinate system;

let spherical = new THREE.Spherical();
spherical.setFromCartesianCoords(pos.x, pos.y, pos.z);
Copy the code

Set position point rotation

Point.rotateX(spherical.phi - Math.PI / 2);
Point.rotateY(spherical.theta);
Copy the code

The reason why we have -math.pi / 2 here is because when we first drew it, the plane was perpendicular to the y axis, and look at the picture below;

Next, draw the line between the two points of the sphere

The curve connecting the two points has to be above the sphere,

There can be countless curves between two points, so how to determine the curve, we need to choose appropriate parameters to determine;

The first is the second-order Bezier curve, taking the midpoint of two points as the control point, but if the connecting points are exactly located at the two symmetric ends of the sphere (the line between the two points is the diameter), the control point should be at infinity;

Therefore, third-order Bezier curves are considered to connect the two points of the sphere and the center of the sphere. A plane determined by the three points is shown as follows.

Link v1, v1, take the midpoint C, link OC, make a ray, take a point P in the ray, link v1p,v2p, take two points on v1 and v2 as control points;

Find the midpoint of two points

 getVCenter(v1, v2) {  let v = v1.add(v2);  return v.divideScalar(2); }
Copy the code

Gets the specified proportional position coordinates between two points

getLenVcetor(v1, v2, len) {   
    let v1v2Len = v1.distanceTo(v2);   
    return v1.lerp(v2, len / v1v2Len);
}
Copy the code

Get the Bezier control point

GetBezierPoint (v0, v3) {let Angle = (v0.angleto (v3) * 180)/math.pi; Let aLen = Angle * 2.5, hLen = Angle * 50; let p0 = new THREE.Vector3(0, 0, 0); // let rayLine = new three. Ray(p0, this.getvcenter (v0.clone(), v3.clone())); Let vtop = rayline.at (hLen/rayline.at (1).distanceto (p0), vtop); Let v1 = this.getLenvcetor (v0.clone(), vtop, aLen); let v2 = this.getLenVcetor(v3.clone(), vtop, aLen); return { v1: v1, v2: v2 }; },Copy the code

Draw bezier curves three times

drawLine(longitude, latitude, longitude2, latitude2) { let geometry = new THREE.Geometry(); // Declare a Geometry object Geometry let v0 = this.getPosition(longitude, latitude, this.radius); let v3 = this.getPosition(longitude2, latitude2, this.radius); let { v1, v2 } = this.getBezierPoint(v0, v3); // CubicBezierCurve3(v0, v1, v2, v3); let curvePoints = curve.getPoints(100); geometry.setFromPoints(curvePoints); let material = new THREE.LineBasicMaterial({ color: 0xff7e41 }); let line = new THREE.Line(geometry, material); this.group.add(line); this.sport(curvePoints); },Copy the code

Five, the trajectory of the ball

For the animation of the ball, we use the frame animation of three. The path can be directly used by the curve in the previous step.

1. Draw balls

drawSportPoint(position, name) { let box = new THREE.SphereGeometry(6, 6, 6); let material = new THREE.MeshLambertMaterial({ color: 0x00bfff }); // Let mesh = new THREE.Mesh(box, material); mesh.name = name; mesh.position.set(position.x, position.y, position.z); this.groupBall.add(mesh); this.group.add(this.groupBall); return mesh; },Copy the code

2. Get the ball moving

sport(curvePoints, index) { const Ball = this.drawSportPoint(curvePoints[0]); let arr = Array.from(Array(101), (v, k) => k); Let times = new Float32Array(arr); let posArr = []; curvePoints.forEach(elem => { posArr.push(elem.x, elem.y, elem.z); }); Float values = new Float32Array(posArr); float values = new Float32Array(posArr); Let posTrack = new THREE.KeyframeTrack("Ball. Position ", times, values); let duration = 101; let clip = new THREE.AnimationClip("default", duration, [posTrack]); this.mixer = new THREE.AnimationMixer(Ball); let AnimationAction = this.mixer.clipAction(clip); AnimationAction.timeScale = 20; AnimationAction.play(); },Copy the code

Add the trigger animation to requestAnimationFrame

this.mixer.update(this.clock.getDelta());
Copy the code