2017-01-09 03:54:10 +02:00

486 lines
11 KiB
JavaScript

// -----------------------------------------------------------------------------
// Configuration
// -----------------------------------------------------------------------------
const conf = {
shaders: {
bluriness: 2,
additiveCoef: 1.0,
bleachOpacity: 0.5,
vignetteDarkness: 1.0
},
world: {
stats: "fps",
autoresize: true,
gravity: {
x: 0,
y: 0,
z: 0
},
camera: {
far: 10000,
position: [0, 10, 30]
},
renderer: {
autoClear: false
}
},
glowSphere: {
geometry: {
radius: 10,
widthSegments: 16,
heightSegments: 16
},
mass: 10,
material: {
color: 0xff0000,
kind: 'basic'
},
pos: {
x: 0,
y: 20,
z: 0
}
},
sphere: {
geometry: {
radius: 5,
widthSegments: 16,
heightSegments: 16
},
mass: 10,
material: {
color: 0xcccccc,
kind: 'basic'
},
pos: {
x: 0,
y: 20,
z: 0
}
},
plane: {
geometry: {
width: 250,
height: 250
},
mass: 0,
material: {
color: 0xff0000,
kind: "basic"
},
pos: {
x: 0,
y: 0,
z: 0
},
rot: {
x: -Math.PI / 2
}
},
glowPostProcessor: {
},
postProcessor: {
}
};
// -----------------------------------------------------------------------------
// Glow related shaders
// -----------------------------------------------------------------------------
const BleachBypassShader = {
uniforms: {
"tDiffuse": { value: null },
"opacity": { value: 1.0 }
},
vertexShader: [
"varying vec2 vUv;",
"void main() {",
"vUv = uv;",
"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
"}"
].join("\n"),
fragmentShader: [
"uniform float opacity;",
"uniform sampler2D tDiffuse;",
"varying vec2 vUv;",
"void main() {",
"vec4 base = texture2D( tDiffuse, vUv );",
"vec3 lumCoeff = vec3( 0.25, 0.65, 0.1 );",
"float lum = dot( lumCoeff, base.rgb );",
"vec3 blend = vec3( lum );",
"float L = min( 1.0, max( 0.0, 10.0 * ( lum - 0.45 ) ) );",
"vec3 result1 = 2.0 * base.rgb * blend;",
"vec3 result2 = 1.0 - 2.0 * ( 1.0 - blend ) * ( 1.0 - base.rgb );",
"vec3 newColor = mix( result1, result2, L );",
"float A2 = opacity * base.a;",
"vec3 mixRGB = A2 * newColor.rgb;",
"mixRGB += ( ( 1.0 - A2 ) * base.rgb );",
"gl_FragColor = vec4( mixRGB, base.a );",
"}"
].join("\n")
};
const VerticalBlurShader = {
uniforms: {
"tDiffuse": { value: null },
"v": { value: 1.0 / 512.0 }
},
vertexShader: [
"varying vec2 vUv;",
"void main() {",
"vUv = uv;",
"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
"}"
].join("\n"),
fragmentShader: [
"uniform sampler2D tDiffuse;",
"uniform float v;",
"varying vec2 vUv;",
"void main() {",
"vec4 sum = vec4( 0.0 );",
"sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y - 4.0 * v ) ) * 0.051;",
"sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y - 3.0 * v ) ) * 0.0918;",
"sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y - 2.0 * v ) ) * 0.12245;",
"sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y - 1.0 * v ) ) * 0.1531;",
"sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y ) ) * 0.1633;",
"sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y + 1.0 * v ) ) * 0.1531;",
"sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y + 2.0 * v ) ) * 0.12245;",
"sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y + 3.0 * v ) ) * 0.0918;",
"sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y + 4.0 * v ) ) * 0.051;",
"gl_FragColor = sum;",
"}"
].join("\n")
};
const HorizontalBlurShader = {
uniforms: {
"tDiffuse": { value: null },
"h": { value: 1.0 / 512.0 }
},
vertexShader: [
"varying vec2 vUv;",
"void main() {",
"vUv = uv;",
"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
"}"
].join("\n"),
fragmentShader: [
"uniform sampler2D tDiffuse;",
"uniform float h;",
"varying vec2 vUv;",
"void main() {",
"vec4 sum = vec4( 0.0 );",
"sum += texture2D( tDiffuse, vec2( vUv.x - 4.0 * h, vUv.y ) ) * 0.051;",
"sum += texture2D( tDiffuse, vec2( vUv.x - 3.0 * h, vUv.y ) ) * 0.0918;",
"sum += texture2D( tDiffuse, vec2( vUv.x - 2.0 * h, vUv.y ) ) * 0.12245;",
"sum += texture2D( tDiffuse, vec2( vUv.x - 1.0 * h, vUv.y ) ) * 0.1531;",
"sum += texture2D( tDiffuse, vec2( vUv.x, vUv.y ) ) * 0.1633;",
"sum += texture2D( tDiffuse, vec2( vUv.x + 1.0 * h, vUv.y ) ) * 0.1531;",
"sum += texture2D( tDiffuse, vec2( vUv.x + 2.0 * h, vUv.y ) ) * 0.12245;",
"sum += texture2D( tDiffuse, vec2( vUv.x + 3.0 * h, vUv.y ) ) * 0.0918;",
"sum += texture2D( tDiffuse, vec2( vUv.x + 4.0 * h, vUv.y ) ) * 0.051;",
"gl_FragColor = sum;",
"}"
].join("\n")
};
const VignetteShader = {
uniforms: {
"tDiffuse": { value: null },
"offset": { value: 1.0 },
"darkness": { value: 1.0 }
},
vertexShader: [
"varying vec2 vUv;",
"void main() {",
"vUv = uv;",
"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
"}"
].join("\n"),
fragmentShader: [
"uniform float offset;",
"uniform float darkness;",
"uniform sampler2D tDiffuse;",
"varying vec2 vUv;",
"void main() {",
// Eskil's vignette
"vec4 texel = texture2D( tDiffuse, vUv );",
"vec2 uv = ( vUv - vec2( 0.5 ) ) * vec2( offset );",
"gl_FragColor = vec4( mix( texel.rgb, vec3( 1.0 - darkness ), dot( uv, uv ) ), texel.a );",
/*
// alternative version from glfx.js
// this one makes more "dusty" look (as opposed to "burned")
"vec4 color = texture2D( tDiffuse, vUv );",
"float dist = distance( vUv, vec2( 0.5 ) );",
"color.rgb *= smoothstep( 0.8, offset * 0.799, dist *( darkness + offset ) );",
"gl_FragColor = color;",
*/
"}"
].join("\n")
};
const AdditiveShader = {
uniforms: {
tDiffuse: {
type: "t",
value: null
},
tAdd: {
type: "t",
value: null
},
fCoeff: {
type: "f",
value: 1.5
}
},
vertexShader: [
"varying vec2 vUv;",
"void main() {",
"vUv = uv;",
"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
"}"
].join("\n"),
fragmentShader: [
"uniform sampler2D tDiffuse;",
"uniform sampler2D tAdd;",
"uniform float fCoeff;",
"varying vec2 vUv;",
"void main() {",
"vec4 texel = texture2D( tDiffuse, vUv );",
"vec4 add = texture2D( tAdd, vUv );",
"gl_FragColor = texel + add * fCoeff;",
"}"
].join("\n")
};
// -----------------------------------------------------------------------------
// Glow Object wrapping geometries in both normal and glow worlds
// -----------------------------------------------------------------------------
class GlowObject {
constructor(options, glowOptions, geometryWorld, glowWorld) {
this._options = options;
this._glowOptions = glowOptions;
this._geometryWorld = geometryWorld;
this._glowWorld = glowWorld;
this._init();
}
_init() {
this.geometryObject = new WHS.Sphere(this._options);
this.geometryObject.addTo(this._geometryWorld);
this.glowObject = new WHS.Sphere(this._glowOptions);
this.glowObject.addTo(this._glowWorld);
}
}
// -----------------------------------------------------------------------------
// Game class
// -----------------------------------------------------------------------------
class Game {
constructor(options) {
this.options = options;
this.world = new (PHYSICS.$world(WHS.World))(options.world);
this.glowWorld = new (PHYSICS.$world(WHS.World))({ init: { render: false } });
this.world.attachWorld(this.glowWorld);
this.createGlowPostProcessing();
this.createPostProcessing();
this.createGeometry();
}
createGlowPostProcessing() {
this.glowPostProcessor = new WHS.PostProcessor(this.options.glowPostProcessor, this.glowWorld);
// render geometry
this.glowPostProcessor.createRenderPass();
// blur passes that will stretch the glow-shapes geometry
this.glowPostProcessor.createPass(composer => {
let pass = new WHS.ShaderPass('hblur_1', HorizontalBlurShader);
pass.uniforms['h'].value = this.options.shaders.bluriness / window.innerWidth * 2;
composer.addPass(pass);
});
this.glowPostProcessor.createPass(composer => {
let pass = new WHS.ShaderPass('vblur_1', VerticalBlurShader);
pass.uniforms['v'].value = this.options.shaders.bluriness / window.innerHeight * 2;
composer.addPass(pass);
});
this.glowPostProcessor.createPass(composer => {
let pass = new WHS.ShaderPass('hblur_2', HorizontalBlurShader);
pass.uniforms['h'].value = this.options.shaders.bluriness / window.innerWidth * 2;
composer.addPass(pass);
});
this.glowPostProcessor.createPass(composer => {
let pass = new WHS.ShaderPass('vblur_2', VerticalBlurShader);
pass.renderToScreen = false;
pass.uniforms['v'].value = this.options.shaders.bluriness / window.innerHeight * 2;
composer.addPass(pass);
});
}
createPostProcessing() {
this.postProcessor = new WHS.PostProcessor(this.options.postProcessor, this.world);
// render geometry
this.postProcessor.createRenderPass();
// applying glow effect
this.postProcessor.createPass(composer => {
let pass = new WHS.ShaderPass('glow_additive', AdditiveShader);
pass.renderToScreen = true;
pass.needsSwap = true;
pass.uniforms['fCoeff'].value = this.options.shaders.additiveCoef;
pass.uniforms['tAdd'].value = this.glowPostProcessor.composer.renderTarget2.texture;
composer.addPass(pass);
});
// this.postProcessor.createPass(composer => {
// let pass = new WHS.TexturePass('rtt1', this.glowPostProcessor.composer.renderTarget1.texture);
// pass.renderToScreen = true;
// composer.addPass(pass);
// })
// bleach bypass
// this.postProcessor.createPass(composer => {
// let pass = new WHS.ShaderPass('bleach_bypass', BleachBypassShader);
// pass.renderToScreen = false;
// pass.uniforms['opacity'].value = this.options.shaders.bleachOpacity;
// composer.addPass(pass);
// });
// vignetting
// this.postProcessor.createPass(composer => {
// let pass = new WHS.ShaderPass('vignette', VignetteShader);
// pass.renderToScreen = true;
// pass.uniforms['darkness'].value = this.options.shaders.vignetteDarkness;
// composer.addPass(pass);
// });
}
createGeometry() {
this.plane = new WHS.Plane(this.options.plane);
this.plane.addTo(this.world);
// create random glow objects here
this.glowSpheres = [];
this.glowSpheres.push(new GlowObject(this.options.sphere, this.options.glowSphere, this.world, this.glowWorld));
}
start() {
this.world.setControls(WHS.orbitControls());
this.world.start();
}
}
// -----------------------------------------------------------------------------
// Application bootstrap
// -----------------------------------------------------------------------------
var app = null;
function bootstrap() {
app.start()
}
function configure() {
return new Promise((resolve) => {
// some async config fetch could be done from here
// ...
// Create a Game instance with its conf
app = new Game(conf);
resolve(true);
});
}
configure().then(() => bootstrap());