I am participating in the Mid-Autumn Festival Creative Submission contest, please see: Mid-Autumn Festival Creative Submission Contest for details

preface

Hello everyone, it’s the weekend again, and it’s time for Fly to write an article. It’s not the Mid-Autumn Festival in a few days. I think I wrote an article about realizing 3D earth from 0-1 before, which has a very good response effect. This article is still an introductory level, focusing on the simple knowledge clearly. If you are an experienced three lover, you can directly cross it out, so as not to waste your time. Ok 👌 without further ado, what can you learn from reading this article?

This article will take an estimated 5 minutes to read

  1. The making of the sky box
  2. Maps in three.js
  3. One object revolves around another

Initialize the

In this article, I will not introduce some elements of Three. js in detail from the beginning. If you are not very clear, you can directly read my introduction to this article —

This article introduces you to Three. js — realizing a 3D visualization map from 0 to 1. In fact, after reading this article, you can have a basic understanding of Three. js

class Moon { constructor() { this.init() } init() { // SCENE this.scene = new THREE.Scene() // CAMERA let SCREEN_WIDTH =  window.innerWidth, SCREEN_HEIGHT = window.innerHeight let VIEW_ANGLE = 45, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, PerspectiveCamera(VIEW_ANGLE, PerspectiveCamera, PerspectiveCamera, PerspectiveCamera, PerspectiveCamera) FAR) this.scene.add(this.camera) this.camera.position.set(0, 150, 400) this.camera.lookAt(this.scene.position) // RENDERER this.renderer = new THREE.WebGLRenderer({ antialias: true, }) this.renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT) document.body.appendChild(this.renderer.domElement) } addCube() { const geometry = new THREE.BoxGeometry() const material = new THREE.MeshBasicMaterial({ color: 0x50ff22 }) this.cube = new THREE.Mesh(geometry, material) this.scene.add(this.cube) } setController() { this.controller = new THREE.OrbitControls( this.camera, this.renderer.domElement ) } render() { this.renderer.render(this.scene, this.camera) } }Copy the code

I set up 📷, render, and a Controller. Then I added a cube to the scene. Take a look at the image now:

Foundation building

Is it really ugly? Ha ha ha that’s just a test. Now we’re going to build the skybox background.

Build the sky box background

The idea behind sky Box is to use a super-large cube with maps on each side. First we create a geometry

let skyGeometry = new THREE.CubeGeometry(10000, 10000, 10000)
Copy the code

The length, width and height of the cubeGeometry are all 10000 mainly to cover our scene. Then start preparing the paths for all six images as follows:

let imagePrefix = 'img/'
let directions = ['up', 'down', 'behind', 'front', 'left', 'right']
let imageSuffix = '.png'
Copy the code

With the image path, we start generating the texture:

let imageURLs = []
for (let i = 0; i < 6; i++) {
  imageURLs.push(imagePrefix + directions[i] + imageSuffix)
}
let textureCube = THREE.ImageUtils.loadTextureCube(imageURLs)
Copy the code

In fact, we all know that the mesh in three.js is composed of geometry and material

Here I use ShaderMaterial, what material is this??

Materials rendered using custom Shader. Shader is a small program written in GLSL that runs on the GPU. You may need to use custom shader if you want to:

  • To achieve effects beyond the built-in materials.
  • Group many objects into a single BufferGeometry to improve performance.

This is a custom material, yes, here we need to use shaders, many friends are very resistant to this language, but later I will introduce you some knowledge of shaders.

What is ShaderLib ????

It is a library of three. Js shaders: source address: github.com/mrdoob/thre…

It has all sorts of shaders in it and just happens to have the cube we need:

let shader = THREE.ShaderLib['cube']
Copy the code

For learning purposes: to see how this works, let’s look at the vertex shader:

"varying vec3 vWorldPosition; Void main() {vec4 worldPosition = vecmatrix (position, 1.0); vWorldPosition = worldPosition.xyz; Gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }"Copy the code

Define a variable like vWorldPosition, multiply the model matrix, and pass the value to the chip shader. A lot of people must have forgotten what this is again? I actually talked about the “dry stuff” in this post a little visualization of the front end – with a mind map, but I’ll go over it again.

  1. First of all, the object itself has a coordinate system which we call the local coordinate system, it also has an origin, but it also has a corresponding position in the world coordinate system, so there is a matrix variation between them — the model matrix
  2. The world coordinate system — there are also matrix changes to the observation coordinate system, which is called the view matrix
  3. The viewing space — the clipping space is called the projection matrix because in three dimensions we simulate the human eye with a camera, and things can only be seen inside the visual vertebra. So you have projection matrices, you have perspective projections, you have orthogonal projections, one near big, one far small, one near big is the same

And again, somebody said, this is modelView and it’s actually the model matrix * view matrix.

After looking at vertices, let’s look at chip shaders:

"uniform samplerCube tCube;
uniform float tFlip;
varying vec3 vWorldPosition;
void main() {
  gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );
}"
Copy the code

This is the vWorldPosition that receives the vertex shader, and two global variables:

Let’s go on to uniform;

uniform

It’s very simple. In fact, we have tcube and tFlip variables here, but tcube is empty and obviously we need to assign it the texture we just generated.

shader.uniforms['tCube'].value = textureCube

let skyMaterial = new THREE.ShaderMaterial({
  fragmentShader: shader.fragmentShader,
  vertexShader: shader.vertexShader,
  uniforms: shader.uniforms,
  depthWrite: false,
  side: THREE.BackSide,
})
let skyBox = new THREE.Mesh(skyGeometry, skyMaterial)
this.scene.add(skyBox)
Copy the code

So the whole skybox is built: let’s look at the effect GIF:

The figure

It looks a little bit stuck, but it’s actually great. If you’re interested, take the project and try it out for yourself.

The moon

Our theme this time is not the moon, OK, we start to add the moon to the scene, the moon is actually a sphere corresponding to three.js is SphereGeometry, or the old operation is to add the moon to the scene.

let sphereGeo = new THREE.SphereGeometry(100, 32, 16) let moonTexture = THREE.ImageUtils.loadTexture('img/moon.jpg') let moonMaterial = new THREE.MeshBasicMaterial({ map:  moonTexture, }) this.moon = new THREE.Mesh(sphereGeo, moonMaterial) scene.add(moon)Copy the code

And added a texture map to it:

The moon

The earth 🌍

We’re going to continue to add to the earth in the same way, because we need to go around it, right?

addEarth() { let sphereGeo = new THREE.SphereGeometry(50, 32, 16) let moonTexture = THREE.ImageUtils.loadTexture('img/daymap.jpg') let moonMaterial = new THREE.MeshBasicMaterial({ map: MoonTexture,}) this.earth = new THREE.Mesh(sphereGeo, moonMaterial) this.earth. Rotation. 0.01) this.earth. ReceiveShadow = true this.scene. Add (this.earth)}Copy the code

The earth

animation

Now that we have the basic elements of the scene, our first step is to make a positional adjustment. It is certain that the radius of the Earth is larger than that of the moon, and the position is also a little deviation. Ok, THEN I will adjust it:

1. The radius of the Earth’s equator :6378136.49 meters, the radius of the moon is 1738000 meters, and the ratio of the radius of the moon and the radius of the Earth is 0.2725. So the radius ratio is 0.27

I reset the radius and distance:

// Let sphereGeo = new THREE.SphereGeo (27, 32, 16) This. Moon.position. Set (250, 5, 0) // Earth let sphereGeo = new THREE.SphereGeo (100, 32, 16) This. Earth. Position. Set (-0.2, 2.7, 0.01)Copy the code

Ok to see the effect:

The Earth and the Moon

Now the whole screen is still, so we need to make the screen move, so we use requestAnimation, just make the Earth and moon move around the Y axis

Animate () {requestAnimationFrame(this.animate. Bind (this)) this.moon.rotation. Y += 0.015 this.earth this.render() this.controls.update() }Copy the code

Take a look at the rendering:

The earth animation

But here’s the question: Do you know that the moon goes around the Earth? He can’t really turn like that

This translates into the problem of one object rotating around another. Let me start with a scheme that is also quite common is that the moving object first moves to a certain place of the object, and then after the rotation Angle, after the rotation, it shifts back. It’s just a transport matrix, a rotation matrix, plus the inverse of a transport matrix

But this time I went about it in a different way:

Create a new sphere in the earth’s place, but hide it, and add the moon to the earth. Rotate the sphere: Let’s start with:

AddCpoy () {let geo = new THREE.SphereGeometry(100, 50, 50) // dO not display let mat = new THREE.MeshBasicMaterial({color: RGB (51, 51, 51)) 0xa3a3a3, visible: False,}) this.copy = new THREE.Mesh(geo, mat) this.copy. Add (this.moon) this.scene.Copy the code

Then rotate the hidden mesh

Animate () {requestAnimationFrame(this.animate. Bind (this)) this.copy.rotation. Y += 0.015 this.earth this.render() this.controls.update() }Copy the code

Let’s take a look at the effect:

Surround animation final version

The implementation principle is also simple: The geometry of the copy and the Earth are in the same position, and then the rotation itself will also cause the child object to rotate. The child object also has a distance from it, which means it is rotating around the earth.

conclusion

Readers, if you think it’s helpful for you to finish, I hope you don’t mean your hands 👍, point a 👍 and attention is one of the largest support to me, knowledge output is not easy, but I don’t forget the beginner’s mind, continue to share visual good article, if you are interested in visualization, you can pay attention to the following my visual column, or I can focus on the public no. : Front-end graphics, continuous sharing of computer graphics knowledge. All the code of this article is welcome star on my Github. If there is something wrong in this article, please point out and communicate.