add controls

This commit is contained in:
Evert Prants 2023-03-30 11:09:07 +03:00
parent afe89c4c79
commit d08cdd4177
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
7 changed files with 156 additions and 17 deletions

26
package-lock.json generated
View File

@ -11,7 +11,9 @@
"three": "^0.147.0" "three": "^0.147.0"
}, },
"devDependencies": { "devDependencies": {
"@types/dat.gui": "^0.7.9",
"@types/three": "^0.146.0", "@types/three": "^0.146.0",
"dat.gui": "^0.7.9",
"typescript": "^4.6.4", "typescript": "^4.6.4",
"vite": "^3.2.3", "vite": "^3.2.3",
"vite-plugin-plain-text": "^1.2.1" "vite-plugin-plain-text": "^1.2.1"
@ -281,6 +283,12 @@
"node": ">= 10" "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": { "node_modules/@types/three": {
"version": "0.146.0", "version": "0.146.0",
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.146.0.tgz", "resolved": "https://registry.npmjs.org/@types/three/-/three-0.146.0.tgz",
@ -296,6 +304,12 @@
"integrity": "sha512-IUMDPSXnYIbEO2IereEFcgcqfDREOgmbGqtrMpVPpACTU6pltYLwHgVkrnYv0XhWEcjio9sYEfIEzgn3c7nDqA==", "integrity": "sha512-IUMDPSXnYIbEO2IereEFcgcqfDREOgmbGqtrMpVPpACTU6pltYLwHgVkrnYv0XhWEcjio9sYEfIEzgn3c7nDqA==",
"dev": true "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": { "node_modules/esbuild": {
"version": "0.15.18", "version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz",
@ -1002,6 +1016,12 @@
"dev": true, "dev": true,
"optional": 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": { "@types/three": {
"version": "0.146.0", "version": "0.146.0",
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.146.0.tgz", "resolved": "https://registry.npmjs.org/@types/three/-/three-0.146.0.tgz",
@ -1017,6 +1037,12 @@
"integrity": "sha512-IUMDPSXnYIbEO2IereEFcgcqfDREOgmbGqtrMpVPpACTU6pltYLwHgVkrnYv0XhWEcjio9sYEfIEzgn3c7nDqA==", "integrity": "sha512-IUMDPSXnYIbEO2IereEFcgcqfDREOgmbGqtrMpVPpACTU6pltYLwHgVkrnYv0XhWEcjio9sYEfIEzgn3c7nDqA==",
"dev": true "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": { "esbuild": {
"version": "0.15.18", "version": "0.15.18",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz",

View File

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

View File

@ -5,6 +5,7 @@ import {
Object3D, Object3D,
ShaderMaterial, ShaderMaterial,
SphereGeometry, SphereGeometry,
Vector3,
} from 'three'; } from 'three';
import { plainText as vertexShader } from './shaders/atmosphere.vert'; import { plainText as vertexShader } from './shaders/atmosphere.vert';
@ -17,7 +18,7 @@ export class Atmosphere extends Object3D {
public Scale = 1; public Scale = 1;
public ScaleDepth = 0.25; public ScaleDepth = 0.25;
public G = -0.95; 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 geom?: SphereGeometry;
public mesh?: Mesh; public mesh?: Mesh;
@ -35,12 +36,30 @@ export class Atmosphere extends Object3D {
this.initialize(); 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) { setUniforms(shader: ShaderMaterial) {
shader.uniforms.invWavelength = { shader.uniforms.invWavelength = {
value: [ value: [
1 / Math.pow(this.Wavelength[0], 4), 1 / Math.pow(this.Wavelength.x, 4),
1 / Math.pow(this.Wavelength[1], 4), 1 / Math.pow(this.Wavelength.y, 4),
1 / Math.pow(this.Wavelength[2], 4), 1 / Math.pow(this.Wavelength.z, 4),
], ],
}; };
shader.uniforms.outerRadius = { value: this.outerRadius }; shader.uniforms.outerRadius = { value: this.outerRadius };
@ -60,8 +79,18 @@ export class Atmosphere extends Object3D {
} }
initialize() { initialize() {
if (this.geom) {
this.geom.dispose();
this.mesh && this.remove(this.mesh);
}
this.geom = new SphereGeometry(this.outerRadius, 128, 128); this.geom = new SphereGeometry(this.outerRadius, 128, 128);
this.mesh = new Mesh(this.geom, this.shader); this.mesh = new Mesh(this.geom, this.shader);
this.add(this.mesh); 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, Color,
DirectionalLight, DirectionalLight,
ShaderMaterial, ShaderMaterial,
MeshPhongMaterial,
} from 'three'; } from 'three';
import { plainText as vertexShader } from './shaders/earth.vert'; import { plainText as vertexShader } from './shaders/earth.vert';
@ -14,15 +15,31 @@ import { plainText as fragmentShader } from './shaders/earth.frag';
import { Atmosphere } from './atmosphere'; import { Atmosphere } from './atmosphere';
export class Globe extends Object3D { export class Globe extends Object3D {
private sphere = new SphereGeometry(1, 128, 128); private sphere?: SphereGeometry;
private atmosphere = new Atmosphere(1, 1.05); private clouds = new SphereGeometry(1.02, 64, 64);
private earthMaterial?: ShaderMaterial; public atmosphere = new Atmosphere(1, 1.05);
public earthMaterial?: ShaderMaterial;
public earthMesh?: Mesh; public earthMesh?: Mesh;
private loader = new TextureLoader(); 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) { public async loadTexture(name: string) {
return new Promise<Texture>((resolve, reject) => { 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.setUniforms(this.earthMaterial);
this.atmosphere.setLight(light); this.atmosphere.setLight(light);
this.earthMesh = new Mesh(this.sphere, this.earthMaterial); this.createMesh();
this.add(this.earthMesh);
this.add(this.atmosphere); this.add(this.atmosphere);
} }
public tick() {} 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 { DirectionalLight } from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import './style.css'; import './style.css';
import { GUI } from 'dat.gui';
const main = new Renderer(); const main = new Renderer();
@ -10,7 +11,7 @@ const dirLight = new DirectionalLight(0xffffff, 1);
dirLight.position.set(10, 10, 10); dirLight.position.set(10, 10, 10);
main.scene.add(dirLight); main.scene.add(dirLight);
const globe = new Globe(); const globe = new Globe(1);
main.resize(); main.resize();
globe.initialize(dirLight).catch(console.error); globe.initialize(dirLight).catch(console.error);
main.scene.add(globe); main.scene.add(globe);
@ -19,6 +20,58 @@ main.camera.position.set(0, 0, 2);
const control = new OrbitControls(main.camera, main.canvas); 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() { function tick() {
requestAnimationFrame(tick); requestAnimationFrame(tick);
control.update(); control.update();

View File

@ -1,10 +1,16 @@
#define EARTH #define EARTH
#include <common>
#include <normal_pars_vertex>
varying vec3 vViewPosition; varying vec3 vViewPosition;
varying vec3 vLightDirection; varying vec3 vLightDirection;
varying vec2 vUv; varying vec2 vUv;
varying vec3 vEyeNormal; varying vec3 vEyeNormal;
#include <common>
#include <normal_pars_vertex> varying vec3 v3Direction;
varying vec3 c0;
varying vec3 c1;
uniform vec3 light; uniform vec3 light;
uniform vec3 invWavelength; // 1 / pow(wavelength, 4) for the red, green, and blue channels 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 int nSamples = 2;
const float fSamples = 1.0; const float fSamples = 1.0;
varying vec3 v3Direction;
varying vec3 c0;
varying vec3 c1;
float dscale(float fCos) { float dscale(float fCos) {
float x = 1.0 - fCos; float x = 1.0 - fCos;
return scaleDepth * exp(-0.00287 + x * (0.459 + x * (3.83 + x * (-6.80 + x * 5.25)))); 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({ export default defineConfig({
plugins: [plainText.default(/\.vert|\.frag$/)], plugins: [plainText.default(/\.vert|\.frag$/)],
base: '',
}); });