Earth (in THREE.js)

by PhilQ

HTML

<script type="x-shader/x-vertex" id="_vertexShader">
	uniform vec2 resolution;

	varying vec4 vPos;
	varying vec3 vNormal;
	varying vec2 vUv;
	varying mat4 matModel;
	varying mat4 matModelView;
	varying mat4 matProjection;
	varying mat3 matNormal;

	void main() {
		// Send to fragment shader
		vNormal = normal;
		vUv = uv;
		vPos = vec4(position, 1.0);
		matModel = modelMatrix;
		matModelView = modelViewMatrix;
		matProjection = projectionMatrix;
		matNormal = normalMatrix;

		// Local space (object)
		gl_Position = vPos;
		
		// to World space (scene)
		gl_Position = modelMatrix * vPos;
		
		// to View space (camera)
		gl_Position = viewMatrix * modelMatrix * vPos;
		
		// to Clip space
		gl_Position = projectionMatrix * viewMatrix * modelMatrix * vPos;

		// Note: Three.js will convert to NDC space and then to Screen/Viewport space
	}
</script>
<script type="x-shader/x-fragment" id="_fragmentShader">
	uniform vec2 resolution;

	varying vec4 vPos;
	varying vec3 vNormal;
	varying vec2 vUv;
	varying mat4 matModel;
	varying mat4 matModelView;
	varying mat4 matProjection;
	varying mat3 matNormal;

	void main() {
		vec4 localUv = vec4(vUv, 0.0, 1.0); // Local uv, bottom-left (0,0) to top-right (1,1)
		vec4 localPos = vPos;
		vec4 worldPos = matModel * vPos;
		vec4 viewPos = viewMatrix * matModel * vPos;
		vec4 clipPos = matProjection * viewMatrix * matModel * vPos;
		vec4 ndcPos = vec4(clipPos.xyz / clipPos.w, clipPos.w); // Added w to be able to back-calculate to clip space
		vec2 screenPos = (ndcPos.xy + 1.0) * 0.5; // Screen uv, bottom-left (0,0) to top-right (1,1)
		vec2 viewportPos = vec2(screenPos.x, -screenPos.y); // Top-left (0,0) to bottom-right (1,1)
		vec2 pixelPos = screenPos * resolution; // Actual pixel coordinates
		
		// gl_FragColor = vec4(screenPos.xy, 0.0, 1.0);
		
		// Basic color + lighting
		vec4 color = vec4(1.0, 0.0, 1.0, 1.0);
		vec3 light = normalize( vec3( 0.5, 0.2, 1.0 ) ); // Normalized direction (like sunlight)
		float...

CSS

body { margin: 0; }
canvas { width: 100%; height: 100% }
#toggle {
	position: fixed;
	top: 0;
	left: 0;
	z-index: 10;
}

JavaScript

import * as THREE from 'three';
import { OrbitControls } from 'OrbitControls';
import { VertexNormalsHelper } from 'VertexNormalsHelper';

class LatLng {
	constructor( lat, lng ) {
		// Latitude and longitude in radians
		this.lat = lat ? lat : 0;
		this.lng = lng ? lng : 0;
		this.u = 0;
		this.v = 0;
		this.updateUV();
	}

	// Latitude:
	//  -90 (N) --> 90 (S)
	//     PI/2 --> -PI/2
	// Longitude:
	// -180 (W) --> 180 (E)
	//      -PI --> PI
	updateUV() {
		this.v = (this.lat / (-0.5 * Math.PI)) * 0.5 + 0.5;
		this.u = (this.lng / Math.PI) * 0.5 + 0.5;
		if (this.v > 0 && this.v < 1) {
			this.u = ((this.u - 0.125) + 1) % 1; // Translate uv map 1/8 eastward to align map and face edges
		}
	}

	fromUV( u, v ) {
		this.lng = 2 * (u - 0.5) * Math.PI;
		this.lat = 2 * (v - 0.5) * -0.5 * Math.PI;
	}

	fromVector( v ) {
		// From point on unit sphere
		v = v.clone().normalize();
		this.lat = Math.asin( v.y );
		this.lng = Math.atan2( v.x, v.z ); //TODO: maybe -z ?
		this.updateUV();
		return this;
	}

	toVector() {
		// To point on unit sphere
		let r = Math.cos( this.lat );
		return new THREE.Vector3(
			Math.sin( this.lng ) * r,
			Math.sin( this.lat ),
			Math.cos( this.lng ) * r //TODO: maybe -z ?
		);
	}
}
THREE.Vector3.prototype.toLatLng = function() {
	return new LatLng().fromVector( this );
};
THREE.Vector3.prototype.fromLatLng = function( latlng ) {
	return this.copy( latlng.toVector() );
};

class Planet extends THREE.Group {
	constructor( params ) {
		super();

		//this.type = 'Planet';

		if ( params.position ) {
			this.position.copy( params.position );
		}
		
		this.r = params.r ? params.r : 1;
		this.segments = params.segments ? params.segments : 1;
		this.distributeMoreEqually = typeof params.distributeMoreEqually !== 'undefined' ? params.distributeMoreEqually : false;
		this.colored = typeof params.colored !== 'undefined' ? params.colored : true;

		this.minU = Infinity;
		this.maxU = -Infinity;
		this.minV = Infinity;
		this.maxV =...