kopia lustrzana https://github.com/robhawkes/vizicities
Merging skybox branch
commit
8fd4c799e1
Plik diff jest za duży
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,14 +1,10 @@
|
|||
var world = VIZI.World('world').setView([51.505, -0.09]);
|
||||
var world = VIZI.World('world', {
|
||||
skybox: true
|
||||
}).setView([51.505, -0.09]);
|
||||
|
||||
// Add controls
|
||||
VIZI.Controls.Orbit().addTo(world);
|
||||
|
||||
// Not sure if I want to keep this as a public API
|
||||
//
|
||||
// Makes sense to allow others to customise their environment so perhaps this
|
||||
// could be left public but a default is set up within World to simplify things
|
||||
var environmentLayer = VIZI.EnvironmentLayer().addTo(world);
|
||||
|
||||
// // http://{s}.tile.osm.org/{z}/{x}/{y}.png
|
||||
// // http://{s}.tiles.wmflabs.org/osm-no-labels/{z}/{x}/{y}.png
|
||||
var imageTileLayer = VIZI.ImageTileLayer('http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png', {
|
||||
|
@ -18,7 +14,8 @@ var imageTileLayer = VIZI.ImageTileLayer('http://{s}.basemaps.cartocdn.com/light
|
|||
var topoJSONTileLayer = VIZI.TopoJSONTileLayer('https://vector.mapzen.com/osm/buildings/{z}/{x}/{y}.topojson', {
|
||||
style: function(feature) {
|
||||
return {
|
||||
color: (feature.properties.area > 10000) ? '#ff0000' : '#0000ff'
|
||||
// color: (feature.properties.area > 10000) ? '#ff0000' : '#0000ff'
|
||||
color: '#cccccc'
|
||||
};
|
||||
},
|
||||
// filter: function(feature) {
|
||||
|
|
|
@ -7,7 +7,7 @@ import THREE from 'three';
|
|||
// http://stackoverflow.com/q/26655930/997339
|
||||
|
||||
export default function(container) {
|
||||
var camera = new THREE.PerspectiveCamera(45, 1, 1, 40000);
|
||||
var camera = new THREE.PerspectiveCamera(45, 1, 1, 200000);
|
||||
camera.position.y = 400;
|
||||
camera.position.z = 400;
|
||||
|
|
@ -9,7 +9,10 @@ export default function(container) {
|
|||
antialias: true
|
||||
});
|
||||
|
||||
renderer.setClearColor(Scene.fog.color, 1);
|
||||
// TODO: Re-enable when this works with the skybox
|
||||
// renderer.setClearColor(Scene.fog.color, 1);
|
||||
|
||||
renderer.setClearColor(0xffffff, 1);
|
||||
|
||||
// Gamma settings make things look nicer
|
||||
renderer.gammaInput = true;
|
|
@ -5,6 +5,8 @@ import THREE from 'three';
|
|||
|
||||
export default (function() {
|
||||
var scene = new THREE.Scene();
|
||||
scene.fog = new THREE.Fog(0xffffff, 1, 15000);
|
||||
|
||||
// TODO: Re-enable when this works with the skybox
|
||||
// scene.fog = new THREE.Fog(0xffffff, 1, 15000);
|
||||
return scene;
|
||||
})();
|
204
src/World.js
204
src/World.js
|
@ -1,204 +0,0 @@
|
|||
import EventEmitter from 'eventemitter3';
|
||||
import extend from 'lodash.assign';
|
||||
import CRS from './geo/CRS/index';
|
||||
import Point from './geo/Point';
|
||||
import LatLon from './geo/LatLon';
|
||||
import Engine from './engine/Engine';
|
||||
|
||||
// Pretty much any event someone using ViziCities would need will be emitted or
|
||||
// proxied by World (eg. render events, etc)
|
||||
|
||||
class World extends EventEmitter {
|
||||
constructor(domId, options) {
|
||||
super();
|
||||
|
||||
var defaults = {
|
||||
crs: CRS.EPSG3857
|
||||
};
|
||||
|
||||
this.options = extend(defaults, options);
|
||||
|
||||
this._layers = [];
|
||||
this._controls = [];
|
||||
|
||||
this._initContainer(domId);
|
||||
this._initEngine();
|
||||
this._initEvents();
|
||||
|
||||
// Kick off the update and render loop
|
||||
this._update();
|
||||
}
|
||||
|
||||
_initContainer(domId) {
|
||||
this._container = document.getElementById(domId);
|
||||
}
|
||||
|
||||
_initEngine() {
|
||||
this._engine = Engine(this._container);
|
||||
|
||||
// Engine events
|
||||
//
|
||||
// Consider proxying these through events on World for public access
|
||||
// this._engine.on('preRender', () => {});
|
||||
// this._engine.on('postRender', () => {});
|
||||
}
|
||||
|
||||
_initEvents() {
|
||||
this.on('controlsMoveEnd', this._onControlsMoveEnd);
|
||||
}
|
||||
|
||||
_onControlsMoveEnd(point) {
|
||||
var _point = Point(point.x, point.z);
|
||||
this._resetView(this.pointToLatLon(_point), _point);
|
||||
}
|
||||
|
||||
// Reset world view
|
||||
_resetView(latlon, point) {
|
||||
this.emit('preResetView');
|
||||
|
||||
this._moveStart();
|
||||
this._move(latlon, point);
|
||||
this._moveEnd();
|
||||
|
||||
this.emit('postResetView');
|
||||
}
|
||||
|
||||
_moveStart() {
|
||||
this.emit('moveStart');
|
||||
}
|
||||
|
||||
_move(latlon, point) {
|
||||
this._lastPosition = latlon;
|
||||
this.emit('move', latlon, point);
|
||||
}
|
||||
_moveEnd() {
|
||||
this.emit('moveEnd');
|
||||
}
|
||||
|
||||
_update() {
|
||||
var delta = this._engine.clock.getDelta();
|
||||
|
||||
// Once _update is called it will run forever, for now
|
||||
window.requestAnimationFrame(this._update.bind(this));
|
||||
|
||||
// Update controls
|
||||
this._controls.forEach(controls => {
|
||||
controls.update();
|
||||
});
|
||||
|
||||
this.emit('preUpdate');
|
||||
this._engine.update(delta);
|
||||
this.emit('postUpdate');
|
||||
}
|
||||
|
||||
// Set world view
|
||||
setView(latlon) {
|
||||
// Store initial geographic coordinate for the [0,0,0] world position
|
||||
//
|
||||
// The origin point doesn't move in three.js / 3D space so only set it once
|
||||
// here instead of every time _resetView is called
|
||||
//
|
||||
// If it was updated every time then coorindates would shift over time and
|
||||
// would be out of place / context with previously-placed points (0,0 would
|
||||
// refer to a different point each time)
|
||||
this._originLatlon = latlon;
|
||||
this._originPoint = this.project(latlon);
|
||||
|
||||
this._resetView(latlon);
|
||||
return this;
|
||||
}
|
||||
|
||||
// Return world geographic position
|
||||
getPosition() {
|
||||
return this._lastPosition;
|
||||
}
|
||||
|
||||
// Transform geographic coordinate to world point
|
||||
//
|
||||
// This doesn't take into account the origin offset
|
||||
//
|
||||
// For example, this takes a geographic coordinate and returns a point
|
||||
// relative to the origin point of the projection (not the world)
|
||||
project(latlon) {
|
||||
return this.options.crs.latLonToPoint(LatLon(latlon));
|
||||
}
|
||||
|
||||
// Transform world point to geographic coordinate
|
||||
//
|
||||
// This doesn't take into account the origin offset
|
||||
//
|
||||
// For example, this takes a point relative to the origin point of the
|
||||
// projection (not the world) and returns a geographic coordinate
|
||||
unproject(point) {
|
||||
return this.options.crs.pointToLatLon(Point(point));
|
||||
}
|
||||
|
||||
// Takes into account the origin offset
|
||||
//
|
||||
// For example, this takes a geographic coordinate and returns a point
|
||||
// relative to the three.js / 3D origin (0,0)
|
||||
latLonToPoint(latlon) {
|
||||
var projectedPoint = this.project(LatLon(latlon));
|
||||
return projectedPoint._subtract(this._originPoint);
|
||||
}
|
||||
|
||||
// Takes into account the origin offset
|
||||
//
|
||||
// For example, this takes a point relative to the three.js / 3D origin (0,0)
|
||||
// and returns the exact geographic coordinate at that point
|
||||
pointToLatLon(point) {
|
||||
var projectedPoint = Point(point).add(this._originPoint);
|
||||
return this.unproject(projectedPoint);
|
||||
}
|
||||
|
||||
// Unsure if it's a good idea to expose this here for components like
|
||||
// GridLayer to use (eg. to keep track of a frustum)
|
||||
getCamera() {
|
||||
return this._engine._camera;
|
||||
}
|
||||
|
||||
addLayer(layer) {
|
||||
layer._addToWorld(this);
|
||||
|
||||
this._layers.push(layer);
|
||||
|
||||
// Could move this into Layer but it'll do here for now
|
||||
this._engine._scene.add(layer._layer);
|
||||
|
||||
this.emit('layerAdded', layer);
|
||||
return this;
|
||||
}
|
||||
|
||||
// Remove layer and perform clean up operations
|
||||
removeLayer(layer) {
|
||||
var layerIndex = this._layers.indexOf(layer);
|
||||
|
||||
if (layerIndex > -1) {
|
||||
// Remove from this._layers
|
||||
this._layers.splice(layerIndex, 1);
|
||||
};
|
||||
|
||||
this._engine._scene.remove(layer._layer);
|
||||
|
||||
layer.destroy();
|
||||
|
||||
this.emit('layerRemoved');
|
||||
return this;
|
||||
}
|
||||
|
||||
addControls(controls) {
|
||||
controls._addToWorld(this);
|
||||
|
||||
this._controls.push(controls);
|
||||
|
||||
this.emit('controlsAdded', controls);
|
||||
return this;
|
||||
}
|
||||
|
||||
removeControls(controls) {}
|
||||
}
|
||||
|
||||
// Initialise without requiring new keyword
|
||||
export default function(domId, options) {
|
||||
return new World(domId, options);
|
||||
};
|
|
@ -1,15 +1,28 @@
|
|||
import Layer from '../Layer';
|
||||
import extend from 'lodash.assign';
|
||||
import THREE from 'three';
|
||||
import Skybox from './Skybox';
|
||||
|
||||
class EnvironmentLayer extends Layer {
|
||||
constructor() {
|
||||
constructor(options) {
|
||||
super();
|
||||
|
||||
this._initLights();
|
||||
// this._initGrid();
|
||||
var defaults = {
|
||||
skybox: false
|
||||
};
|
||||
|
||||
this._options = extend(defaults, options);
|
||||
}
|
||||
|
||||
_onAdd() {}
|
||||
_onAdd() {
|
||||
this._initLights();
|
||||
|
||||
if (this._options.skybox) {
|
||||
this._initSkybox();
|
||||
}
|
||||
|
||||
// this._initGrid();
|
||||
}
|
||||
|
||||
// Not fleshed out or thought through yet
|
||||
//
|
||||
|
@ -19,26 +32,37 @@ class EnvironmentLayer extends Layer {
|
|||
// Position doesn't really matter (the angle is important), however it's
|
||||
// used here so the helpers look more natural.
|
||||
|
||||
var directionalLight = new THREE.DirectionalLight(0x999999);
|
||||
directionalLight.intesity = 0.1;
|
||||
directionalLight.position.x = 100;
|
||||
directionalLight.position.y = 100;
|
||||
directionalLight.position.z = 100;
|
||||
if (!this._options.skybox) {
|
||||
var directionalLight = new THREE.DirectionalLight(0x999999);
|
||||
directionalLight.intesity = 0.1;
|
||||
directionalLight.position.x = 100;
|
||||
directionalLight.position.y = 100;
|
||||
directionalLight.position.z = 100;
|
||||
|
||||
var directionalLight2 = new THREE.DirectionalLight(0x999999);
|
||||
directionalLight2.intesity = 0.1;
|
||||
directionalLight2.position.x = -100;
|
||||
directionalLight2.position.y = 100;
|
||||
directionalLight2.position.z = -100;
|
||||
var directionalLight2 = new THREE.DirectionalLight(0x999999);
|
||||
directionalLight2.intesity = 0.1;
|
||||
directionalLight2.position.x = -100;
|
||||
directionalLight2.position.y = 100;
|
||||
directionalLight2.position.z = -100;
|
||||
|
||||
var helper = new THREE.DirectionalLightHelper(directionalLight, 10);
|
||||
var helper2 = new THREE.DirectionalLightHelper(directionalLight2, 10);
|
||||
var helper = new THREE.DirectionalLightHelper(directionalLight, 10);
|
||||
var helper2 = new THREE.DirectionalLightHelper(directionalLight2, 10);
|
||||
|
||||
this._layer.add(directionalLight);
|
||||
this._layer.add(directionalLight2);
|
||||
this._layer.add(directionalLight);
|
||||
this._layer.add(directionalLight2);
|
||||
|
||||
this._layer.add(helper);
|
||||
this._layer.add(helper2);
|
||||
this._layer.add(helper);
|
||||
this._layer.add(helper2);
|
||||
} else {
|
||||
// Directional light that will be projected from the sun
|
||||
this._skyboxLight = new THREE.DirectionalLight(0xffffff, 1);
|
||||
this._layer.add(this._skyboxLight);
|
||||
}
|
||||
}
|
||||
|
||||
_initSkybox() {
|
||||
this._skybox = Skybox(this._world, this._skyboxLight);
|
||||
this._layer.add(this._skybox._mesh);
|
||||
}
|
||||
|
||||
// Add grid helper for context during initial development
|
||||
|
@ -49,9 +73,20 @@ class EnvironmentLayer extends Layer {
|
|||
var gridHelper = new THREE.GridHelper(size, step);
|
||||
this._layer.add(gridHelper);
|
||||
}
|
||||
|
||||
// Clean up environment
|
||||
destroy() {
|
||||
this._skyboxLight = null;
|
||||
|
||||
this._layer.remove(this._skybox._mesh);
|
||||
this._skybox.destroy();
|
||||
this._skybox = null;
|
||||
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
// Initialise without requiring new keyword
|
||||
export default function() {
|
||||
return new EnvironmentLayer();
|
||||
export default function(options) {
|
||||
return new EnvironmentLayer(options);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,268 @@
|
|||
// jscs:disable
|
||||
/*eslint eqeqeq:0*/
|
||||
|
||||
/**
|
||||
* @author zz85 / https://github.com/zz85
|
||||
*
|
||||
* Based on 'A Practical Analytic Model for Daylight'
|
||||
* aka The Preetham Model, the de facto standard analytic skydome model
|
||||
* http://www.cs.utah.edu/~shirley/papers/sunsky/sunsky.pdf
|
||||
*
|
||||
* First implemented by Simon Wallner
|
||||
* http://www.simonwallner.at/projects/atmospheric-scattering
|
||||
*
|
||||
* Improved by Martin Upitis
|
||||
* http://blenderartists.org/forum/showthread.php?245954-preethams-sky-impementation-HDR
|
||||
*
|
||||
* Three.js integration by zz85 http://twitter.com/blurspline
|
||||
*/
|
||||
|
||||
import THREE from 'three';
|
||||
|
||||
THREE.ShaderLib[ 'sky' ] = {
|
||||
|
||||
uniforms: {
|
||||
|
||||
luminance: { type: 'f', value: 1 },
|
||||
turbidity: { type: 'f', value: 2 },
|
||||
reileigh: { type: 'f', value: 1 },
|
||||
mieCoefficient: { type: 'f', value: 0.005 },
|
||||
mieDirectionalG: { type: 'f', value: 0.8 },
|
||||
sunPosition: { type: 'v3', value: new THREE.Vector3() }
|
||||
|
||||
},
|
||||
|
||||
vertexShader: [
|
||||
|
||||
'varying vec3 vWorldPosition;',
|
||||
|
||||
'void main() {',
|
||||
|
||||
'vec4 worldPosition = modelMatrix * vec4( position, 1.0 );',
|
||||
'vWorldPosition = worldPosition.xyz;',
|
||||
|
||||
'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
|
||||
|
||||
'}',
|
||||
|
||||
].join( '\n' ),
|
||||
|
||||
fragmentShader: [
|
||||
|
||||
'uniform sampler2D skySampler;',
|
||||
'uniform vec3 sunPosition;',
|
||||
'varying vec3 vWorldPosition;',
|
||||
|
||||
'vec3 cameraPos = vec3(0., 0., 0.);',
|
||||
'// uniform sampler2D sDiffuse;',
|
||||
'// const float turbidity = 10.0; //',
|
||||
'// const float reileigh = 2.; //',
|
||||
'// const float luminance = 1.0; //',
|
||||
'// const float mieCoefficient = 0.005;',
|
||||
'// const float mieDirectionalG = 0.8;',
|
||||
|
||||
'uniform float luminance;',
|
||||
'uniform float turbidity;',
|
||||
'uniform float reileigh;',
|
||||
'uniform float mieCoefficient;',
|
||||
'uniform float mieDirectionalG;',
|
||||
|
||||
'// constants for atmospheric scattering',
|
||||
'const float e = 2.71828182845904523536028747135266249775724709369995957;',
|
||||
'const float pi = 3.141592653589793238462643383279502884197169;',
|
||||
|
||||
'const float n = 1.0003; // refractive index of air',
|
||||
'const float N = 2.545E25; // number of molecules per unit volume for air at',
|
||||
'// 288.15K and 1013mb (sea level -45 celsius)',
|
||||
'const float pn = 0.035; // depolatization factor for standard air',
|
||||
|
||||
'// wavelength of used primaries, according to preetham',
|
||||
'const vec3 lambda = vec3(680E-9, 550E-9, 450E-9);',
|
||||
|
||||
'// mie stuff',
|
||||
'// K coefficient for the primaries',
|
||||
'const vec3 K = vec3(0.686, 0.678, 0.666);',
|
||||
'const float v = 4.0;',
|
||||
|
||||
'// optical length at zenith for molecules',
|
||||
'const float rayleighZenithLength = 8.4E3;',
|
||||
'const float mieZenithLength = 1.25E3;',
|
||||
'const vec3 up = vec3(0.0, 1.0, 0.0);',
|
||||
|
||||
'const float EE = 1000.0;',
|
||||
'const float sunAngularDiameterCos = 0.999956676946448443553574619906976478926848692873900859324;',
|
||||
'// 66 arc seconds -> degrees, and the cosine of that',
|
||||
|
||||
'// earth shadow hack',
|
||||
'const float cutoffAngle = pi/1.95;',
|
||||
'const float steepness = 1.5;',
|
||||
|
||||
|
||||
'vec3 totalRayleigh(vec3 lambda)',
|
||||
'{',
|
||||
'return (8.0 * pow(pi, 3.0) * pow(pow(n, 2.0) - 1.0, 2.0) * (6.0 + 3.0 * pn)) / (3.0 * N * pow(lambda, vec3(4.0)) * (6.0 - 7.0 * pn));',
|
||||
'}',
|
||||
|
||||
// see http://blenderartists.org/forum/showthread.php?321110-Shaders-and-Skybox-madness
|
||||
'// A simplied version of the total Reayleigh scattering to works on browsers that use ANGLE',
|
||||
'vec3 simplifiedRayleigh()',
|
||||
'{',
|
||||
'return 0.0005 / vec3(94, 40, 18);',
|
||||
// return 0.00054532832366 / (3.0 * 2.545E25 * pow(vec3(680E-9, 550E-9, 450E-9), vec3(4.0)) * 6.245);
|
||||
'}',
|
||||
|
||||
'float rayleighPhase(float cosTheta)',
|
||||
'{ ',
|
||||
'return (3.0 / (16.0*pi)) * (1.0 + pow(cosTheta, 2.0));',
|
||||
'// return (1.0 / (3.0*pi)) * (1.0 + pow(cosTheta, 2.0));',
|
||||
'// return (3.0 / 4.0) * (1.0 + pow(cosTheta, 2.0));',
|
||||
'}',
|
||||
|
||||
'vec3 totalMie(vec3 lambda, vec3 K, float T)',
|
||||
'{',
|
||||
'float c = (0.2 * T ) * 10E-18;',
|
||||
'return 0.434 * c * pi * pow((2.0 * pi) / lambda, vec3(v - 2.0)) * K;',
|
||||
'}',
|
||||
|
||||
'float hgPhase(float cosTheta, float g)',
|
||||
'{',
|
||||
'return (1.0 / (4.0*pi)) * ((1.0 - pow(g, 2.0)) / pow(1.0 - 2.0*g*cosTheta + pow(g, 2.0), 1.5));',
|
||||
'}',
|
||||
|
||||
'float sunIntensity(float zenithAngleCos)',
|
||||
'{',
|
||||
'return EE * max(0.0, 1.0 - exp(-((cutoffAngle - acos(zenithAngleCos))/steepness)));',
|
||||
'}',
|
||||
|
||||
'// float logLuminance(vec3 c)',
|
||||
'// {',
|
||||
'// return log(c.r * 0.2126 + c.g * 0.7152 + c.b * 0.0722);',
|
||||
'// }',
|
||||
|
||||
'// Filmic ToneMapping http://filmicgames.com/archives/75',
|
||||
'float A = 0.15;',
|
||||
'float B = 0.50;',
|
||||
'float C = 0.10;',
|
||||
'float D = 0.20;',
|
||||
'float E = 0.02;',
|
||||
'float F = 0.30;',
|
||||
'float W = 1000.0;',
|
||||
|
||||
'vec3 Uncharted2Tonemap(vec3 x)',
|
||||
'{',
|
||||
'return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;',
|
||||
'}',
|
||||
|
||||
|
||||
'void main() ',
|
||||
'{',
|
||||
'float sunfade = 1.0-clamp(1.0-exp((sunPosition.y/450000.0)),0.0,1.0);',
|
||||
|
||||
'// luminance = 1.0 ;// vWorldPosition.y / 450000. + 0.5; //sunPosition.y / 450000. * 1. + 0.5;',
|
||||
|
||||
'// gl_FragColor = vec4(sunfade, sunfade, sunfade, 1.0);',
|
||||
|
||||
'float reileighCoefficient = reileigh - (1.0* (1.0-sunfade));',
|
||||
|
||||
'vec3 sunDirection = normalize(sunPosition);',
|
||||
|
||||
'float sunE = sunIntensity(dot(sunDirection, up));',
|
||||
|
||||
'// extinction (absorbtion + out scattering) ',
|
||||
'// rayleigh coefficients',
|
||||
|
||||
// 'vec3 betaR = totalRayleigh(lambda) * reileighCoefficient;',
|
||||
'vec3 betaR = simplifiedRayleigh() * reileighCoefficient;',
|
||||
|
||||
'// mie coefficients',
|
||||
'vec3 betaM = totalMie(lambda, K, turbidity) * mieCoefficient;',
|
||||
|
||||
'// optical length',
|
||||
'// cutoff angle at 90 to avoid singularity in next formula.',
|
||||
'float zenithAngle = acos(max(0.0, dot(up, normalize(vWorldPosition - cameraPos))));',
|
||||
'float sR = rayleighZenithLength / (cos(zenithAngle) + 0.15 * pow(93.885 - ((zenithAngle * 180.0) / pi), -1.253));',
|
||||
'float sM = mieZenithLength / (cos(zenithAngle) + 0.15 * pow(93.885 - ((zenithAngle * 180.0) / pi), -1.253));',
|
||||
|
||||
|
||||
|
||||
'// combined extinction factor ',
|
||||
'vec3 Fex = exp(-(betaR * sR + betaM * sM));',
|
||||
|
||||
'// in scattering',
|
||||
'float cosTheta = dot(normalize(vWorldPosition - cameraPos), sunDirection);',
|
||||
|
||||
'float rPhase = rayleighPhase(cosTheta*0.5+0.5);',
|
||||
'vec3 betaRTheta = betaR * rPhase;',
|
||||
|
||||
'float mPhase = hgPhase(cosTheta, mieDirectionalG);',
|
||||
'vec3 betaMTheta = betaM * mPhase;',
|
||||
|
||||
|
||||
'vec3 Lin = pow(sunE * ((betaRTheta + betaMTheta) / (betaR + betaM)) * (1.0 - Fex),vec3(1.5));',
|
||||
'Lin *= mix(vec3(1.0),pow(sunE * ((betaRTheta + betaMTheta) / (betaR + betaM)) * Fex,vec3(1.0/2.0)),clamp(pow(1.0-dot(up, sunDirection),5.0),0.0,1.0));',
|
||||
|
||||
'//nightsky',
|
||||
'vec3 direction = normalize(vWorldPosition - cameraPos);',
|
||||
'float theta = acos(direction.y); // elevation --> y-axis, [-pi/2, pi/2]',
|
||||
'float phi = atan(direction.z, direction.x); // azimuth --> x-axis [-pi/2, pi/2]',
|
||||
'vec2 uv = vec2(phi, theta) / vec2(2.0*pi, pi) + vec2(0.5, 0.0);',
|
||||
'// vec3 L0 = texture2D(skySampler, uv).rgb+0.1 * Fex;',
|
||||
'vec3 L0 = vec3(0.1) * Fex;',
|
||||
|
||||
'// composition + solar disc',
|
||||
'//if (cosTheta > sunAngularDiameterCos)',
|
||||
'float sundisk = smoothstep(sunAngularDiameterCos,sunAngularDiameterCos+0.00002,cosTheta);',
|
||||
'// if (normalize(vWorldPosition - cameraPos).y>0.0)',
|
||||
'L0 += (sunE * 19000.0 * Fex)*sundisk;',
|
||||
|
||||
|
||||
'vec3 whiteScale = 1.0/Uncharted2Tonemap(vec3(W));',
|
||||
|
||||
'vec3 texColor = (Lin+L0); ',
|
||||
'texColor *= 0.04 ;',
|
||||
'texColor += vec3(0.0,0.001,0.0025)*0.3;',
|
||||
|
||||
'float g_fMaxLuminance = 1.0;',
|
||||
'float fLumScaled = 0.1 / luminance; ',
|
||||
'float fLumCompressed = (fLumScaled * (1.0 + (fLumScaled / (g_fMaxLuminance * g_fMaxLuminance)))) / (1.0 + fLumScaled); ',
|
||||
|
||||
'float ExposureBias = fLumCompressed;',
|
||||
|
||||
'vec3 curr = Uncharted2Tonemap((log2(2.0/pow(luminance,4.0)))*texColor);',
|
||||
'vec3 color = curr*whiteScale;',
|
||||
|
||||
'vec3 retColor = pow(color,vec3(1.0/(1.2+(1.2*sunfade))));',
|
||||
|
||||
|
||||
'gl_FragColor.rgb = retColor;',
|
||||
|
||||
'gl_FragColor.a = 1.0;',
|
||||
'}',
|
||||
|
||||
].join( '\n' )
|
||||
|
||||
};
|
||||
|
||||
var Sky = function () {
|
||||
|
||||
var skyShader = THREE.ShaderLib[ 'sky' ];
|
||||
var skyUniforms = THREE.UniformsUtils.clone( skyShader.uniforms );
|
||||
|
||||
var skyMat = new THREE.ShaderMaterial( {
|
||||
fragmentShader: skyShader.fragmentShader,
|
||||
vertexShader: skyShader.vertexShader,
|
||||
uniforms: skyUniforms,
|
||||
side: THREE.BackSide
|
||||
} );
|
||||
|
||||
var skyGeo = new THREE.SphereBufferGeometry( 450000, 32, 15 );
|
||||
var skyMesh = new THREE.Mesh( skyGeo, skyMat );
|
||||
|
||||
|
||||
// Expose variables
|
||||
this.mesh = skyMesh;
|
||||
this.uniforms = skyUniforms;
|
||||
|
||||
};
|
||||
|
||||
export default Sky;
|
|
@ -0,0 +1,195 @@
|
|||
import THREE from 'three';
|
||||
import Sky from './Sky';
|
||||
import throttle from 'lodash.throttle';
|
||||
|
||||
var cubemap = {
|
||||
vertexShader: [
|
||||
'varying vec3 vPosition;',
|
||||
'void main() {',
|
||||
'vPosition = position;',
|
||||
'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
|
||||
'}'
|
||||
].join('\n'),
|
||||
|
||||
fragmentShader: [
|
||||
'uniform samplerCube cubemap;',
|
||||
'varying vec3 vPosition;',
|
||||
|
||||
'void main() {',
|
||||
'gl_FragColor = textureCube(cubemap, normalize(vPosition));',
|
||||
'}'
|
||||
].join('\n')
|
||||
};
|
||||
|
||||
class Skybox {
|
||||
constructor(world, light) {
|
||||
this._world = world;
|
||||
this._light = light;
|
||||
|
||||
this._settings = {
|
||||
distance: 40000,
|
||||
turbidity: 10,
|
||||
reileigh: 2,
|
||||
mieCoefficient: 0.005,
|
||||
mieDirectionalG: 0.8,
|
||||
luminance: 1,
|
||||
inclination: 0.48, // Elevation / inclination
|
||||
azimuth: 0.25, // Facing front
|
||||
};
|
||||
|
||||
this._initSkybox();
|
||||
this._updateUniforms();
|
||||
this._initEvents();
|
||||
}
|
||||
|
||||
_initEvents() {
|
||||
// Throttled to 1 per 100ms
|
||||
this._throttledWorldUpdate = throttle(this._update, 100);
|
||||
this._world.on('preUpdate', this._throttledWorldUpdate, this);
|
||||
}
|
||||
|
||||
_initSkybox() {
|
||||
// Cube camera for skybox
|
||||
this._cubeCamera = new THREE.CubeCamera(1, 2000000, 128);
|
||||
|
||||
// Cube material
|
||||
var cubeTarget = this._cubeCamera.renderTarget;
|
||||
|
||||
// Add Sky Mesh
|
||||
this._sky = new Sky();
|
||||
this._skyScene = new THREE.Scene();
|
||||
this._skyScene.add(this._sky.mesh);
|
||||
|
||||
// Add Sun Helper
|
||||
this._sunSphere = new THREE.Mesh(
|
||||
new THREE.SphereBufferGeometry(2000, 16, 8),
|
||||
new THREE.MeshBasicMaterial({
|
||||
color: 0xffffff
|
||||
})
|
||||
);
|
||||
this._sunSphere.position.y = -700000;
|
||||
this._sunSphere.visible = false;
|
||||
|
||||
var skyboxUniforms = {
|
||||
cubemap: { type: 't', value: cubeTarget }
|
||||
};
|
||||
|
||||
var skyboxMat = new THREE.ShaderMaterial({
|
||||
uniforms: skyboxUniforms,
|
||||
vertexShader: cubemap.vertexShader,
|
||||
fragmentShader: cubemap.fragmentShader,
|
||||
side: THREE.BackSide
|
||||
});
|
||||
|
||||
this._mesh = new THREE.Mesh(new THREE.BoxGeometry(190000, 190000, 190000), skyboxMat);
|
||||
}
|
||||
|
||||
_updateUniforms() {
|
||||
var settings = this._settings;
|
||||
var uniforms = this._sky.uniforms;
|
||||
uniforms.turbidity.value = settings.turbidity;
|
||||
uniforms.reileigh.value = settings.reileigh;
|
||||
uniforms.luminance.value = settings.luminance;
|
||||
uniforms.mieCoefficient.value = settings.mieCoefficient;
|
||||
uniforms.mieDirectionalG.value = settings.mieDirectionalG;
|
||||
|
||||
var theta = Math.PI * (settings.inclination - 0.5);
|
||||
var phi = 2 * Math.PI * (settings.azimuth - 0.5);
|
||||
|
||||
this._sunSphere.position.x = settings.distance * Math.cos(phi);
|
||||
this._sunSphere.position.y = settings.distance * Math.sin(phi) * Math.sin(theta);
|
||||
this._sunSphere.position.z = settings.distance * Math.sin(phi) * Math.cos(theta);
|
||||
|
||||
// Move directional light to sun position
|
||||
this._light.position.copy(this._sunSphere.position);
|
||||
|
||||
this._sky.uniforms.sunPosition.value.copy(this._sunSphere.position);
|
||||
}
|
||||
|
||||
_update(delta) {
|
||||
if (!this._done) {
|
||||
this._done = true;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// if (!this._angle) {
|
||||
// this._angle = 0;
|
||||
// }
|
||||
//
|
||||
// // Animate inclination
|
||||
// this._angle += Math.PI * delta;
|
||||
// this._settings.inclination = 0.5 * (Math.sin(this._angle) / 2 + 0.5);
|
||||
|
||||
// Update light intensity depending on elevation of sun (day to night)
|
||||
this._light.intensity = 1 - 0.95 * (this._settings.inclination / 0.5);
|
||||
|
||||
// // console.log(delta, this._angle, this._settings.inclination);
|
||||
//
|
||||
// TODO: Only do this when the uniforms have been changed
|
||||
this._updateUniforms();
|
||||
|
||||
// TODO: Only do this when the cubemap has actually changed
|
||||
this._cubeCamera.updateCubeMap(this._world._engine._renderer, this._skyScene);
|
||||
}
|
||||
|
||||
getRenderTarget() {
|
||||
return this._cubeCamera.renderTarget;
|
||||
}
|
||||
|
||||
// Destroy the skybox and remove it from memory
|
||||
destroy() {
|
||||
this._world.off('preUpdate', this._throttledWorldUpdate);
|
||||
this._throttledWorldUpdate = null;
|
||||
|
||||
this._world = null;
|
||||
this._light = null;
|
||||
|
||||
this._cubeCamera = null;
|
||||
|
||||
this._sky.mesh.geometry.dispose();
|
||||
this._sky.mesh.geometry = null;
|
||||
|
||||
if (this._sky.mesh.material.map) {
|
||||
this._sky.mesh.material.map.dispose();
|
||||
this._sky.mesh.material.map = null;
|
||||
}
|
||||
|
||||
this._sky.mesh.material.dispose();
|
||||
this._sky.mesh.material = null;
|
||||
|
||||
this._sky.mesh = null;
|
||||
this._sky = null;
|
||||
|
||||
this._skyScene = null;
|
||||
|
||||
this._sunSphere.geometry.dispose();
|
||||
this._sunSphere.geometry = null;
|
||||
|
||||
if (this._sunSphere.material.map) {
|
||||
this._sunSphere.material.map.dispose();
|
||||
this._sunSphere.material.map = null;
|
||||
}
|
||||
|
||||
this._sunSphere.material.dispose();
|
||||
this._sunSphere.material = null;
|
||||
|
||||
this._sunSphere = null;
|
||||
|
||||
this._mesh.geometry.dispose();
|
||||
this._mesh.geometry = null;
|
||||
|
||||
if (this._mesh.material.map) {
|
||||
this._mesh.material.map.dispose();
|
||||
this._mesh.material.map = null;
|
||||
}
|
||||
|
||||
this._mesh.material.dispose();
|
||||
this._mesh.material = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialise without requiring new keyword
|
||||
export default function(world, light) {
|
||||
return new Skybox(world, light);
|
||||
};
|
|
@ -39,15 +39,33 @@ class ImageTile extends Tile {
|
|||
var mesh = new THREE.Object3D();
|
||||
var geom = new THREE.PlaneBufferGeometry(this._side, this._side, 1);
|
||||
|
||||
var material = new THREE.MeshBasicMaterial({
|
||||
depthWrite: false
|
||||
});
|
||||
var material;
|
||||
if (!this._world._environment._skybox) {
|
||||
material = new THREE.MeshBasicMaterial({
|
||||
depthWrite: false
|
||||
});
|
||||
|
||||
// var material = new THREE.MeshPhongMaterial({
|
||||
// depthWrite: false
|
||||
// });
|
||||
} else {
|
||||
// Other MeshStandardMaterial settings
|
||||
//
|
||||
// material.envMapIntensity will change the amount of colour reflected(?)
|
||||
// from the environment map – can be greater than 1 for more intensity
|
||||
|
||||
material = new THREE.MeshStandardMaterial({
|
||||
depthWrite: false
|
||||
});
|
||||
material.roughness = 1;
|
||||
material.metalness = 0.1;
|
||||
material.envMap = this._world._environment._skybox.getRenderTarget();
|
||||
}
|
||||
|
||||
var localMesh = new THREE.Mesh(geom, material);
|
||||
localMesh.rotation.x = -90 * Math.PI / 180;
|
||||
|
||||
mesh.add(localMesh);
|
||||
|
||||
mesh.renderOrder = 0;
|
||||
|
||||
mesh.position.x = this._center[0];
|
||||
|
|
|
@ -3,6 +3,7 @@ import ImageTile from './ImageTile';
|
|||
import ImageTileLayerBaseMaterial from './ImageTileLayerBaseMaterial';
|
||||
import throttle from 'lodash.throttle';
|
||||
import THREE from 'three';
|
||||
import extend from 'lodash.assign';
|
||||
|
||||
// DONE: Find a way to avoid the flashing caused by the gap between old tiles
|
||||
// being removed and the new tiles being ready for display
|
||||
|
@ -45,6 +46,12 @@ import THREE from 'three';
|
|||
|
||||
class ImageTileLayer extends TileLayer {
|
||||
constructor(path, options) {
|
||||
var defaults = {
|
||||
distance: 40000
|
||||
};
|
||||
|
||||
options = extend(defaults, options);
|
||||
|
||||
super(options);
|
||||
|
||||
this._path = path;
|
||||
|
@ -54,8 +61,16 @@ class ImageTileLayer extends TileLayer {
|
|||
super._onAdd(world);
|
||||
|
||||
// Add base layer
|
||||
var geom = new THREE.PlaneBufferGeometry(40000, 40000, 1);
|
||||
var mesh = new THREE.Mesh(geom, ImageTileLayerBaseMaterial('#f5f5f3'));
|
||||
var geom = new THREE.PlaneBufferGeometry(200000, 200000, 1);
|
||||
|
||||
var baseMaterial;
|
||||
if (this._world._environment._skybox) {
|
||||
baseMaterial = ImageTileLayerBaseMaterial('#f5f5f3', this._world._environment._skybox.getRenderTarget());
|
||||
} else {
|
||||
baseMaterial = ImageTileLayerBaseMaterial('#f5f5f3');
|
||||
}
|
||||
|
||||
var mesh = new THREE.Mesh(geom, baseMaterial);
|
||||
mesh.rotation.x = -90 * Math.PI / 180;
|
||||
|
||||
this._baseLayer = mesh;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import THREE from 'three';
|
||||
|
||||
export default function(colour) {
|
||||
export default function(colour, skyboxTarget) {
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = 1;
|
||||
canvas.height = 1;
|
||||
|
@ -26,10 +26,21 @@ export default function(colour) {
|
|||
|
||||
texture.needsUpdate = true;
|
||||
|
||||
var material = new THREE.MeshBasicMaterial({
|
||||
map: texture,
|
||||
depthWrite: false
|
||||
});
|
||||
var material;
|
||||
|
||||
if (!skyboxTarget) {
|
||||
material = new THREE.MeshBasicMaterial({
|
||||
map: texture,
|
||||
depthWrite: false
|
||||
});
|
||||
} else {
|
||||
material = new THREE.MeshStandardMaterial({
|
||||
depthWrite: false
|
||||
});
|
||||
material.roughness = 1;
|
||||
material.metalness = 0.1;
|
||||
material.envMap = skyboxTarget;
|
||||
}
|
||||
|
||||
return material;
|
||||
};
|
||||
|
|
|
@ -10,6 +10,7 @@ var tileURLRegex = /\{([szxy])\}/g;
|
|||
class Tile {
|
||||
constructor(quadcode, path, layer) {
|
||||
this._layer = layer;
|
||||
this._world = layer._world;
|
||||
this._quadcode = quadcode;
|
||||
this._path = path;
|
||||
|
||||
|
@ -64,8 +65,9 @@ class Tile {
|
|||
// Ensure that this leaves no trace of the tile – no textures, no meshes,
|
||||
// nothing in memory or the GPU
|
||||
destroy() {
|
||||
// Delete reference to layer
|
||||
// Delete reference to layer and world
|
||||
this._layer = null;
|
||||
thos._world = null;
|
||||
|
||||
// Delete location references
|
||||
this._boundsLatLon = null;
|
||||
|
@ -111,7 +113,7 @@ class Tile {
|
|||
_getTileURL(urlParams) {
|
||||
if (!urlParams.s) {
|
||||
// Default to a random choice of a, b or c
|
||||
s = String.fromCharCode(97 + Math.floor(Math.random() * 3));
|
||||
urlParams.s = String.fromCharCode(97 + Math.floor(Math.random() * 3));
|
||||
}
|
||||
|
||||
tileURLRegex.lastIndex = 0;
|
||||
|
|
|
@ -44,7 +44,7 @@ class TopoJSONTile extends Tile {
|
|||
var defaults = {
|
||||
filter: null,
|
||||
style: {
|
||||
color: '#ff0000'
|
||||
color: '#ffffff'
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -82,8 +82,6 @@ class TopoJSONTile extends Tile {
|
|||
|
||||
var mesh = new THREE.Object3D();
|
||||
|
||||
mesh.renderOrder = 1;
|
||||
|
||||
mesh.position.x = this._center[0];
|
||||
mesh.position.z = this._center[1];
|
||||
|
||||
|
@ -386,12 +384,26 @@ class TopoJSONTile extends Tile {
|
|||
|
||||
geometry.computeBoundingBox();
|
||||
|
||||
var material = new THREE.MeshPhongMaterial({
|
||||
vertexColors: THREE.VertexColors,
|
||||
side: THREE.BackSide
|
||||
// depthWrite: false
|
||||
});
|
||||
var material;
|
||||
if (!this._world._environment._skybox) {
|
||||
material = new THREE.MeshPhongMaterial({
|
||||
vertexColors: THREE.VertexColors,
|
||||
side: THREE.BackSide
|
||||
});
|
||||
} else {
|
||||
material = new THREE.MeshStandardMaterial({
|
||||
vertexColors: THREE.VertexColors,
|
||||
side: THREE.BackSide
|
||||
});
|
||||
material.roughness = 1;
|
||||
material.metalness = 0.1;
|
||||
material.envMapIntensity = 3;
|
||||
material.envMap = this._world._environment._skybox.getRenderTarget();
|
||||
}
|
||||
|
||||
var mesh = new THREE.Mesh(geometry, material);
|
||||
|
||||
// This is only useful for flat objects
|
||||
// mesh.renderOrder = 1;
|
||||
|
||||
this._mesh.add(mesh);
|
||||
|
|
Ładowanie…
Reference in New Issue