29

About starting with three.js to making a 3d earth (Part 4: Textured Earth)

The relevant code can be github

Introduction

We have almost stored the basic knowledge through the first three articles. In this article, we will make a map earth. This kind of earth is also a scheme used by many companies, but the disadvantage is also obvious that it cannot accurately select a certain It can’t be done with some detailed operations, but learning this technology is still a pleasant thing. Maybe you don’t need to select the function of the country, so let’s talk less about our army's attack.

1. Draw a wooden block

We only discuss regular rectangular wooden blocks in this article. We will talk about the more common irregular wooden blocks in our life in the 3d model article. The principle of drawing wooden blocks is to first geometry and define its material as a wood picture. The material is evenly and regularly distributed on the geometry , so it becomes a wooden block in our eyes.

Download a picture of a wooden block

I am a picture of wood texture directly from Baidu, you can also use this one and create a new img to store the picture.
image

New concept "loader"
const loader = new THREE.TextureLoader();

We generated code above a loader can load a series of operations with this example, internal ImageLoader to load files, as the name suggests Texture a call can be textured meaning texture load, a back section down to 3d loaded Model loaders will be introduced later.
image.png

const loader = new THREE.TextureLoader();
        loader.load(
            './img/木块.jpeg',
            (texture) => {
                const material = new THREE.MeshBasicMaterial({
                    map: texture
                })
                const geometry = new THREE.BoxGeometry(2, 2, 1);
                // 加入纹理
                const mesh = new THREE.Mesh(geometry, material)
                // 放入几何
                scene.add(mesh);
            },
            (xhr) => {
                // 进度
                console.log(`${xhr.loaded / xhr.total * 100}%`)
            },
            (err) => {
                // 错误
                console.log(err)
            }
        )
  1. The path of the resource to be loaded as the first parameter.
  2. Callback after the second parameter is successfully loaded, the texture object will be returned.
  3. third parameter progress will be called during the loading process. The parameter is an instance of XMLHttpRequest, which contains total and loaded bytes. Please note that three.js r84 has abandoned the TextureLoader progress event. We can actually fill in undefined .
  4. The callback of the fourth parameter error.

At present, when we open our html file directly, he will report the following error:

image.png

Every time we encounter this kind of cross-domain error report, we should immediately think of putting resources on the server, but there are currently more concise ways.

Configure vscode plug-in

image.png
Go live lower right corner of our project page to start a service.
image.png
At this point we can get the following effects:
image.png
image.png

Complete code:

<html>
<body>
    <script src="https://cdn.bootcdn.net/ajax/libs/three.js/r122/three.min.js"></script>
    <script src="../utils/OrbitControls.js"></script>
    <script>
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 1000);
        camera.position.z = 20;
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setClearColor(0xffffff)
        orbitControls = new THREE.OrbitControls(camera, renderer.domElement);
        document.body.appendChild(renderer.domElement);

        const axisHelper = new THREE.AxisHelper(4)
        scene.add(axisHelper)

        // 为物体增加材质
        const loader = new THREE.TextureLoader();
        loader.load(
            './img/木块.jpeg',
            (texture) => {
                console.log(texture)
                const material = new THREE.MeshBasicMaterial({
                    map: texture
                })
                const geometry = new THREE.BoxGeometry(2, 2, 1);
                // 加入纹理
                const mesh = new THREE.Mesh(geometry, material)
                // 放入几何
                scene.add(mesh);
            },
            (xhr) => {
                // 进度(已废弃)
                console.log(`${xhr.loaded / xhr.total * 100}%`)
            },
            (err) => {
                // 错误
                console.log(err)
            }
        )

        const animate = function () {
            requestAnimationFrame(animate);
            renderer.render(scene, camera);
        };
        animate();

    </script>
</body>

</html>

2. Detailed discussion of texture attributes

Let's talk about several attributes of texture. To see that the difference is not obvious in the picture of the wooden block, we put another picture of Naruto img folder.
image

In the code, we can only change the path.

loader.load(
    './img/螺旋丸.jpeg',
    (texture) => {
    ...//

image.png
image.png

From the figure above, we can see that there are complete pictures on all six sides, but because different images with different aspect ratios are compressed accordingly, we will introduce a few more commonly used attributes next.

Repeat repeat

We process the loaded texture texture.repeat.x = 0.5 define its x-axis repeat value.
image.png
Increase its value to 5.
image.png

From the above effect, it can be seen that this repeat.x similar to the number of pictures in the x-axis direction of the object, that is to say 0.5 means that the x-axis direction is filled with 0.5 pictures, and 5 means that 5 pictures are needed to be full, so it is opposite. The repetition of the y-axis is as shown in the following figure:
image.png
image.png
This looks like a rope of a gift box, so next we let this picture cover the surface.

Loopback wrapS wrapT

t is the y-axis of the picture, let's set it:

texture.wrapT = THREE.RepeatWrapping;

image.png
Set the x-axis in the same way. Note that the x-axis is called s :

textureObj.wrapS = THREE.RepeatWrapping

image.png

texture is not the focus of our series and we won’t expand it.
The complete code is as follows:

<html>
<body>
    <script src="https://cdn.bootcdn.net/ajax/libs/three.js/r122/three.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/dat-gui/0.7.7/dat.gui.min.js"></script>
    <script src="../utils/OrbitControls.js"></script>
    <script>
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 1000);
        camera.position.z = 14;
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setClearColor(0xffffff)
        orbitControls = new THREE.OrbitControls(camera, renderer.domElement);
        document.body.appendChild(renderer.domElement);
        const axisHelper = new THREE.AxisHelper(4)
        scene.add(axisHelper)
        // 为物体增加材质
        let textureObj = null;
        const loader = new THREE.TextureLoader();
        loader.load(
            './img/螺旋丸.jpeg',
            (texture) => {
                textureObj = texture;
                const material = new THREE.MeshBasicMaterial({
                    map: texture
                })
                const geometry = new THREE.BoxGeometry(2, 2, 1);
                // 加入纹理
                const mesh = new THREE.Mesh(geometry, material)
                // 放入几何
                scene.add(mesh);
            },
            (xhr) => {
                // 进度(已废弃)
                console.log(`${xhr.loaded / xhr.total * 100}%`)
            },
            (err) => {
                // 错误
                console.log(err)
            }
        )
        const pames = {
            repeatx: 5,
            repeaty: 5,
        }
        function createUI() {
            const gui = new dat.GUI();
            gui.add(pames, "repeatx", 0, 5).name("repeatx")
            gui.add(pames, "repeaty", 0, 5).name("repeaty")
        }
        const animate = function () {
            if (textureObj) {
                textureObj.repeat.x = pames.repeatx
                textureObj.repeat.y = pames.repeaty
                textureObj.wrapT = THREE.RepeatWrapping;
                textureObj.wrapS = THREE.RepeatWrapping
            }
            requestAnimationFrame(animate);
            renderer.render(scene, camera);
        };
        createUI()
        animate();
    </script>
</body>
</html>

3. Build the vue project ( main task finally started)

Initialize a clean vue project. I won’t talk about this process here. Let’s start with the introduction of three.js . It is important to note that three.js is very important. The same logic has different effects in different versions, so I want to be different from the original version. Students who write code like this article can temporarily unify the version with me:

yarn add three@0.123.2 

App.vue to look like the following

<template>
  <div id="app">
    <cc-map id="map"></cc-map>
  </div>
</template>

<script>
import ccMap from "./components/cc_map.vue";
export default {
  name: "App",
  components: {
    ccMap,
  },
};
</script>

<style>
#app {
  overflow: hidden;
  border: 1px solid #ccc;
  width: 700px;
  height: 600px;
  margin: 20px auto;
}
</style>

As can be seen from the above code, <cc-map></cc-map> is vue component mentioned in the first article . In the following chapters, we will focus on developing the function of this component, unless the scattered knowledge points me I will open a html , most of which are main tasks.

Create these three folders and files temporarily.
image.png

4. Stickers to be used

Do you want to prevent uploading 4M that exceed 06076b625091f0, so the following is a vague screenshot. Those who want to see the original picture can see the picture in my project. The picture here is in the position assets > images

image.png

Two parameters are configured in config > earth.config.js

export default {
    r: 80, // 半径
    earthBg: require("../assets/images/地图.png"), // 贴图路径
}

At present components > cc_map.vue is preliminary, and pay attention to the habit of introducing'three'.

<template>
  <div class="map" ref="map"></div>
</template>

<script>
import * as THREE from "three";
import envConifg from "../config/earth.config";

export default {
  name: "ccMap",
  data() {
    return {
    };
  },
  methods: {
  },
  mounted() {
  },
};
</script>

<style scoped>
.map {
  box-sizing: border-box;
  width: 100%;
  height: 100%;
}
</style>

5. Extract the construction of the basic environment into a method

are the methods mentioned in the previous chapter, first initialize the data data

data() {
    return {
      scene: null,
      camera: null,
      mapDom: null,
      renderer: null,
      orbitControls: null,
      object: new THREE.Object3D(),
      axisHelper: new THREE.AxesHelper(120),
      textureLoader: new THREE.TextureLoader(),
    };
  },
Step 1: The initial scene uses initTHREE (it will not be changed afterwards)
initTHREE() {
  this.renderer = new THREE.WebGLRenderer({
    antialias: true,
  });
  this.mapDom = this.$refs.map;
  this.renderer.setSize(this.mapDom.clientWidth, this.mapDom.clientHeight);
  this.renderer.setClearColor(0xffffff, 1.0);
  this.mapDom.appendChild(this.renderer.domElement);
},
Step 2: The initial camera uses initCamera (they will not be changed afterwards)
initCamera() {
  this.camera = new THREE.PerspectiveCamera(
    45,
    this.mapDom.clientWidth / this.mapDom.clientHeight,
    1,
    2000
  );
  this.camera.position.z = 300;
  this.camera.up.set(0, 1, 0);
  this.camera.lookAt(0, 0, 0);
},
The third step: the initial container uses initScene (there will be basically no change afterwards)
this.scene = new THREE.Scene();
The fourth step: the initial auxiliary line uses initAxisHelper (there will be basically no change afterwards)
this.scene.add(this.axisHelper);
Step 5: The initial light source uses initLight (there will be basically no change afterwards)
const ambientLight = new THREE.AmbientLight(0xffffff);
this.scene.add(ambientLight);

In the later stage, the sunlight can be simulated, and when we add a flat light, it will be very similar.

Step 6: The initial track uses initOrbitControls (they will not be changed afterwards)
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// ...
initOrbitControls() {
  const os = new OrbitControls(this.camera, this.renderer.domElement);
  os.target = new THREE.Vector3(0, 0, 0); //控制焦点
  os.autoRotate = false; //将自动旋转关闭
  os.enablePan = false; // 不禁止鼠标平移, 可以用键盘来平移
  os.maxDistance = 1000; // 最大外移动
  os.minDistance = 100; // 向内最小外移动
  this.orbitControls = os;
},
Step 7: Initial earth background use initBg
  • , there will be a special drawing of objects, and then we will talk about the circle
initBg() {
  // 把背景图加载过来当做纹理。
  const texture = this.textureLoader.load(envConifg.earthBg);
  // 这个绘制球体
  const geometry = new THREE.SphereGeometry(envConifg.r, 50, 50);
  // 放入纹理
  const material = new THREE.MeshLambertMaterial({
    map: texture,
  });
  const mesh = new THREE.Mesh(geometry, material);
  this.scene.add(mesh);
},
Step 8: The initial rendering function uses glRender
this.renderer.render(this.scene, this.camera);
requestAnimationFrame(this.glRender);

It must not be called render

end: switch mode
  mounted() {
    this.initTHREE();
    this.initCamera();
    this.initScene();
    this.initAxisHelper();
    this.initLight();
    this.initOrbitControls();
    this.initBg();
    this.glRender();
  },

image.png
image.png

The texture map here can actually meet some of the needs of the scene, don't look at it, it can be simple and it can be very dazzling.

6. ps add text, but it will be distorted

The map of the earth has its limitations, for example, the map above is empty and there is no corresponding country name, but if I put the country name on the ps in the picture, let's see the effect.
image.png
PS is not the most flexible method after all, and if you look closely, you will find that the text is a little curved upwards, because the picture is attached to the sphere, so the closer it is to the north and south poles, the more it converges into one point, so this mode of adding text is only for a few Large area and useful in countries near the equator.

7. Interesting sphere

Let’s take out the sphere we set above and play with it separately. Here we only talk about the first three parameters, and there will be an article dedicated to geometry later.

![image.png](/img/bVcRbTv)
  1. r is the radius, which determines the size of the sphere.
  2. The number of horizontal segments (segments along the meridian), the minimum value is 3, and the default value is 8. For example, if a circle is composed of 100 points linked to each other, the parameter is 100.
  3. The number of vertical segments (segments along the latitude line), the minimum value is 2, and the default value is 6.

Come on show: When I change the number of horizontal segments to 5 new THREE.SphereGeometry(envConifg.r, 5, 50);
image.png
image.png

Come on show: When I change the vertical segment number to 5 new THREE.SphereGeometry(envConifg.r, 50, 5);
image.png
image.png

8. Limitations of Mapped Earth

  1. As mentioned above, it is difficult to add names to countries and regions.
  2. It is not possible to specifically select a country.
  3. Unable to highlight an area or have a red border.
  4. The viewing angle is somewhat distorted after zooming in.
  5. Unable to hover to display details
Here is the comparison

1x
image.png
2x
image.png
3x
image.png

end.

The next article will start to formally draw our vector 16076b62509711 3d earth, which will involve some mathematical knowledge, such as trigonometric functions, if you can no
This is the case this time, I hope to make progress with you.


lulu_up
5.7k 声望6.9k 粉丝

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