Threejs realizes DiDi official website homepage earth animation

Let's look at the example first: http://39.106.166.212:8080/webgl/t4 (Because it is a project that writes dome, there is a lot of content without optimization, the first loading will be slower, and you need to wait a few more seconds)

(The screenshot tool lice is also very difficult to use, and it takes half of the screenshot every time ╮(╯﹏╰)╭)

1. Construction of 3d rendering scene

To draw a 3d program, you first need to add elements such as renderer, scene, camera, and add a light here;

1. Renderer

First create a renderer, the parameter is the canvas element in the page,

The role of the renderer is to render the content of the 3d scene combined with the camera to the page.

Finally set the canvas background to white.

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

2. Scene

The scene, as the name suggests, is to add a venue where you play (draw), and all the elements drawn later must be added to the scene;

cosnt scene = new Three.Scene();

3. Camera

The camera is like human vision or what angle to look at the scene from. The position of the scene and the direction of the line of sight determine the content rendered to the page. Therefore, it is generally necessary to set two parameters, the camera position position and the line of sight direction lookAt. In webgl, three sets of parameters are actually required: the viewpoint, the observation point, and the upward direction. In thresjs, it seems that the default Y axis is up, and the perspective camera is used here.

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

4. Lighting

The THREE.HemisphereLight light is used here, which can be closer to the natural outdoor lighting effect;

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

Above our basic drawing elements have been added, let's start to draw the content of each geometry;

There are three parts to geometry drawing: creating geometry, creating materials, and adding mesh models;

The drawing of the earth

threejs provides the drawing of the sphere, we only need to create a sphere, and the material uses the texture map to add the map;

The texture image resource is what I found from the official website

const geometry = new THREE.SphereGeometry(this.radius, 100, 100); //sphere
const textureLoader = new THREE.TextureLoader(); //Create texture map
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);
});

Since the image loading is asynchronous, it is necessary to wait for the image loading to complete before creating the material;

Here we have created a model of the earth, and then let it rotate; (the middle is the xyz coordinate axis)

threejs provides basic 3d transformation of geometry, directly use rotateY(angleChange) to set its rotation angle around the y-axis (green axis) according to time;

3. Drawing of spherical coordinate points

1. When drawing a spherical position point, you need to look at the spherical coordinate system first to determine the position of the point. The coordinate direction in webgl is inconsistent with the following figure, but the representation of the point is consistent;

Any point on the sphere can be represented by r, θ, φ, and can also be transformed into the Cartesian coordinate system by the following formula

x=rsinθcosφ.
y=rsinθsinφ.
z=rcosθ

But in practice, we generally also use latitude and longitude to represent the location of the earth. . . The following is a method of converting latitude and longitude to coordinates.

threejs provides the THREE.Math.degToRad method to convert longitude and latitude into θ, φ. The conversion method is as follows. Here, for the convenience of later use, I directly return a 3-dimensional vector;

getPosition(longitude, latitude, radius = this.radius) {      //Longitude, latitude converted to coordinates
  let lg = THREE.Math.degToRad(longitude);      
  let lt = THREE.Math.degToRad(latitude);      //Get x, y, z coordinates
  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);  
}

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 a point and a ring, we first draw a point and an arc in a two-dimensional plane, and then move to the target coordinate position point by setting its position and rotation;

(You can also draw geometric small spheres to simulate)

(1) Drawing of points

THREE.Shape is used to draw the shape in the 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);

(2) Drawing of arc

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

(3) Location setting

position.set(pos.x, pos.y, pos.z);

(4) Two-dimensional graphics are rotated to a spherical surface
THREE.Spherical() method, which can convert the coordinate point into the offset angle of the coordinate point converted back to the spherical coordinate system;

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

set position point rotation

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

The reason why - Math.PI / 2 is here is because when we first draw, the plane is the plane perpendicular to the y-axis, refer to the following picture;

4. Then draw a line connecting the two points on the spherical surface

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

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

The first thing to think about is the second-order Bezier curve, taking the midpoint of the two points as the control point, but if the linking two points are just located at the two symmetrical endpoints of the sphere (the line connecting the two points is the diameter), the control point needs to be in infinity far away;

Therefore, consider using a third-order Bezier curve to connect two points on the sphere and the center of the sphere. A plane determined by the three points is as shown in the figure below.

Link v1 v1, take the midpoint c, link oc, make a ray, take a point p on the ray, link v1p,v2p, take two points on v1,v2 as control points;

Find the midpoint of two points

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

Get the specified scale position coordinates between two points

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

Get bezier control points

getBezierPoint(v0, v3) {          
 let angle = (v0.angleTo(v3) * 180) / Math.PI; //0 ~ Math.PI // Calculate vector angle
 let aLen = angle * 2.5,        
     hLen = angle * angle * 50;      
 let p0 = new THREE.Vector3(0, 0, 0);      //normal vector
 let rayLine = new THREE.Ray(p0, this.getVCenter(v0.clone(), v3.clone()));      //vertex coordinates
 let vtop = rayLine.at(hLen / rayLine.at(1).distanceTo(p0), vtop); //  Location       
 //Control point coordinates
 let v1 = this.getLenVcetor(v0.clone(), vtop, aLen);      
 let v2 = this.getLenVcetor(v3.clone(), vtop, aLen);     
 return {        
  v1: v1,        
  v2: v2      
 };    
},

draw cubic bezier curve

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);   //3D Quadratic Bezier Curve
  let curve = new THREE.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);    
},

5. The trajectory of the ball

For the animation of the ball, we use the frame animation of three, and the path can directly use the curve in the previous step;

1. Draw the ball

drawSportPoint(position, name) {    
  let box = new THREE.SphereGeometry(6, 6, 6);    
  let material = new THREE.MeshLambertMaterial({      
   color: 0x00bfff    
  });      //material object
  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;
}

2. Let the ball move

sport(curvePoints, index) {      
  const Ball = this.drawSportPoint(curvePoints[0]);      
  let arr = Array.from(Array(101), (v, k) => k);      //generate a time series
  let times = new Float32Array(arr);
  let posArr = [];      
  curvePoints.forEach(elem => {        
    posArr.push(elem.x, elem.y, elem.z);      
  });      //Create a series of position coordinates corresponding to the time series
  let values = new Float32Array(posArr);  //Create key frame data of a frame animation, the position sequence on the curve corresponds to a time series
  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();
}

3. Add trigger animation in requestAnimationFrame

this.mixer.update(this.clock.getDelta());

Original link: https://www.cnblogs.com/pangys/p/13276936.html

Posted by gabrielserban on Mon, 30 May 2022 07:46:12 +0530