Compare commits

...

2 Commits

Author SHA1 Message Date
93290a6c06
Merge branch 'master' of git.icynet.eu:evert/globe 2023-03-30 11:09:47 +03:00
d08cdd4177
add controls 2023-03-30 11:09:07 +03:00
7 changed files with 156 additions and 17 deletions

26
package-lock.json generated
View File

@ -11,7 +11,9 @@
"three": "^0.147.0"
},
"devDependencies": {
"@types/dat.gui": "^0.7.9",
"@types/three": "^0.146.0",
"dat.gui": "^0.7.9",
"typescript": "^4.6.4",
"vite": "^3.2.3",
"vite-plugin-plain-text": "^1.2.1"
@ -281,6 +283,12 @@
"node": ">= 10"
}
},
"node_modules/@types/dat.gui": {
"version": "0.7.9",
"resolved": "https://registry.npmjs.org/@types/dat.gui/-/dat.gui-0.7.9.tgz",
"integrity": "sha512-UiqZasQIask5cUwWOO6BgOjP1dNj9ChYtmeAb4WTKs5IJ7ha3ATRTeXY7/TzlmEZznh40lS6Ov5BdNA+tU+fWQ==",
"dev": true
},
"node_modules/@types/three": {
"version": "0.146.0",
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.146.0.tgz",
@ -296,6 +304,12 @@
"integrity": "sha512-IUMDPSXnYIbEO2IereEFcgcqfDREOgmbGqtrMpVPpACTU6pltYLwHgVkrnYv0XhWEcjio9sYEfIEzgn3c7nDqA==",
"dev": true
},
"node_modules/dat.gui": {
"version": "0.7.9",
"resolved": "https://registry.npmjs.org/dat.gui/-/dat.gui-0.7.9.tgz",
"integrity": "sha512-sCNc1OHobc+Erc1HqiswYgHdVNpSJUlk/Hz8vzOCsER7rl+oF/4+v8GXFUyCgtXpoCX6+bnmg07DedLvBLwYKQ==",
"dev": true
},
"node_modules/esbuild": {
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz",
@ -1002,6 +1016,12 @@
"dev": true,
"optional": true
},
"@types/dat.gui": {
"version": "0.7.9",
"resolved": "https://registry.npmjs.org/@types/dat.gui/-/dat.gui-0.7.9.tgz",
"integrity": "sha512-UiqZasQIask5cUwWOO6BgOjP1dNj9ChYtmeAb4WTKs5IJ7ha3ATRTeXY7/TzlmEZznh40lS6Ov5BdNA+tU+fWQ==",
"dev": true
},
"@types/three": {
"version": "0.146.0",
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.146.0.tgz",
@ -1017,6 +1037,12 @@
"integrity": "sha512-IUMDPSXnYIbEO2IereEFcgcqfDREOgmbGqtrMpVPpACTU6pltYLwHgVkrnYv0XhWEcjio9sYEfIEzgn3c7nDqA==",
"dev": true
},
"dat.gui": {
"version": "0.7.9",
"resolved": "https://registry.npmjs.org/dat.gui/-/dat.gui-0.7.9.tgz",
"integrity": "sha512-sCNc1OHobc+Erc1HqiswYgHdVNpSJUlk/Hz8vzOCsER7rl+oF/4+v8GXFUyCgtXpoCX6+bnmg07DedLvBLwYKQ==",
"dev": true
},
"esbuild": {
"version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz",

View File

@ -9,7 +9,9 @@
"preview": "vite preview"
},
"devDependencies": {
"@types/dat.gui": "^0.7.9",
"@types/three": "^0.146.0",
"dat.gui": "^0.7.9",
"typescript": "^4.6.4",
"vite": "^3.2.3",
"vite-plugin-plain-text": "^1.2.1"

View File

@ -5,6 +5,7 @@ import {
Object3D,
ShaderMaterial,
SphereGeometry,
Vector3,
} from 'three';
import { plainText as vertexShader } from './shaders/atmosphere.vert';
@ -17,7 +18,7 @@ export class Atmosphere extends Object3D {
public Scale = 1;
public ScaleDepth = 0.25;
public G = -0.95;
public Wavelength = [0.65, 0.57, 0.475];
public Wavelength = new Vector3(0.65, 0.57, 0.475);
public geom?: SphereGeometry;
public mesh?: Mesh;
@ -35,12 +36,30 @@ export class Atmosphere extends Object3D {
this.initialize();
}
set planetRadius(radius: number) {
const thickness = this.thickness;
this.innerRadius = radius;
this.outerRadius = radius + thickness;
this.refreshScale();
}
get planetRadius() {
return this.innerRadius;
}
set thickness(thickness: number) {
this.outerRadius = thickness + this.innerRadius;
this.refreshScale();
}
get thickness() {
return this.outerRadius - this.innerRadius;
}
setUniforms(shader: ShaderMaterial) {
shader.uniforms.invWavelength = {
value: [
1 / Math.pow(this.Wavelength[0], 4),
1 / Math.pow(this.Wavelength[1], 4),
1 / Math.pow(this.Wavelength[2], 4),
1 / Math.pow(this.Wavelength.x, 4),
1 / Math.pow(this.Wavelength.y, 4),
1 / Math.pow(this.Wavelength.z, 4),
],
};
shader.uniforms.outerRadius = { value: this.outerRadius };
@ -60,8 +79,18 @@ export class Atmosphere extends Object3D {
}
initialize() {
if (this.geom) {
this.geom.dispose();
this.mesh && this.remove(this.mesh);
}
this.geom = new SphereGeometry(this.outerRadius, 128, 128);
this.mesh = new Mesh(this.geom, this.shader);
this.add(this.mesh);
}
private refreshScale() {
this.Scale = 1 / (this.outerRadius - this.innerRadius);
this.setUniforms(this.shader);
this.initialize();
}
}

View File

@ -7,6 +7,7 @@ import {
Color,
DirectionalLight,
ShaderMaterial,
MeshPhongMaterial,
} from 'three';
import { plainText as vertexShader } from './shaders/earth.vert';
@ -14,15 +15,31 @@ import { plainText as fragmentShader } from './shaders/earth.frag';
import { Atmosphere } from './atmosphere';
export class Globe extends Object3D {
private sphere = new SphereGeometry(1, 128, 128);
private atmosphere = new Atmosphere(1, 1.05);
private earthMaterial?: ShaderMaterial;
private sphere?: SphereGeometry;
private clouds = new SphereGeometry(1.02, 64, 64);
public atmosphere = new Atmosphere(1, 1.05);
public earthMaterial?: ShaderMaterial;
public earthMesh?: Mesh;
private loader = new TextureLoader();
constructor(private planetRadius: number) {
super();
}
set radius(radius: number) {
this.planetRadius = radius;
this.atmosphere.planetRadius = radius;
this.earthMaterial && this.atmosphere.setUniforms(this.earthMaterial);
this.createMesh();
}
get radius() {
return this.planetRadius;
}
public async loadTexture(name: string) {
return new Promise<Texture>((resolve, reject) => {
this.loader.load(`/${name}`, resolve, undefined, reject);
this.loader.load(`./${name}`, resolve, undefined, reject);
});
}
@ -55,10 +72,19 @@ export class Globe extends Object3D {
this.atmosphere.setUniforms(this.earthMaterial);
this.atmosphere.setLight(light);
this.earthMesh = new Mesh(this.sphere, this.earthMaterial);
this.add(this.earthMesh);
this.createMesh();
this.add(this.atmosphere);
}
public tick() {}
private createMesh() {
if (this.sphere) {
this.sphere.dispose();
this.earthMesh && this.remove(this.earthMesh);
}
this.sphere = new SphereGeometry(this.planetRadius, 128, 128);
this.earthMesh = new Mesh(this.sphere, this.earthMaterial);
this.add(this.earthMesh);
}
}

View File

@ -3,6 +3,7 @@ import { Renderer } from './renderer';
import { DirectionalLight } from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import './style.css';
import { GUI } from 'dat.gui';
const main = new Renderer();
@ -10,7 +11,7 @@ const dirLight = new DirectionalLight(0xffffff, 1);
dirLight.position.set(10, 10, 10);
main.scene.add(dirLight);
const globe = new Globe();
const globe = new Globe(1);
main.resize();
globe.initialize(dirLight).catch(console.error);
main.scene.add(globe);
@ -19,6 +20,58 @@ main.camera.position.set(0, 0, 2);
const control = new OrbitControls(main.camera, main.canvas);
const gui = new GUI();
const atmosGui = gui.addFolder('Atmosphere');
const updateAtmosShader = () => {
globe.atmosphere.setUniforms(globe.atmosphere.shader);
globe.atmosphere.setUniforms(globe.earthMaterial!);
};
// any's because for some reason the generic expects a string indexed record
atmosGui
.add<any>(globe.atmosphere.Wavelength, 'x', 0, 1)
.name('R')
.onChange(updateAtmosShader);
atmosGui
.add<any>(globe.atmosphere.Wavelength, 'y', 0, 1)
.name('G')
.onChange(updateAtmosShader);
atmosGui
.add<any>(globe.atmosphere.Wavelength, 'z', 0, 1)
.name('B')
.onChange(updateAtmosShader);
atmosGui
.add<any>(globe.atmosphere, 'thickness', 0.001, 0.5)
.name('Thickness')
.onChange(updateAtmosShader);
atmosGui
.add<any>(globe.atmosphere, 'ScaleDepth', 0.01, 1)
.name('Scale depth')
.onChange(updateAtmosShader);
atmosGui
.add<any>(globe.atmosphere, 'Rayleigh', 0.0001, 0.01)
.onChange(updateAtmosShader);
atmosGui
.add<any>(globe.atmosphere, 'Mie', 0.0001, 0.01)
.onChange(updateAtmosShader);
atmosGui
.add<any>(globe.atmosphere, 'Exposure', 0, 100)
.onChange(updateAtmosShader);
atmosGui.open();
const planetGui = gui.addFolder('Planet');
planetGui
.add<any>(globe.rotation, 'x', -Math.PI * 2, Math.PI * 2, 0.01)
.name('Rotation X');
planetGui
.add<any>(globe.rotation, 'y', -Math.PI * 2, Math.PI * 2, 0.01)
.name('Rotation Y');
planetGui
.add<any>(globe.rotation, 'z', -Math.PI * 2, Math.PI * 2, 0.01)
.name('Rotation Z');
// planetGui.add<any>(globe, 'radius', 1, 100).name('Radius');
planetGui.open();
function tick() {
requestAnimationFrame(tick);
control.update();

View File

@ -1,10 +1,16 @@
#define EARTH
#include <common>
#include <normal_pars_vertex>
varying vec3 vViewPosition;
varying vec3 vLightDirection;
varying vec2 vUv;
varying vec3 vEyeNormal;
#include <common>
#include <normal_pars_vertex>
varying vec3 v3Direction;
varying vec3 c0;
varying vec3 c1;
uniform vec3 light;
uniform vec3 invWavelength; // 1 / pow(wavelength, 4) for the red, green, and blue channels
@ -19,10 +25,6 @@ uniform float scaleDepth; // The scale depth (i.e. the altitude at which the
const int nSamples = 2;
const float fSamples = 1.0;
varying vec3 v3Direction;
varying vec3 c0;
varying vec3 c1;
float dscale(float fCos) {
float x = 1.0 - fCos;
return scaleDepth * exp(-0.00287 + x * (0.459 + x * (3.83 + x * (-6.80 + x * 5.25))));

View File

@ -3,4 +3,5 @@ import plainText from 'vite-plugin-plain-text';
export default defineConfig({
plugins: [plainText.default(/\.vert|\.frag$/)],
base: '',
});