18

Regarding from getting started with three.js to making a 3d earth (part 6: decorate the earth, manage operations, etc.)

In this chapter, let us add some decorations to our lonely earth line (the solar system will be drawn later).

1. Starry sky background

Our earth background has always been black, this time we will use the starry sky picture as the earth background.

The background of the starry sky should also be 3D and enclose the earth. You can imagine the starry sky as a texture on the inside of a sphere, and our'camera' is inside the sphere.

Find a background image of the starry sky on the Internet. The width of the image should be larger than the height, otherwise the display is not clear:

image.png

/cc_map_3d_pro/src/config/earth.config.js

export default {
    r: 80, // 半径
    bg: require("../assets/images/星空.jpg"), // 背景图 (新增)
    earthBg: require("../assets/images/地图加文字.png"), // 贴图路径
}

In the life cycle function, we add a new initialization background function

mounted() {
    // ...
    this.initBg();
  },

What we have to do here is to draw a big sphere that wraps our earth and camera, and set its texture inside.

    initBg() {
      // 加载星空纹理
      const texture = this.textureLoader.load(envConifg.bg);
      // 生成球体
      const sphereGeometry = new THREE.SphereGeometry(1000, 50, 50);
      // 调转球体正反
      sphereGeometry.scale(-1, 1, 1);
      // 赋予纹理贴图
      const sphereMaterial = new THREE.MeshBasicMaterial({ map: texture });
      // 生成几何球体
      this.sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
      // 放入场景内
      this.scene.add(this.sphere);
    },
Precautions
  1. new THREE.SphereGeometry(1000, 50, 50) first parameter of 060e3025e998ed is the radius of the sphere. This radius must not be greater than our point, otherwise the sphere will not be visible.
  2. The radius of the sphere should not be too small, otherwise there will be a star close to the earth, and then we will try to draw the solar system. The sphere is too small to be drawn. Below I will show the effect of setting it too small.
  3. sphereGeometry.scale(-1, 1, 1) You can think of it as flipping a ball from the inside out.

Different angles of normal effect:
image.png

image.png

The size of the outer sphere exceeds the far view:

image.png

The size of the outer sphere is equal to the radius of the earth:
image.png

2. Earth's light transmittance

The above renderings have a display problem, that is, our map is hollow, and we can see through one side of the other side. We want to make the earth look more like an entity, we now need to put a sphere on the earth Inside, the transparency of the earth is controlled by controlling the transparency of this sphere.

    initInside() {
      const sphereGeometry = new THREE.SphereGeometry(envConifg.r - 1, 50, 50);
      const sphereMaterial = new THREE.MeshBasicMaterial({
        color: this.bgColor,
        opacity: 0.9,
        transparent: true,
      });
      this.InsideSphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
      this.scene.add(this.InsideSphere);
    },
  1. Note that you need to set transparent: true to set the transparency.
  2. The reason why the radius of the inner ball is set to envConifg.r - 1 is because it is afraid that it will cover the line of our country.
  3. If you want to change the color of the inner ball, you can use the this.InsideSphere.material.color.set(this.bgColor) method.

Look at the two effects:
image.png

image.png

3. Earth Halo ( spirit)

This special material will always face the camera , that is, no matter what angle we turn to look at the model, it will be facing our screen, and the sprite will not cast any shadows.

new THREE.Sprite is similar in nature to new THREE.BoxGeometry , except that it will create a sprite geometry that always faces the screen.

THREE.SpriteMaterial a material that uses Sprite. THREE.Sprite is a pair with 060e3025e99c02. You can adjust the color, transparency, etc.

First prepare a halo image similar to the following:
image.png

initSprite() {
   const texture = this.textureLoader.load(envConifg.haloBg);
   const spriteMaterial = new THREE.SpriteMaterial({
     map: texture,
     transparent: true,
     opacity: 0.7,
   });
   const sprite = new THREE.Sprite(spriteMaterial);
   sprite.scale.set(envConifg.r * Math.PI, envConifg.r * Math.PI, 1);
   this.scene.add(sprite);
 },
  1. As always, the introduction of'texture mapping'.
  2. But he has to use THREE.SpriteMaterial and THREE.Sprite to assign texture maps.
  3. Set sprite.scale because the sprite map is generally very small, so it needs to be increased proportionally. This number may not be π , input according to your own actual situation.

image.png

image.png

For example, the sprite can be used in the aiming star of shooting games. For example, in the 3d game, the character will put his name on his head. If this name is not facing our screen, it will definitely be unclear.

Four. ps modify the color of the sprite map

If you feel that my halo is not good-looking, you can open ps modify it yourself:

Import pictures

image.png

Click replace the color

image.png

Choose your favorite color

image.png

Five. Earth RBI

Usually we need to make some marks on the earth, and these marks are large and small, different colors, and different shapes. The important thing is that this figure needs to be parallel to the radius line projected from the center of the earth, and it can be completely displayed on the surface of the earth.

Prepare a dot map, preferably white, so that we can give it other colors in the future:
image.png

We add a markSpot method to the earth component. This method supports the processing of multiple and single objects, receiving arrays or objects, and calling this method ref

    markSpot(obj) {
      if (obj instanceof Array) {
        obj.forEach((item) => {
          this.object.add(spot(item));
        });
      } else {
        this.object.add(spot(obj));
      }
    },
  1. this.object is a new THREE.Object3D generated by 060e3025e99fdc, which can store multiple Mesh as a group to form a whole.
  2. spot what we will implement next.
  3. The received obj is a configuration item, which includes the configuration of the dotted color, size, transparency, shape, etc.

Write like this when using the component:

 <cc-map ref="map"></cc-map>
// ...
initMarks() {
   const arr = [
     {
       longitude: 116.2,
       latitude: 39.56,
       color: "red",
     },
     {
       longitude: 76.2,
       latitude: 49.56,
       color: "blue",
     },
    ];
   this.$refs.map.markSpot(arr);
 },
Start writing management methods

Some default attributes of /cc_map_3d_pro/src/utils/sport.config.js

const config = {
    size: 7,
    opacity: .8,
    color: 'yellow',
    url: require('../assets/images/打点.png')
}


export default (options) => {
    return { ...config, ...options }
}

/cc_map_3d_pro/src/utils/spot.js , first import the configuration items and set the default values.

import * as THREE from "three";
import envConifg from '../config/earth.config';
import lon2xyz from './lon2xyz';
import mergeConfig from './sport.config';
const geometry = new THREE.PlaneBufferGeometry(1, 1);
const textureLoader = new THREE.TextureLoader();

export default function spot(options) {
    const { longitude, latitude, color, opacity, size, url } = mergeConfig(options);
    const texture = textureLoader.load(url);
    const material = new THREE.MeshBasicMaterial({
        color,
        opacity,
        map: texture,
        transparent: true,
    });
    const mesh = new THREE.Mesh(geometry, material);
    const coord = lon2xyz(envConifg.r * 1.01, longitude, latitude)
    mesh.scale.set(size, size, size);
    mesh.position.set(coord.x, coord.y, coord.z);
    return mesh;
}
  1. We set the default configuration properties and merged with the configuration passed in by the user.
  2. Convert the incoming longitude and latitude into the Cartesian coordinate system x, y, z value.
  3. The size is achieved by zooming in and out of graphics.
  4. THREE.PlaneBufferGeometry(1, 1) generates a plane geometry. The parameters inside are width and height. The reason why the width and height are not used to manipulate the size of the picture is because the setting here is not as flexible scale
  5. When obtaining the latitude and longitude, envConifg.r * 1.01 is because it is afraid that it will coincide with the line on the earth.

The effect is as follows:

image.png

Flip dot

Although the position is correct, the angle is definitely not good. Now we have to calculate how many degrees it needs to rotate to be perpendicular to the radius line. Here we begin to involve some mathematical knowledge.

Step 1: normalize normalization

For example, there are two line segments starting from the center of a circle, no matter how long the two line segments are, the degree of the angle between the two will not change. Therefore, when calculating some proportions or angles, if the length of the data is not Affect the calculation result, then we will return it to the processing before calculating.

What normalization does is to turn your vector into a vector with a length of 1 . For example, if you have a three-dimensional vector x, y, z length, width and height are 060e3025e9a339, it will become x*x + y*y + z*z = 1

  console.log(new THREE.Vector3(10, 10, 10))
  console.log(new THREE.Vector3(1, 1, 1).normalize())

image.png

image.png

As shown in the figure, red is x, y, z values of 060e3025e9a3e7 are all 6 , which are normalized into a vector represented by a blue line segment.

Step 2: normal of the XOY plane
"Normal line (normal line) refers to a straight line that is always perpendicular to a plane

At present, our plane RBI default is XOY on the plane, so it's normals can be understood as the vertical is z-axis, for example, is (0, 0, z) this line, z is no relationship between how much you can write such new THREE.Vector3(0, 0, 999).normalize() , but It is recommended to write new THREE.Vector3(0, 0, 1) directly to save some calculation performance.

Step 3: Use quaternion to flip the plane

image.png

I can't explain the mathematical concept of quaternion, but I can give you a simple understanding. It is used to calculate the coordinates of a certain point after rotating'c degrees' around a certain vector. His concept is similar to complex number (i*i = -1) Very similar, the quaternion can be written as i*i = j*j = k*k = i*j*k = -1 in geometry, and a set of formulas for the rotated coordinates can be obtained through mathematical calculations.

Use quaternion to set the rotation angle quaternion.setFromUnitVectors('vector 1','vector 2') parameters need to be normalized, vector 1 rotates to the direction vector 2 requires a rotation angle of n , then Turn the target n degrees.

Just now we learned that the normal of (0, 0, 1) , and the coordinate from the center of the circle to the point is (x, y, z) , then we control its normal rotation so that the normal (x, y, z) vector, then the (x, y, z) vector will be the same vertical. Dot the plane , so that the dotted graphics will be tangent to the

    const coordVec3 = new THREE.Vector3(coord.x, coord.y, coord.z).normalize();
    const meshNormal = new THREE.Vector3(0, 0, 1);
    mesh.quaternion.setFromUnitVectors(meshNormal, coordVec3);

image.png

image.png

All codes
import * as THREE from "three";
import envConifg from '../config/earth.config';
import lon2xyz from './lon2xyz';
import mergeConfig from './sport.config';
const geometry = new THREE.PlaneBufferGeometry(1, 1);
const textureLoader = new THREE.TextureLoader();

export default function spot(options) {
    const { longitude, latitude, color, opacity, size, url } = mergeConfig(options);
    const texture = textureLoader.load(url);
    const material = new THREE.MeshBasicMaterial({
        color,
        opacity,
        map: texture,
        transparent: true,
    });
    const mesh = new THREE.Mesh(geometry, material);
    const coord = lon2xyz(envConifg.r * 1.01, longitude, latitude)
    mesh.scale.set(size, size, size);
    mesh.position.set(coord.x, coord.y, coord.z);
    const coordVec3 = new THREE.Vector3(coord.x, coord.y, coord.z).normalize();
    const meshNormal = new THREE.Vector3(0, 0, 1);
    mesh.quaternion.setFromUnitVectors(meshNormal, coordVec3);
    return mesh;
}
Just write a timer and keep changing the size and color of the dots, and you can make a dynamic dot effect. In this later chapter, we will unify the style when we talk about animation.

6. The Earth's Beam of Light

Sometimes we need to light up a beam of light on a certain coordinate on the earth, and the height of the beam of light indicates the density of assets here, or the sales volume of commodities.

Cone THREE.CylinderGeometry
  const geometry = new THREE.CylinderGeometry(1.5, 2, 5, 100, 100);
  const material = new THREE.MeshBasicMaterial({
      color: 'red'
  })
  const mesh = new THREE.Mesh(geometry, material);
  1. CylinderGeometry first parameter of is 160e3025e9adc1. The radius of the circle is the top of the cone. Setting it to 0 means a sharp awl.
  2. CylinderGeometry first parameter of , the radius of the lower circle is the base of the cone.
  3. The third parameter is the height of the cone.
  4. The number of segments around the side of the fourth cylinder. The default is 8.
  5. The number of segments along the height of the fifth cylinder side. The default value is 1.
  6. Note that the default centerline of the cylinder is on the y axis, which is useful when using quaternion flipping.

image.png

Encapsulated as a function

/cc_map_3d_pro/src/utils/column.config.js

const config = {
    size: 7,
    opacity: .8,
    color: 'yellow',
}


export default (options) => {
    return { ...config, ...options }
}

/cc_map_3d_pro/src/utils/column.js

import * as THREE from "three";
import envConifg from '../config/earth.config';
import lon2xyz from './lon2xyz';
import mergeConfig from './column.config';

export default function column(options) {
    const { longitude, latitude, color, opacity, size, url } = mergeConfig(options);
    const material = new THREE.MeshBasicMaterial({
        color,
        opacity,
        transparent: true,
        side: THREE.DoubleSide,
    });
    const coord = lon2xyz(envConifg.r * 1.01, longitude, latitude)
    const coordVec3 = new THREE.Vector3(coord.x, coord.y, coord.z).normalize();
    const geometry = new THREE.CylinderGeometry(0, 3, size);
    const mesh = new THREE.Mesh(geometry, material);
    return mesh
}

image.png

Quaternion again

Due to the characteristics of the cone, we need to make the center height of the cone coincide with the spherical vector, so the normalized vector of the cone can be selected as (0, 1, 0) :

mesh.quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), coordVec3);

Effect picture:

image.png

image.png

All codes
import * as THREE from "three";
import envConifg from '../config/earth.config';
import lon2xyz from './lon2xyz';
import mergeConfig from './column.config';

export default function column(options) {
    const { longitude, latitude, color, opacity, size, url } = mergeConfig(options);
    const material = new THREE.MeshBasicMaterial({
        color,
        opacity,
        transparent: true,
        side: THREE.DoubleSide,
    });
    const coord = lon2xyz(envConifg.r * 1.01, longitude, latitude)
    const coordVec3 = new THREE.Vector3(coord.x, coord.y, coord.z).normalize();
    const geometry = new THREE.CylinderGeometry(0, 3, size);
    const mesh = new THREE.Mesh(geometry, material);
    mesh.position.set(coord.x, coord.y, coord.z);
    mesh.quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), coordVec3);

    return mesh
}
Just write a timer and keep changing the color and height of the column, you can make a dynamic cone flashing effect. This later chapter will focus on the animation, and then unify the style.
The earth flight line needs to be discussed separately later, because the knowledge involved is a bit difficult and requires detailed planning.

end

In the next article, we are going to talk about how to distinguish each country. When hovering the mouse, a prompt box for the current country can appear. Although there are many mathematical concepts in it, don't be afraid, I will start to understand these concepts in detail. The process is depicted, I hope to progress with you.


lulu_up
5.7k 声望6.9k 粉丝

自信自律, 终身学习, 创业者