# 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

content syllabus

1. Build grid

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
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
}
}``````

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

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