From getting started with Three.js to making a 3D Earth (Part 6: Decorating the Earth, Dotting, etc.)

In this chapter, let’s add some decoration to our lonely Earth line (the solar system will be painted later).

Starry sky background

Our Earth has always been a dark background, and this time we’re going to have a picture of the stars as the Earth’s background.

The starry sky background is also 3D, and the earth is wrapped inside it. Think of the starry sky as a tile on the inside of a sphere where our ‘camera’ is located.

Go online to find a starry sky background picture, the picture to the width of some larger than the height, otherwise the display is not clear:

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

Export default {r: 80, // radius bg: require(".. /assets/images/ star.jpg "), // EarthBg: require(".. /assets/images/ map with text. PNG "), // Map path}

Within the lifecycle function we added the initialize background function

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

What we’re going to do here is draw a big ball, wrap our Earth and camera around it, and set the texture inside.

InitBg () {// Load star texture const texture = this.textureLoader. Load (envconifg.bg); // Const SphereGeometry = new Three.SphereGeometry(1000, 50, 50); // SphereGeometry. Scale (-1, 1, 1); // Add a texture to const SphereMaterial = new Three.MeshBasicMaterial ({map: Texture}); // This. Sphere = new Three.Mesh(SphereGeometry, Spherematerial); This.scene.add (this.sphere); // Add this.scene.add(this.sphere); },
Matters needing attention
  1. new THREE.SphereGeometry(1000, 50, 50)The first parameter is the radius of the sphere, which must be no larger than our radiusFar view“, or you won’t be able to see the ball.
  2. The radius of the sphere can’t be too small, otherwise it will look like the stars are too close to the earth, and then we’ll try to draw the solar system and this sphere is too small to draw, and I’ll show you what it looks like when it’s too small.
  3. sphereGeometry.scale(-1, 1, 1)You can think of it as turning a ball inside out.

Different angles of normal effects:

The size of the outer sphere exceeds the far view:

The size of the outer sphere equals the radius of the Earth:

2. Light transmittance of the earth

The above renderings all have a display problem, that is, our map is hollow, and you can see through the side to the other side, we want to make the earth look more like a solid, we now need to put a sphere inside the earth, by controlling the transparency of the sphere to control the transparency of the earth.

    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 Settings are requiredtransparent: trueYou can set the transparency.
  2. The reason I set the radius of the inner sphere to be zeroenvConifg.r - 1For fear that it would overshadow our national bonds.
  3. If you want to change the color of the inner ball can be usedthis.InsideSphere.material.color.set(this.bgColor)Methods.

Take a look at two effects:

3. Earth Halo (The elves)

This special material is always facing the camera, which means that no matter what Angle we turn to look at the model, it’s always facing our screen, and the Sprite doesn’t cast any shadows.

The new Three. Sprite is similar in nature to the new Three. boxGeometry, except that it creates a Sprite geometry that is always facing the screen.

SpriteMaterial is a material that uses a Sprite. It is paired with a Sprite. You can adjust the color, opacity, etc.

Prepare a halo image like the one below:

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. Introduced ‘texture mapping’ as always.
  2. It’s just that he needs itTHREE.SpriteMaterialwithTHREE.SpriteTo map the texture.
  3. Set up thesprite.scaleBecause sprites are usually very small and you have to scale it up a little bit, this number doesn’t have to bePI., according to their own real situation input.

For example, Sprite graphics can be used in shooting games to aim at stars, or in 3D games, the game characters will have their names on their heads, which will be hard to see if the name is not facing our screen.

4. PS to modify the color of the Sprite map

If you feel my halo does not look good, you can open Photoshop and modify it:

Import images

Click on theReplace the color

Choose your favorite color

Five. Earth RBI

We usually need to make some marks on the earth, and these marks come in different sizes, different colors, different shapes, but the important thing is that this figure needs to be parallel to the radius line from the center of the earth in order to fully show the surface of the earth.

Prepare a dotting chart, preferably in white so that we can use other colors later:

We add a markSpot method to the Earth component. This method supports multiple and single object processing, accepts an array or object, and calls this method externally using ref:

markSpot(obj) { if (obj instanceof Array) { obj.forEach((item) => { this.object.add(spot(item)); }); } else { this.object.add(spot(obj)); }},
  1. this.objectIs anew THREE.Object3DGenerated container that can store more than oneMeshTo form a whole for a group.
  2. spotSo we are going to implement the next RBI method.
  3. The receivedobjIs a configuration item that includes the color, size, opacity, shape, and so on of the dot.

Write it this way when using a component:

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

/ cc_map_3d_pro/SRC/utils/sport. Config. Js rbi some default properties

const config = { size: 7, opacity: .8, color: 'yellow', url: require('.. /assets/images/ dot.png ')} export default (options) = bb0 {return {... config, ... options } }

/cc_map_3d_pro/ SRC /utils/spot.js, first import the configuration 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 them with the configuration passed in by the user.
  2. Converts the incoming latitude and longitude to Cartesian coordinatesx, y, zValue.
  3. Size is achieved by zooming in and out of graphics.
  4. THREE.PlaneBufferGeometry(1, 1)Generate a flat geometry with width and height as parameters. The reason why you don’t use width and height to manipulate the image size is because it is not set as wellscaleIn agile.
  5. To get the latitude and longitudeEnvConifg. R * 1.01It’s because it doesn’t coincide with the line on the earth.

The effect is as follows:

Reverse dot

The position is right, but the Angle is not going to work, so now we have to figure out how many degrees it has to rotate to be perpendicular to the radius line, and here we get a little bit of math.

The first step:normalizeThe normalized

For example, if there are two line segments starting from the center of a circle, the degree of the Angle between them will not change no matter how long the two line segments are. Therefore, when calculating some ratios or angles, if the length of the data does not affect the calculation result, we will return them to the calculation after processing.

And what normalization does is it turns your vector into a vector of length 1, so if you have a three dimensional vector of length, width and height x, y, z, it’s going to be x times x plus y times y plus z times z is equal to 1

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

As shown in the figure, the red is the vector with x, y and z values of 6, which becomes the vector represented by the blue line segment after normalization.

The second step:XOYThe plane ofnormal

“A normal line is a straight line that is always perpendicular to a plane

The current plane of our dot is in the XOY plane by default, so its normal can be understood as the midline is the z axis, for example, the line 0, 0, z, it doesn’t matter what z is you can write it as new three.vector3 (0, 0, 0, 999).normalize(), but it is recommended to write new three.vector3 (0, 0, 1) directly to save some performance.

Step 3: UsequaternionsFlip the plane

Quaternion is a mathematical concept that I can’t explain, but it gives you a sense of it, and it’s used to calculate the coordinates of a point, after a rotation of c degrees about a vector, and it’s very similar to the complex number, I * I = -1, Quaternion in geometry can be written as I * I = J * J = K * K = I * J * K = -1, through mathematical calculation can be obtained after the rotation of the coordinates of a set of formulas.

Using quaternions set rotation Angle quaternion. SetFromUnitVectors (‘ vector 1 ‘, 2 ‘ ‘vector) the parameters of the need to use the normalized processing, vector rotation to the direction vector Angle of rotation is needed for 2 n, make goals around n degrees.

Just now we know that the normal line of our dot graph is (0, 0, 1), and the coordinate from the center of the circle to the point is (x, y, z), then we control the rotation of its normal line to make the normal line coincide with the vector (x, y, z), then the vector (x, y, z) will be also perpendicular to the dot plane. In this way, the dot shape will be tangent to the earth.

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

All the code
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, constantly changing the size and color of the dots, you can make dynamic dots effect, this later section is dedicated to animation and then unified style

6. The Earth Light Pillar

Sometimes we need to light a beam of light at some point on the planet, and the height of the beam indicates the density of assets or the sales of goods there.

coneTHREE.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. CylinderGeometryThe first parameterRadius of the circleThe tip of the cone, set to zero, is a pointed awl.
  2. CylinderGeometryThe first parameterRadius of the circleThat’s 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 defaults to 8.
  5. The number of segments along the height of the fifth cylinder. Default is 1.
  6. Note that the cylinder’s default center line is on the Y-axis, useful when using quaternion flipping.

Encapsulated 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 coordVec3 = new three.vector3 (coord.x, coord.y, coord.y, coord.y, coordVec3) const coordVec3 = new three.vector3 (coord.x, coord.y, coord.y, coord.y, coordVec3) coord.z).normalize(); const geometry = new THREE.CylinderGeometry(0, 3, size); const mesh = new THREE.Mesh(geometry, material); return mesh }

Again, use quaternions

Considering the characteristics of a cone, we need to make the central high line of the cone coincide with the spherical vector, so the normalized vector of the cone can be selected (0, 1, 0):

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

Effect:

All the code
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 coordVec3 = new three.vector3 (coord.x, coord.y, coord.y, coord.y, coordVec3) const coordVec3 = new three.vector3 (coord.x, coord.y, coord.y, coord.y, coordVec3) 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 to create a dynamic taper effect. This will be done in the animation section later
The earth flight line needs to be discussed separately after, because the knowledge involved is a little difficult, need to be detailed planning.

end

In the next post, we will talk about how to distinguish different countries. When you hover over the mouse, you will see the prompt box of the current country. There will be a lot of mathematical concepts in this box, but don’t be afraid.