Drawing 3D Earth by webgl

Time:2021-7-28

It may be convenient to display the 3D model through three.js, but do you know how it works step by step from mesh construction to mapping to final rendering? Now we can implement it directly by using the underlying web GL and a little mathematical knowledge.

The effect of this section:Webgl 3D Earth
WebGL三维地球

content syllabus

  1. Build grid

  2. Writing shaders

  3. Realize 3D Earth

Build grid

First, we need to build a three-dimensional model of the sphere. The three-dimensional mesh model includes the following attributes (if you are not familiar with it, please review the webgl tutorial):

  • Vertex (position)
  • Normal
  • Mapping coordinates (UV)
  • Vertex indexes

Finally, the longitude and latitude sphere model as shown below should be constructed

经纬球

First, you can build a circle from the XY plane, and then convert the circle into a sphere from the XZ plane. All you need to do is use the trigonometric function. Is it very simple.

  • Normals use vertex coordinates because they are in the same direction as vertices
  • The vertex index is 6 points because each face consists of two triangles
  • The mapping UV coordinate does not need depth information, it corresponds to the XY coordinate of the upper map

The following is the basic logic of building a grid model:

const radius = 8;// radius
const n = 20;// Longitude and latitude lattice number
const position = [];// vertex
const normal = [];// normal
const texcoord = [];// UV coordinates
const indices = [];// Vertex Index 
let x, y, z;

for (let i = 0; i < n; i++) {
  const rad = Math.PI / n * i - Math.PI / 2;// Calculated from - 90 degrees
  const r = radius * Math.cos(rad);
  y = radius * Math.sin(rad);
  for (let j = 0; j < n; j++) {
    x = r * Math.sin(xRadian * j);
    z = r * Math.cos(xRadian * j);
    position.push(x, y, z);
    texcoord.push(j / n, i / n);
    normal.push(x, y, z); // The vertex is the normal, which radiates 360 degrees from the center of the circle
    const c = i * (n + 1) + j
    indices.push(c, c + 1, c + l + 1, c, c + l + 1, c + l);// Index of plane
  }
}

Writing shaders

Compared with ordinary shaders, only the UV coordinates are added, and the UV can be transmitted directly to the fragment shader through the vertex shader difference. In the fragment shader, the texture 2D function is used to obtain the color corresponding to the UV coordinates, which is also the basis of comparison on the whole.

//Vertex shader
attribute vec4 aPosition;
attribute vec4 aNormal;
attribute vec2 aTexcoord;
uniform mat4 modelMatrix;
uniform mat4 vpMatrix;
varying vec3 fragPos;
varying vec3 fragNor;
varying vec2 texcoord;

void main() {
    gl_Position = vpMatrix * modelMatrix * aPosition;
    fragPos= vec3(modelMatrix * aPosition);
    fragNor = vec3(modelMatrix * aNormal);
    texcoord = aTexcoord;
}

//Clip shader
precision mediump float;
uniform vec3 viewPos;
uniform vec3 lightPos;
uniform vec3 lightColor;
uniform vec3 ambientColor;
uniform sampler2D diffMap;
varying vec3 fragPos;
varying vec3 fragNor;
varying vec2 texcoord;

void main() {
    vec3 normal = normalize(fragNor);
    vec3 color = texture2D(diffMap, texcoord).rgb;
	
    //Light direction
    vec3 lightDir = normalize(lightPos - fragPos);
    //Light direction和法向量夹角
    float cosTheta = max(dot(lightDir, normal), 0.0);
    //Diffuse reflection
    vec3 diffuse = lightColor * color * cosTheta;
	
    //Ambient light
    // ...
    //Highlight
    // ...

    gl_FragColor = vec4(ambient + diffuse + specular, 1.0);
}

Realize 3D Earth

The final implementation part is consistent with the basic logic of the previous webgl, but the earth map should be prepared

地图

After the image is loaded, just pass the constructed map sampler into the shader. The rest are basic business logic and will not be described in detail. In this way, we will realize the three-dimensional earth

//...
const vpMatrix = m4.identity();
const uniforms = {
   modelMatrix: m4.identity(),
   lightPos: [20, 0, -20],
   lightColor: [1, 1, 1],
   ambientColor: [0.5, 0.5, 0.5],
};

gl.clearColor(0.1, 0.1, 0.1, 1);
gl.enable(gl.DEPTH_ TEST);// Depth test
gl.enable(gl.CULL_ FACE);// backface culling 
gl.viewport(0, 0, canvas.width, canvas.height); // Set drawing area
gl.useProgram(program.program);

function animate() {
   gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
   m4.multiply(projection, m4.inverse(m4.lookAt(eye, [0, 0, 0], [0, 1, 0])), vpMatrix);
   setBuffersAndAttributes(gl, vao);
   setUniforms(program, { vpMatrix });
   drawBufferInfo(gl, vao);
   gl.bindVertexArray(null);

   requestAnimationFrame(animate);
};

//Execute after loading the map
createTexture(gl, { src: '/img/earth.jpg', flipY: true }, texture => {
   uniforms.diffMap = texture;
   setUniforms(program, uniforms );
   animate();
});

Recommended Today

Implementation example of go operation etcd

etcdIt is an open-source, distributed key value pair data storage system, which provides shared configuration, service registration and discovery. This paper mainly introduces the installation and use of etcd. Etcdetcd introduction etcdIt is an open source and highly available distributed key value storage system developed with go language, which can be used to configure sharing […]