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:
/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
new THREE.SphereGeometry(1000, 50, 50)
first parameter of 060e3025e998ed is the radius of the sphere. This radius must not be greater than ourpoint, otherwise the sphere will not be visible.
- 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.
sphereGeometry.scale(-1, 1, 1)
You can think of it as flipping a ball from the inside out.
Different angles of normal effect:
The size of the outer sphere exceeds the far view:
The size of the outer sphere is equal to the radius of the earth:
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);
},
- Note that you need to set
transparent: true
to set the transparency. - 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. - 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:
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:
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);
},
- As always, the introduction of'texture mapping'.
- But he has to use
THREE.SpriteMaterial
andTHREE.Sprite
to assign texture maps. - 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.
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
Click replace the color
Choose your favorite color
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:
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));
}
},
this.object
is anew THREE.Object3D
generated by 060e3025e99fdc, which can store multipleMesh
as a group to form a whole.spot
what we will implement next.- 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;
}
- We set the default configuration properties and merged with the configuration passed in by the user.
- Convert the incoming longitude and latitude into the Cartesian coordinate system
x, y, z
value. - The size is achieved by zooming in and out of graphics.
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 flexiblescale
- 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:
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())
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
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
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);
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);
CylinderGeometry
first parameter ofis 160e3025e9adc1. The radius of the circle is the top of the cone. Setting it to 0 means a sharp awl.
CylinderGeometry
first parameter of, the radius of the lower circle is the base of the cone.
- The third parameter is the height of the cone.
- The number of segments around the side of the fourth cylinder. The default is 8.
- The number of segments along the height of the fifth cylinder side. The default value is 1.
- Note that the default centerline of the cylinder is on the y axis, which is useful when using quaternion flipping.
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
}
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:
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.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。