solution
Version:
An animation library for different types of liquids.
1,101 lines (803 loc) • 50.8 kB
JavaScript
/**
* solution v0.1.1 build Apr 05 2016
* https://github.com/vanruesc/solution
* Copyright 2016 Raoul van Rüschen, Zlib
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('three')) :
typeof define === 'function' && define.amd ? define(['exports', 'three'], factory) :
(factory((global.SOLUTION = global.SOLUTION || {}),global.THREE));
}(this, function (exports,THREE) { 'use strict';
THREE = 'default' in THREE ? THREE['default'] : THREE;
var fragment = "#define DISTORTION\r\n\r\nuniform sampler2D tPerturb;\r\nuniform sampler2D tDiffuse;\r\n\r\nuniform float time;\r\nuniform float resetTimer;\r\n\r\nuniform vec2 rollOffSpeed;\r\nuniform vec2 waveStrength;\r\nuniform vec3 tint;\r\n\r\nvarying vec2 vUv;\r\n\r\nconst float FADE = 12.0;\r\n\r\nvoid main() {\r\n\r\n\tfloat n = 0.0;\r\n\tfloat drop = 0.0;\r\n\r\n\tfloat resetTimerFaster = resetTimer * rollOffSpeed.x;\r\n\tfloat resetTimerSlow = resetTimer * rollOffSpeed.y;\r\n\r\n\tvec2 perturbSample;\r\n\r\n\tif(resetTimer > 0.0) {\r\n\r\n\t\tperturbSample = texture2D(tPerturb, vUv).rg;\r\n\r\n\t\tif(resetTimer < T_DISSOLVE) {\r\n\r\n\t\t\tn = perturbSample.r;\r\n\r\n\t\t}\r\n\r\n\t\tif(resetTimer < T_DROPLETS) {\r\n\r\n\t\t\tdrop = perturbSample.g;\r\n\r\n\t\t}\r\n\r\n\t}\r\n\r\n\t// if-less alternative.\r\n\t//perturbSample = texture2D(tPerturb, vUv).rg;\r\n\t//n = perturbSample.r;\r\n\t//drop = perturbSample.g;\r\n\t//n *= clamp(ceil(resetTimer / T_DISSOLVE), 0.0, 1.0);\r\n\t//drop *= clamp(ceil(resetTimer / T_DROPLETS), 0.0, 1.0);\r\n\r\n\tfloat drops = clamp(smoothstep(resetTimerFaster, 0.5 + resetTimerFaster, n), 0.0, 1.0);\r\n\tfloat droplet = clamp(smoothstep(0.75 + resetTimerSlow, 1.0 + resetTimerSlow, drop), 0.0, 1.0);\r\n\r\n\tdroplet = pow(clamp(droplet + drops, 0.0, 1.0), 0.1) * 3.0;\r\n\r\n\tvec2 droplets = vec2(dFdx(vUv + droplet).r, dFdy(vUv + droplet).g);\t\t\r\n\r\n\tvec2 wave = vec2(0.0);\r\n\r\n\tif(resetTimer < 1.0) {\r\n\r\n\t\twave.x = sin((vUv.x - vUv.y * 2.0) - time * 1.5) * waveStrength.x;\r\n\t\twave.x += cos((vUv.y * 4.0 - vUv.x * 6.0) + time * 4.2) * waveStrength.y;\r\n\t\twave.x += sin((vUv.x * 9.0 + vUv.y * 8.0) + time * 3.5) * waveStrength.x;\r\n\r\n\t\twave.y = sin((vUv.x * 2.0 + vUv.x * 2.5) + time * 2.5) * waveStrength.x;\r\n\t\twave.y += cos((vUv.y * 3.0 + vUv.x * 6.0) - time * 2.5) * waveStrength.y;\r\n\t\twave.y += sin((vUv.x * 11.0 - vUv.y * 12.0) + time * 4.5) * waveStrength.x;\r\n\r\n\t}\r\n\r\n\t//wave *= clamp(ceil(1.0 - resetTimer), 0.0, 1.0);\r\n\r\n\t// Texture edge bleed removal.\r\n\tvec2 distortFade = vec2(0.0);\r\n\tdistortFade.s = clamp(vUv.s * FADE, 0.0, 1.0);\r\n\tdistortFade.s -= clamp(1.0 - (1.0 - vUv.s) * FADE, 0.0, 1.0);\r\n\tdistortFade.t = clamp(vUv.t * FADE, 0.0, 1.0);\r\n\tdistortFade.t -= clamp(1.0 - (1.0 - vUv.t) * FADE, 0.0, 1.0); \r\n\r\n\tfloat dfade = 1.0 - pow(1.0 - distortFade.s * distortFade.t, 2.0);\r\n\twave = wave * dfade;\r\n\tdroplets = droplets * dfade;\r\n\r\n\tvec2 waveCoordR = vUv - wave * 0.004;\r\n\tvec2 waveCoordG = vUv - wave * 0.006;\t\r\n\tvec2 waveCoordB = vUv - wave * 0.008;\r\n\r\n\tvec2 dropCoordR = vUv - droplets * 1.1;\r\n\tvec2 dropCoordG = vUv - droplets * 1.2;\t\r\n\tvec2 dropCoordB = vUv - droplets * 1.3;\t\r\n\r\n\tvec3 dropletColor = vec3(0.0);\t\r\n\tdropletColor.r = texture2D(tDiffuse, dropCoordR).r;\r\n\tdropletColor.g = texture2D(tDiffuse, dropCoordG).g;\r\n\tdropletColor.b = texture2D(tDiffuse, dropCoordB).b;\r\n\r\n\tvec3 waveColor = vec3(0.0);\r\n\twaveColor.r = texture2D(tDiffuse, waveCoordR).r;\r\n\twaveColor.g = texture2D(tDiffuse, waveCoordG).g;\r\n\twaveColor.b = texture2D(tDiffuse, waveCoordB).b;\r\n\r\n\tfloat dropFade = clamp(resetTimer * 10.0, 0.0, 1.0);\r\n\tfloat dropletMask = smoothstep(0.77 + resetTimerSlow, 0.79 + resetTimerSlow, drop);\r\n\tfloat mask = smoothstep(0.02 + resetTimerFaster, 0.03 + resetTimerFaster, n);\r\n\r\n\tvec4 c = texture2D(tDiffuse, vUv);\r\n\r\n\tvec3 color = mix(waveColor, c.rgb, dropFade);\r\n\tcolor = mix(color, dropletColor * tint, clamp(dropletMask + mask, 0.0, 1.0) * dropFade);\r\n\r\n\tgl_FragColor = vec4(color, c.a);\r\n\r\n}\r\n";
var vertex = "#define DISTORTION\n\nvarying vec2 vUv;\n\nvoid main() {\n\n\tvUv = uv;\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n\n}\n";
/**
* A distortion shader material.
*
* Shader code taken from Martins Upitis' water/underwater blend:
* http://devlog-martinsh.blogspot.de/2013/09/waterunderwater-sky-shader-update-02.html
*
* @class DistortionMaterial
* @constructor
* @extends ShaderMaterial
* @param {Object} [options] - The options.
* @param {Texture} [options.perturbMap] - If none is provided, the shader will generate perlin noise on the fly.
* @param {Vector2} [options.rollOffSpeed] - The water roll off speed. X affects the overall roll off, while Y controls the droplets.
* @param {Vector2} [options.waveStrength] - The distortion wave strength. X = sine, Y = cosine.
* @param {Color} [options.color] - The droplet tint.
*/
class DistortionMaterial extends THREE.ShaderMaterial {
constructor(options) {
if(options === undefined) { options = {}; }
super({
defines: {
T_DISSOLVE: "4.0",
T_DROPLETS: "60.0"
},
uniforms: {
tPerturb: {type: "t", value: (options.perturbMap !== undefined) ? options.perturbMap : null},
tDiffuse: {type: "t", value: null},
time: {type: "1f", value: Math.random() * 1000.0},
resetTimer: {type: "1f", value: 0.0},
rollOffSpeed: {type: "v2", value: (options.rollOffSpeed !== undefined) ? options.rollOffSpeed : new THREE.Vector2(0.5, 0.02)},
waveStrength: {type: "v2", value: (options.waveStrength !== undefined) ? options.waveStrength : new THREE.Vector2(0.25, 0.5)},
tint: {type: "c", value: (options.color !== undefined) ? options.color : new THREE.Color(1.0, 1.0, 1.0)},
},
fragmentShader: fragment,
vertexShader: vertex,
extensions: {
derivatives: true
}
});
}
}
var fragment$1 = "#define DROPLET_NOISE\r\n\r\nuniform float tWidth;\r\nuniform float tHeight;\r\n\r\nuniform float texelSize;\r\nuniform float halfTexelSize;\r\n\r\nuniform float time;\r\nuniform float randomTime;\r\n\r\nvarying vec2 vUv;\r\n\r\n/*vec2 coordRot(vec2 tc, float angle) {\r\n\r\n\tfloat rotX = ((tc.x * 2.0 - 1.0) * (tWidth / tHeight) * cos(angle)) - ((tc.y * 2.0 - 1.0) * sin(angle));\r\n\tfloat rotY = ((tc.y * 2.0 - 1.0) * cos(angle)) + ((tc.x * 2.0 - 1.0) * (tWidth / tHeight) * sin(angle));\r\n\trotX = ((rotX / (tWidth / tHeight)) * 0.5 + 0.5);\r\n\trotY = rotY * 0.5 + 0.5;\r\n\r\n\treturn vec2(rotX, rotY);\r\n\r\n}*/\r\n\r\nvec3 randRGB(vec2 tc) {\r\n\r\n\tfloat noise = sin(dot(tc, vec2(12.9898, 78.233))) * 43758.5453;\r\n\r\n\treturn vec3(\r\n\t\tfract(noise) * 2.0 - 1.0,\r\n\t\tfract(noise * 1.2154) * 2.0 - 1.0,\r\n\t\tfract(noise * 1.3453) * 2.0 - 1.0\r\n\t);\r\n\r\n}\r\n\r\nfloat randA(vec2 tc) {\r\n\r\n\treturn fract(sin(dot(tc, vec2(12.9898, 78.233))) * 43758.5453 * 1.3647) * 2.0 - 1.0;\r\n\r\n}\r\n\r\nfloat fade(float t) {\r\n\r\n\treturn t * t * t * (t * (t * 6.0 - 15.0) + 10.0);\r\n\r\n}\r\n\r\nfloat perlinNoise(vec3 p) {\r\n\r\n\t// Integer part, scaled so +1 moves texelSize texel.\r\n\t// Add 1/2 texel to sample texel centers.\r\n\tvec3 pi = texelSize * floor(p) + halfTexelSize;\r\n\r\n\t// Fractional part for interpolation.\r\n\tvec3 pf = fract(p);\r\n\r\n\t// Noise contributions from (x=0, y=0), z=0 and z=1.\r\n\tfloat perm00 = randA(pi.xy);\r\n\tvec3 grad000 = randRGB(vec2(perm00, pi.z)) * 4.0 - 1.0;\r\n\tfloat n000 = dot(grad000, pf);\r\n\tvec3 grad001 = randRGB(vec2(perm00, pi.z + texelSize)) * 4.0 - 1.0;\r\n\tfloat n001 = dot(grad001, pf - vec3(0.0, 0.0, 1.0));\r\n\r\n\t// Noise contributions from (x=0, y=1), z=0 and z=1.\r\n\tfloat perm01 = randA(pi.xy + vec2(0.0, texelSize));\r\n\tvec3 grad010 = randRGB(vec2(perm01, pi.z)) * 4.0 - 1.0;\r\n\tfloat n010 = dot(grad010, pf - vec3(0.0, 1.0, 0.0));\r\n\tvec3 grad011 = randRGB(vec2(perm01, pi.z + texelSize)) * 4.0 - 1.0;\r\n\tfloat n011 = dot(grad011, pf - vec3(0.0, 1.0, 1.0));\r\n\r\n\t// Noise contributions from (x=1, y=0), z=0 and z=1.\r\n\tfloat perm10 = randA(pi.xy + vec2(texelSize, 0.0));\r\n\tvec3 grad100 = randRGB(vec2(perm10, pi.z)) * 4.0 - 1.0;\r\n\tfloat n100 = dot(grad100, pf - vec3(1.0, 0.0, 0.0));\r\n\tvec3 grad101 = randRGB(vec2(perm10, pi.z + texelSize)) * 4.0 - 1.0;\r\n\tfloat n101 = dot(grad101, pf - vec3(1.0, 0.0, 1.0));\r\n\r\n\t// Noise contributions from (x=1, y=1), z=0 and z=1.\r\n\tfloat perm11 = randA(pi.xy + vec2(texelSize));\r\n\tvec3 grad110 = randRGB(vec2(perm11, pi.z)) * 4.0 - 1.0;\r\n\tfloat n110 = dot(grad110, pf - vec3(1.0, 1.0, 0.0));\r\n\tvec3 grad111 = randRGB(vec2(perm11, pi.z + texelSize)) * 4.0 - 1.0;\r\n\tfloat n111 = dot(grad111, pf - vec3(1.0, 1.0, 1.0));\r\n\r\n\t// Blend contributions along x.\r\n\tvec4 nX = mix(vec4(n000, n001, n010, n011), vec4(n100, n101, n110, n111), fade(pf.x));\r\n\r\n\t// Blend contributions along y.\r\n\tvec2 nXY = mix(nX.xy, nX.zw, fade(pf.y));\r\n\r\n\t// Blend contributions along z and return the final noise value.\r\n\treturn mix(nXY.x, nXY.y, fade(pf.z));\r\n\r\n}\r\n\r\nvoid main() {\r\n\r\n\tfloat r = 0.0;\r\n\tfloat g = 0.0;\r\n\r\n\tr += perlinNoise(vec3(vUv * vec2(tWidth / 90.0, tHeight / 200.0) + vec2(0.0, time * 0.6), 1.0 + time * 0.2)) * 0.25;\r\n\tr += perlinNoise(vec3(vUv * vec2(tWidth / 1200.0, tHeight / 1800.0) + vec2(0.0, time * 0.5), 3.0 + time * 0.3)) * 0.75;\r\n\r\n\tg += perlinNoise(vec3(vUv * vec2(tWidth / 40.0, tHeight / 60.0), randomTime / 8.0 + time * 0.02)) * 0.2;\r\n\tg += perlinNoise(vec3(vUv * vec2(tWidth / 80.0, tHeight / 200.0), randomTime * 2.1 + time * 0.03)) * 0.25;\r\n\r\n\t#ifdef HIGH_QUALITY\r\n\r\n\t\tr += perlinNoise(vec3(vUv * vec2(tWidth / 50.0, tHeight / 80.0) + vec2(0.0, time * 0.8), time * 0.2)) * 0.1;\r\n\t\tr += perlinNoise(vec3(vUv * vec2(tWidth / 200.0, tHeight / 400.0) + vec2(0.0, time * 0.4), 2.0 + time * 0.4)) * 0.25;\r\n\r\n\t\tg += perlinNoise(vec3(vUv * vec2(tWidth / 200.0, tHeight / 400.0), randomTime * 0.23 + time * 0.04)) * 0.2;\r\n\t\tg += perlinNoise(vec3(vUv * vec2(tWidth / 800.0, tHeight / 1800.0), randomTime * 1.64 + time * 0.05)) * 0.1;\r\n\r\n\t#endif\r\n\r\n\tr = r * 0.5 + 0.5;\r\n\tg = g * 0.5 + 0.5;\r\n\r\n\tgl_FragColor = vec4(r, g, 0.0, 1.0);\r\n\r\n}\r\n";
var vertex$1 = "#define DROPLET_NOISE\n\nvarying vec2 vUv;\n\nvoid main() {\n\n\tvUv = uv;\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n\n}\n";
/**
* A droplet noise shader material.
*
* Shader code adopted from Martins Upitis' water/underwater blend:
* http://devlog-martinsh.blogspot.de/2013/09/waterunderwater-sky-shader-update-02.html
*
* @class DropletNoiseMaterial
* @constructor
* @extends ShaderMaterial
* @param {Boolean} [highQuality] - Generates double the amount of noise values. Stresses the GPU immensely.
*/
class DropletNoiseMaterial extends THREE.ShaderMaterial {
constructor(highQuality) {
super({
uniforms: {
tWidth: {type: "1f", value: 0},
tHeight: {type: "1f", value: 0},
texelSize: {type: "1f", value: 1.0},
halfTexelSize: {type: "1f", value: 0.5},
time: {type: "1f", value: 0.0},
randomTime: {type: "1f", value: Math.random() * 10.0 - 1.0}
},
fragmentShader: fragment$1,
vertexShader: vertex$1
});
if(highQuality) { this.defines.HIGH_QUALITY = "1"; }
}
}
var fragment$2 = "#define LAVA\n\n#include <common>\n#include <logdepthbuf_pars_fragment>\n#include <fog_pars_fragment>\n\nuniform sampler2D noiseMap;\n\nuniform float time;\nuniform float timeScale;\n\nuniform float primarySpeed;\nuniform float secondarySpeed;\nuniform float displacement;\nuniform float advection;\nuniform float intensity;\n\nuniform vec2 octaveScale;\nuniform vec3 lavaColor;\n\nuniform float direction;\n\n//varying float vViewTheta;\nvarying vec2 vUv;\n\nfloat noise3(vec2 n) {\n\n\tfloat x = n.x * n.y * 1000.0;\n\tx = mod(x, 13.0) * mod(x, 123.0);\n\tx = mod(x, 0.01);\n\n\treturn clamp(0.1 + x * 100.0, 0.0, 1.0);\n\n\t//return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453);\n\n}\n\nmat2 makem2(float theta) {\n\n\tfloat c = cos(theta);\n\tfloat s = sin(theta);\n\n\tfloat a = mix(c, s, direction);\n\tfloat b = mix(s, c, direction);\n\n\t//return mat2(c, -s, s, c);\n\treturn mat2(a, -b, b, a);\n\n}\n\nfloat noise(vec2 x) {\n\n\treturn texture2D(noiseMap, x * 0.01).x;\n\n}\n\nvec2 gradn(vec2 p) {\n\n\tfloat ep = 0.09;\n\tfloat gradx = noise(vec2(p.x + ep, p.y)) - noise(vec2(p.x - ep, p.y));\n\tfloat grady = noise(vec2(p.x, p.y + ep)) - noise(vec2(p.x, p.y - ep));\n\n\treturn vec2(gradx, grady);\n\n}\n\nfloat flow(vec2 p) {\n\n\tfloat t = time * timeScale;\n\tfloat t1 = t * primarySpeed;\n\tfloat t2 = t * secondarySpeed;\n\n\tfloat z = 2.0;\n\tfloat rz = 0.0;\n\tvec2 bp = p;\n\n\tfor(float i = 1.0; i < 7.0; ++i) {\n\n\t\tp += t1;\n\t\tbp += t2;\n\n\t\t// Displacement field.\n\t\tvec2 gr = gradn(i * p * 0.34 + t * displacement);\n\n\t\t// Rotation of the displacement field.\n\t\tgr *= makem2(t * 6.0 - (0.05 * p.x + 0.03 * p.y) * 40.0);\n\n\t\t// Displace the system.\n\t\tp += gr * 0.5;\n\n\t\t// Add noise octave.\n\t\trz += (sin(noise(p) * 7.0) * 0.5 + 0.5) / z;\n\n\t\t// Blend.\n\t\tp = mix(bp, p, advection);\n\n\t\t// Intensity scaling.\n\t\tz *= intensity;\n\n\t\t// Octave scaling.\n\t\tp *= octaveScale.x;\n\t\tbp *= octaveScale.y;\n\n\t}\n\n\treturn rz;\n\n}\n\nvoid main() {\n\n\t#include <logdepthbuf_fragment>\n\n\tfloat rz = flow(vUv);\n\n\tvec3 color = lavaColor / rz;\n\tcolor = pow(abs(color), vec3(1.4));\n\n\tgl_FragColor = vec4(color, 1.0);\n\n\t#include <premultiplied_alpha_fragment>\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n\n}\n";
var vertex$2 = "#define LAVA\n\n#include <common>\n#include <logdepthbuf_pars_vertex>\n\nuniform float scale;\n\n//varying float vViewTheta;\nvarying vec2 vUv;\n\nvoid main() {\n\n\tvec4 mvPosition = modelViewMatrix * vec4(position, 1.0);\n\n\t//vViewTheta = clamp((normalize(cameraPosition - position).z + 1.0) * 0.5, 0.0, 1.0);\n\tvUv = uv * scale;\n\n\tgl_Position = projectionMatrix * mvPosition;\n\n\t#include <logdepthbuf_vertex>\n\n}\n";
/**
* A noise-based flowing lava material.
*
* Original shader code by: https://www.shadertoy.com/user/nimitz
*
* @class LavaMaterial
* @constructor
* @extends ShaderMaterial
* @param {Object} [options] - The options.
* @param {Number} [options.timeScale=0.1] - The time scale.
* @param {Number} [options.primarySpeed=0.6] - The primary flow speed.
* @param {Number} [options.secondarySpeed=1.9] - The secondary flow speed (speed of the perceived flow).
* @param {Number} [options.displacement=1.0] - A time multiplier for the displacement field.
* @param {Number} [options.advection=0.77] - Blend factor (blending displaced system with base system). 0.5 ~ low, 0.95 ~ high.
* @param {Number} [options.intensity=1.4] - Overall intensity.
* @param {Vector2} [options.octaveScale=(2.0, 1.9)] - Used to scale the noise octave.
* @param {Vector2} [options.scale=(3.0, 3.0)] - The overall scale of the lava.
*/
class LavaMaterial extends THREE.ShaderMaterial {
constructor(options) {
if(options === undefined) { options = {}; }
super({
uniforms: THREE.UniformsUtils.merge([
THREE.UniformsLib.fog,
{
time: {type: "1f", value: 0.0},
timeScale: {type: "1f", value: (options.timeScale !== undefined) ? options.timeScale : 0.1},
primarySpeed: {type: "1f", value: (options.primarySpeed !== undefined) ? options.primarySpeed : 0.6},
secondarySpeed: {type: "1f", value: (options.secondarySpeed !== undefined) ? options.secondarySpeed : 1.9},
displacement: {type: "1f", value: (options.displacement !== undefined) ? options.displacement : 1.0},
advection: {type: "1f", value: (options.advection !== undefined) ? options.advection : 0.77},
intensity: {type: "1f", value: (options.intensity !== undefined) ? options.intensity : 1.4},
octaveScale: {type: "v2", value: (options.octaveScale !== undefined) ? options.octaveScale : new THREE.Vector2(2.0, 1.9)},
lavaColor: {type: "c", value: (options.color !== undefined) ? options.color : new THREE.Color(0.2, 0.07, 0.01)},
direction: {type: "1f", value: (options.direction !== undefined) ? options.direction : 0.0},
noiseMap: {type: "t", value: null},
scale: {type: "1f", value: 75.0}
}
]),
fragmentShader: fragment$2,
vertexShader: vertex$2,
fog: true
});
}
/**
* A noise map.
*
* @property noiseMap
* @type Texture
*/
get noiseMap() { return this.uniforms.noiseMap.value; }
set noiseMap(x) {
this.uniforms.noiseMap.value = x;
}
}
var fragment$3 = "#define WATER\n\n#include <common>\n#include <logdepthbuf_pars_fragment>\n#include <fog_pars_fragment>\n\nuniform float time;\nuniform float waterLevel;\n\nuniform sampler2D reflectionMap;\nuniform sampler2D refractionMap;\nuniform sampler2D normalMap;\n\nuniform float waveScale;\nuniform vec2 bigWaves;\nuniform vec2 midWaves;\nuniform vec2 smallWaves;\nuniform float waveChoppiness;\n\nuniform float windSpeed;\nuniform vec2 windDirection;\n\nuniform float waterDensity;\nuniform float chromaticAberration;\nuniform float waterBump;\nuniform float reflectionBump;\nuniform float refractionBump;\nuniform float eta;\n\nuniform vec3 waterColor;\nuniform float sunSpecular;\nuniform float scatterAmount;\nuniform vec3 scatterColor;\n\nuniform float fade;\nuniform vec3 luminosity;\n\nvarying vec3 vLightDirection;\nvarying vec3 vViewPosition;\nvarying vec4 vFragPosition;\nvarying mat3 vTbn;\nvarying vec2 vUv;\n\nvec3 tangentSpace(vec3 v) {\n\n\t//v.y = -v.y;\n\tvec3 vec;\n\tvec.xy = v.xy;\n\tvec.z = sqrt(1.0 - dot(vec.xy, vec.xy));\n\tvec.xyz = normalize(vec.x * vTbn[0] + vec.y * vTbn[1] + vec.z * vTbn[2]);\n\n\treturn vec;\n\n}\n\n/**\n * Computes fresnel reflectance without explicitly computing the refracted direction.\n */\n\nfloat fresnelDielectric(vec3 viewDirection, vec3 normal, float eta) {\n\n\tfloat c = abs(dot(viewDirection, normal));\n\tfloat g = eta * eta - 1.0 + c * c;\n\tfloat A, B;\n\tfloat result = 1.0;\n\n\tif(g > 0.0)\t{\n\n\t\tg = sqrt(g);\n\t\tA = (g - c) / (g + c);\n\t\tB = (c * (g + c) - 1.0) / (c * (g - c) + 1.0);\n\t\tresult = 0.5 * A * A * (1.0 + B * B);\n\n\t}\n\n\treturn result;\n\n}\n\nvoid main() {\n\n\t#include <logdepthbuf_fragment>\n\n\tvec2 fragCoord = (vFragPosition.xy / vFragPosition.w) * 0.5 + 0.5;\n\tfragCoord = clamp(fragCoord, 0.002, 0.998);\n\n\t// Normal map sampling.\n\n\tvec2 nCoord = vec2(0.0);\n\tvec2 offset = windDirection * time * windSpeed;\n\n\tvec2 t[3];\n\tt[0] = vec2(time * 0.005, time * 0.01);\n\tt[1] = vec2(time * 0.02, time * 0.03);\n\tt[2] = vec2(time * 0.06, time * 0.08);\n\n\tnCoord = vUv * (waveScale * 0.015) + offset * 0.03;\n\tvec3 normal0 = 2.0 * texture2D(normalMap, nCoord + vec2(-t[0][0], -t[0][1])).rgb - 1.0;\n\tnCoord = vUv * (waveScale * 0.05) + offset * 0.05 - normal0.xy * waveChoppiness;\n\tvec3 normal1 = 2.0 * texture2D(normalMap, nCoord + vec2(t[0][1], t[0][0])).rgb - 1.0;\n \n\tnCoord = vUv * (waveScale * 0.15) + offset * 0.1 - normal1.xy * waveChoppiness;\n\tvec3 normal2 = 2.0 * texture2D(normalMap, nCoord + vec2(-t[1][0], -t[1][1])).rgb - 1.0;\n\tnCoord = vUv * (waveScale * 0.5) + offset * 0.2 - normal2.xy * waveChoppiness;\n\tvec3 normal3 = 2.0 * texture2D(normalMap, nCoord + vec2(t[1][1], t[1][0])).rgb - 1.0;\n \n\tnCoord = vUv * (waveScale* 1.5) + offset * 1.0 - normal3.xy * waveChoppiness;\n\tvec3 normal4 = 2.0 * texture2D(normalMap, nCoord + vec2(-t[2][0], t[2][1])).rgb - 1.0; \n\tnCoord = vUv * (waveScale * 5.0) + offset * 1.3 - normal4.xy * waveChoppiness;\n\tvec3 normal5 = 2.0 * texture2D(normalMap, nCoord + vec2(t[2][1], -t[2][0])).rgb - 1.0;\n\n\tvec3 viewDirection = normalize(vViewPosition);\n\n\tvec3 waveNormal = normalize(\n\t\tnormal0 * bigWaves.x + normal1 * bigWaves.y +\n\t\tnormal2 * midWaves.x + normal3 * midWaves.y +\n\t\tnormal4 * smallWaves.x + normal5 * smallWaves.y\n\t);\n\n\twaveNormal = tangentSpace(waveNormal * waterBump);\n\n\tvec3 scatterNormal = normalize(\n\t\tnormal0 * bigWaves.x + normal1 * bigWaves.y * 0.5 +\n\t\tnormal2 * midWaves.x * 0.3 + normal3 * midWaves.y * 0.3 +\n\t\tnormal4 * smallWaves.x * 0.2 + normal5 * smallWaves.y * 0.2\n\t);\n\n\tscatterNormal = tangentSpace(scatterNormal * waterBump);\n\n\tvec3 lR = reflect(vLightDirection, scatterNormal);\n\tfloat s = max(dot(lR, viewDirection) * 2.0 - 1.2, 0.0);\n\tfloat lightScatter = clamp((max(dot(-vLightDirection, scatterNormal) * 0.75 + 0.25, 0.0) * s) * scatterAmount, 0.0, 1.0);\n\n\t// Fresnel term.\n\tfloat ior = (cameraPosition.y > waterLevel) ? eta : 1.0 / eta;\n\tfloat fresnel = fresnelDielectric(-viewDirection, waveNormal, ior);\n\n\t// Texture edge bleed removal.\n\tvec2 distortFade = vec2(0.0);\n\tdistortFade.s = clamp(fragCoord.s * fade, 0.0, 1.0);\n\tdistortFade.s -= clamp(1.0 - (1.0 - fragCoord.s) * fade, 0.0, 1.0);\n\tdistortFade.t = clamp(fragCoord.t * fade, 0.0, 1.0);\n\tdistortFade.t -= clamp(1.0 - (1.0 - fragCoord.t) * fade, 0.0, 1.0);\n\n\t// Inverting frag coord s, because reflection sampler is mirrored along x axis.\n\tvec3 reflection = texture2D(reflectionMap, vec2(1.0 - fragCoord.s, fragCoord.t) + (waveNormal.xy * reflectionBump * distortFade)).rgb;\n\n\tfloat reflectivity = pow(dot(luminosity, reflection.rgb * 2.0), 3.0);\n\n\tvec3 R = reflect(viewDirection, waveNormal);\n\n\tfloat specular = pow(max(dot(R, vLightDirection), 0.0), sunSpecular) * reflectivity;\n\n\tvec2 rCoord = reflect(viewDirection, waveNormal).st;\n\trCoord *= chromaticAberration;\n\n\tvec2 fCoord = fragCoord - (waveNormal.xy * refractionBump * distortFade);\n\n\tvec3 refraction = vec3(0.0);\n\trefraction.r = texture2D(refractionMap, fCoord).r;\n\trefraction.g = texture2D(refractionMap, fCoord - rCoord).g;\n\trefraction.b = texture2D(refractionMap, fCoord - rCoord * 2.0).b;\n\n\tvec3 transmittance = mix(refraction, refraction * waterColor, waterDensity);\n\tvec3 color = mix(mix(transmittance, scatterColor, lightScatter), reflection, clamp(fresnel, 0.0, 1.0));\n\n\tcolor += specular;\n\n\tgl_FragColor = vec4(color, 1.0);\n\n\t#include <premultiplied_alpha_fragment>\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n\n}\n";
var vertex$3 = "#define WATER\n\n#include <common>\n#include <logdepthbuf_pars_vertex>\n\nuniform vec3 lightPosition;\nuniform vec4 offsetRepeat;\n\nvarying vec3 vLightDirection;\nvarying vec3 vViewPosition;\nvarying vec4 vFragPosition;\nvarying mat3 vTbn;\nvarying vec2 vUv;\n\nattribute vec4 tangent;\n\nvoid main() {\n\n\tvec3 transformedNormal = normalize(normalMatrix * normal);\n\tvec3 transformedTangent = normalize(normalMatrix * tangent.xyz);\n\n\tvTbn = mat3(\n\t\ttransformedTangent,\n\t\tnormalize(cross(transformedNormal, transformedTangent) * tangent.w),\n\t\ttransformedNormal\n\t);\n\n\tvUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n\n\tmat3 m3 = mat3(modelMatrix[0].xyz, modelMatrix[1].xyz, modelMatrix[2].xyz);\n\tvec3 worldPosition = m3 * position;\n\tvec4 mvPosition = modelViewMatrix * vec4(position, 1.0);\n\tvec4 lightVector = viewMatrix * vec4(lightPosition, 1.0);\n\n\tvLightDirection = normalize(lightVector.xyz - cameraPosition);\n\tvViewPosition = mvPosition.xyz;\n\tvFragPosition = projectionMatrix * mvPosition;\n\n\tgl_Position = vFragPosition;\n\n\t#include <logdepthbuf_vertex>\n\n}\n";
/**
* A water material.
*
* Shader code taken from Martins Upitis' water/underwater blend:
* http://www.elysiun.com/forum/showthread.php?378697-Multiple-Water-Shaders-in-BGE
*
* @class WaterMaterial
* @constructor
* @extends ShaderMaterial
* @param {Object} [options] - The options.
* @param {Vector3} [options.lightPosition] - The position of the main light source.
* @param {Boolean} [options.lowQuality=false] - The quality of the shader.
*/
class WaterMaterial extends THREE.ShaderMaterial {
constructor(options) {
if(options === undefined) { options = {}; }
super({
uniforms: THREE.UniformsUtils.merge([
THREE.UniformsLib.fog,
{
time: {type: "1f", value: 0.0},
waterLevel: {type: "1f", value: 0.0},
reflectionMap: {type: "t", value: null},
refractionMap: {type: "t", value: null},
normalMap: {type: "t", value: null},
offsetRepeat: {type: "v4", value: new THREE.Vector4(0.0, 0.0, 1.0, 1.0)},
waveScale: {type: "1f", value: 350.0},
waveChoppiness: {type: "1f", value: 0.01},
bigWaves: {type: "v2", value: new THREE.Vector2(2.0, 3.0)},
midWaves: {type: "v2", value: new THREE.Vector2(4.0, 2.0)},
smallWaves: {type: "v2", value: new THREE.Vector2(1.0, 0.5)},
windSpeed: {type: "1f", value: 0.3},
windDirection: {type: "v2", value: new THREE.Vector2(0.2, -0.5)},
lightPosition: {type: "v3", value: options.lightPosition},
waterDensity: {type: "1f", value: 1.0},
chromaticAberration: {type: "1f", value: 0.002},
waterBump: {type: "1f", value: 1.0},
reflectionBump: {type: "1f", value: 0.1},
refractionBump: {type: "1f", value: 0.1},
eta: {type: "1f", value: 1.33},
waterColor: {type: "c", value: new THREE.Color(0.2, 0.4, 0.5)},
scatterColor: {type: "c", value: new THREE.Color(0.0, 1.0, 0.95)},
scatterAmount: {type: "1f", value: 3.0},
sunSpecular: {type: "1f", value: 250.0},
fade: {type: "1f", value: 12.0},
luminosity: {type: "v3", value: new THREE.Vector3(0.16, 0.32, 0.11)}
}
]),
fragmentShader: fragment$3,
vertexShader: vertex$3,
shading: THREE.FlatShading,
side: THREE.DoubleSide,
fog: true
});
}
/**
* The reflection map.
*
* @property reflectionMap
* @type Texture
*/
get reflectionMap() { return this.uniforms.reflectionMap.value; }
set reflectionMap(x) {
this.uniforms.reflectionMap.value = x;
}
/**
* The refraction map.
*
* @property refractionMap
* @type Texture
*/
get refractionMap() { return this.uniforms.refractionMap.value; }
set refractionMap(x) {
this.uniforms.refractionMap.value = x;
}
/**
* The wave normal map.
*
* @property normalMap
* @type Texture
*/
get normalMap() { return this.uniforms.normalMap.value; }
set normalMap(x) {
this.uniforms.normalMap.value = x;
}
}
var fragment$4 = "#define WATERFALL\n\n#include <common>\n#include <logdepthbuf_pars_fragment>\n#include <fog_pars_fragment>\n\nuniform float time;\nuniform float timeScale;\n\nuniform float smoothness;\nuniform float fallAccel;\nuniform float spread;\nuniform float drops;\nuniform float shape;\nuniform float power;\nuniform float alpha;\nuniform float height;\nuniform float overflow;\nuniform vec2 scale;\nuniform vec2 strength;\nuniform vec3 tint;\n\nvarying vec2 vUv;\n\nconst float K1 = 0.366025404; // (sqrt(3) - 1) / 2\nconst float K2 = 0.211324865; // (3 - sqrt(3)) / 6\n\nvec2 hash(vec2 p) {\n\n\tp = vec2(\n\t\tdot(p, vec2(127.1, 311.7)),\n\t\tdot(p, vec2(269.5, 183.3))\n\t);\n\n\treturn -1.0 + 2.0 * fract(sin(p * smoothness) * 43758.5453123);\n\n}\n\nfloat noise(vec2 p) {\n\n\tvec2 i = floor(p + (p.x + p.y) * K1);\n\n\tvec2 a = p - i + (i.x + i.y) * K2;\n\tfloat z = clamp(ceil(a.x - a.y), 0.0, 1.0); // x > y = 1, else 0\n\tvec2 o = vec2(z, 1.0 - z);\n\tvec2 b = a - o + K2;\n\tvec2 c = a - 1.0 + 2.0 * K2;\n\n\tvec3 h = max(0.5 - vec3(dot(a, a), dot(b, b), dot(c, c)), 0.0);\n\n\tvec3 n = h * h * h * h * vec3(\n\t\tdot(a, hash(i)),\n\t\tdot(b, hash(i + o)),\n\t\tdot(c, hash(i + 1.0))\n\t);\n\n\treturn dot(n, vec3(70.0));\n\n}\n\nfloat fbm(vec2 uv) {\n\n\tmat2 m = mat2(1.6, 1.2, -1.2, 1.6);\n\n\tfloat f = 0.5000 * noise(uv);\n\tuv = m * uv; f += 0.2500 * noise(uv);\n\tuv = m * uv; f += 0.1250 * noise(uv);\n\tuv = m * uv; f += 0.0625 * noise(uv);\n\n\treturn spread + 0.5 * f;\n\n}\n\nvoid main() {\n\n\t#include <logdepthbuf_fragment>\n\n\tvec2 q = -vec2(vUv);\n\n\tfloat t = time * timeScale;\n\n\tq.x *= scale.x;\n\tq.y *= scale.y;\n\n\tfloat T3 = max(3.0, 1.25 * strength.x) * t * 0.6 + pow(abs(q.y), fallAccel) * 2.0;\n\n\tfloat n = fbm(vec2(strength.x * q.x, strength.x * q.y) - vec2(0.0, T3));\n\n\tfloat T3B = max(3.0, 1.25 * strength.y) * t * 0.6 + pow(abs(q.y), fallAccel) * 2.0;\n\n\tn = n * 0.5 + (n * 0.5) / (0.001 + 1.5 * fbm(vec2(strength.y * q.x, strength.y * q.y) - vec2(0.0, T3B)));\n\n\tfloat intensity = abs(sin(t * overflow));\n\tn *= 1.0 + pow(intensity, 8.0) * 0.5;\n\n\tfloat c = 1.0 - (drops / abs(pow(q.y, 1.0) * 4.0 + 1.0)) * pow(max(0.0, length(q * vec2(1.8 + q.y * 1.5, 0.75)) - n * max(0.0, q.y + 0.25)), shape);\n\tfloat c1 = n * c * ((power + pow(intensity, height) * 0.9 - pow(intensity, 4.0) * 0.4) - pow(vUv.y, 2.0));\n\n\tc1 = c1 * 1.05 + sin(c1 * 3.4) * 0.4;\n\tc1 *= 0.95 - pow(q.y, 2.0);\n\tc1 = clamp(c1, 0.4, 1.0);\n\n\tfloat c4 = c1 * c1 * c1 * c1;\n\n\tvec3 color = vec3(\n\t\t(1.0 + tint.r) * c4,\n\t\t(1.0 + tint.g) * c4,\n\t\t(1.0 + tint.b) * c4 / c1\n\t);\n\n\tfloat a = c * (1.0 - pow(abs(vUv.y), alpha));\n\n\tgl_FragColor = vec4(color, a);\n\n\t#include <premultiplied_alpha_fragment>\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n\n}\n";
var vertex$4 = "#define WATERFALL\n\n#include <common>\n#include <logdepthbuf_pars_vertex>\n\nuniform vec4 offsetRepeat;\n\nvarying vec2 vUv;\n\nvoid main() {\n\n\tvUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n\n\t#include <logdepthbuf_vertex>\n\n}\n";
/**
* A mod of xbe's "fire" that creates a fast, smooth falling water effect.
*
* Original shader code by: https://www.shadertoy.com/user/bbcollinsworth
*
* @class WaterfallMaterial
* @constructor
* @extends ShaderMaterial
* @param {Object} [options] - The options.
* @param {Number} [options.drops=16] - The sharpness of the water drops. The higher, the sharper.
*/
class WaterfallMaterial extends THREE.ShaderMaterial {
constructor(options) {
if(options === undefined) { options = {}; }
super({
uniforms: THREE.UniformsUtils.merge([
THREE.UniformsLib.fog,
{
time: {type: "1f", value: 0.0},
timeScale: {type: "1f", value: (options.timeScale !== undefined) ? options.timeScale : 1.0},
smoothness: {type: "1f", value: (options.smoothness !== undefined) ? options.smoothness : 0.0001},
fallAccel: {type: "1f", value: (options.fallAccel !== undefined) ? options.fallAccel : 1.25},
spread: {type: "1f", value: (options.spread !== undefined) ? options.spread : 0.6},
drops: {type: "1f", value: (options.drops !== undefined) ? options.drops : 16.0},
shape: {type: "1f", value: (options.shape !== undefined) ? options.shape : 1.2},
power: {type: "1f", value: (options.power !== undefined) ? options.power : 0.7},
alpha: {type: "1f", value: (options.alpha !== undefined) ? options.alpha : 10.0},
height: {type: "1f", value: (options.height !== undefined) ? options.height : 0.8},
overflow: {type: "1f", value: (options.overflow !== undefined) ? options.overflow : 0.2},
scale: {type: "v2", value: (options.scale !== undefined) ? options.scale : new THREE.Vector2(1.0, 1.0)},
strength: {type: "v2", value: (options.strength !== undefined) ? options.strength : new THREE.Vector2(6.0, 26.0)},
tint: {type: "c", value: (options.tint !== undefined) ? options.tint : new THREE.Color(0.25, 0.5, 0.5)},
offsetRepeat: {type: "v4", value: new THREE.Vector4(-0.5, -0.75, 1.0, 1.0)}
}
]),
fragmentShader: fragment$4,
vertexShader: vertex$4,
side: THREE.DoubleSide,
transparent: true,
fog: true
});
}
}
/**
* An abstract pass.
*
* This class implements a dispose method that frees memory on demand.
* The EffectComposer calls this method when it is being destroyed.
*
* For this mechanism to work properly, please assign your render targets,
* materials or textures directly to your pass!
*
* You can prevent your disposable objects from being deleted by keeping them
* inside deeper structures such as arrays or objects.
*
* @class Pass
* @constructor
* @param {Scene} [scene] - The scene to render.
* @param {Camera} [camera] - The camera will be added to the given scene if it has no parent.
* @param {Mesh} [quad] - A quad that fills the screen. Used for rendering a pure 2D effect. Set this to null, if you don't need it (see RenderPass).
*/
class Pass {
constructor(scene, camera, quad) {
/**
* The scene to render.
*
* @property scene
* @type Scene
* @private
* @default Scene()
*/
this.scene = (scene !== undefined) ? scene : new THREE.Scene();
/**
* The camera to render with.
*
* @property camera
* @type Camera
* @private
* @default OrthographicCamera(-1, 1, 1, -1, 0, 1)
*/
this.camera = (camera !== undefined) ? camera : new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
/**
* The quad mesh to use for rendering.
*
* Assign your shader material to this mesh!
*
* @property quad
* @type Mesh
* @private
* @default Mesh(PlaneBufferGeometry(2, 2), null)
* @example
* this.quad.material = this.myMaterial;
*/
this.quad = (quad !== undefined) ? quad : new THREE.Mesh(new THREE.PlaneBufferGeometry(2, 2), null);
/**
* Indicates whether the read and write buffers should be swapped after this
* pass has finished rendering.
*
* Set this to true if this pass renders to the write buffer so that a
* following pass can find the result in the read buffer.
*
* @property needsSwap
* @type Boolean
* @private
* @default false
*/
this.needsSwap = false;
/**
* Enabled flag.
*
* @property enabled
* @type Boolean
* @default true
*/
this.enabled = true;
/**
* Render to screen flag.
*
* @property renderToScreen
* @type Boolean
* @default false
*/
this.renderToScreen = false;
// Finally, add the camera and the quad to the scene.
if(this.scene !== null) {
if(this.camera !== null && this.camera.parent === null) { this.scene.add(this.camera); }
if(this.quad !== null) { this.scene.add(this.quad); }
}
}
/**
* Renders the effect.
*
* This is an abstract method that must be overridden.
*
* @method render
* @throws {Error} An error is thrown if the method is not overridden.
* @param {WebGLRenderer} renderer - The renderer to use.
* @param {WebGLRenderTarget} readBuffer - A read buffer. Contains the result of the previous pass.
* @param {WebGLRenderTarget} writeBuffer - A write buffer. Normally used as the render target.
* @param {Number} [delta] - The delta time.
* @param {Boolean} [maskActive] - Indicates whether a stencil test mask is active or not.
*/
render(renderer, readBuffer, writeBuffer, delta, maskActive) {
throw new Error("Render method not implemented!");
}
/**
* Performs initialisation tasks.
*
* By implementing this abstract method you gain access to the renderer.
* You'll also be able to configure your custom render targets to use the
* appropriate format (RGB or RGBA).
*
* The provided renderer can be used to warm up special off-screen render
* targets by performing a preliminary render operation.
*
* The effect composer calls this method when this pass is added to its queue.
*
* @method initialise
* @param {WebGLRenderer} renderer - The renderer.
* @param {Boolean} alpha - Whether the renderer uses the alpha channel or not.
*/
initialise(renderer, alpha) {}
/**
* Updates this pass with the renderer's size.
*
* This is an abstract method that may be overriden in case you want to be
* informed about the main render size.
*
* The effect composer calls this method when its own size is updated.
*
* @method setSize
* @param {Number} width - The renderer's width.
* @param {Number} height - The renderer's height.
* @example
* this.myRenderTarget.setSize(width, height);
*/
setSize(width, height) {}
/**
* Performs a shallow search for properties that define a dispose method and
* deletes them. The pass will be inoperative after this method was called!
*
* Disposable objects:
* - render targets
* - materials
* - textures
*
* The EffectComposer calls this method automatically when it is being
* destroyed. You may, however, use it independently to free memory
* when you are certain that you don't need this pass anymore.
*
* @method dispose
*/
dispose() {
let i, p;
let keys = Object.keys(this);
for(i = keys.length - 1; i >= 0; --i) {
p = this[keys[i]];
if(p !== null && typeof p.dispose === "function") {
p.dispose();
this[keys[i]] = null;
}
}
}
}
/**
* Used for saving the original clear color during
* the rendering process of the masked scene.
*
* @property clearColor
* @type Color
* @private
* @static
*/
const clearColor = new THREE.Color();
/**
* Used for saving the original clear color during rendering.
*
* @property clearColor
* @type Color
* @private
* @static
*/
const clearColor$1 = new THREE.Color();
/**
* A distortion and droplet pass for underwater and wet lens effects.
*
* @class DistortionPass
* @constructor
* @param {Object} [options] - The options.
* @param {Number} [options.resolution=512] - The size of the generated perturbation map, power of two.
* @param {Vector2} [options.rollOffSpeed] - The droplet roll off speed.
* @param {Vector2} [options.waveStrength] - The sine and cosine wave distortion strength.
* @param {Boolean} [options.highQuality] - The effect quality. If set to true, double the amount of noise values will be computed.
* @param {Number} [options.speed] - The effect's animation speed.
*/
class DistortionPass extends Pass {
constructor(options) {
super();
this.needsSwap = true;
if(options === undefined) { options = {}; }
/**
* Noise render texture.
*
* @property renderTargetPerturb
* @type WebGLRenderTarget
* @private
*/
this.renderTargetPerturb = new THREE.WebGLRenderTarget(1, 1, {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBFormat, // want RG16F or RG32F.
type: THREE.FloatType,
generateMipmaps: false,
stencilBuffer: false,
depthBuffer: false
});
/**
* Droplet noise shader material.
*
* @property noiseMaterial
* @type DropletNoiseMaterial
* @private
*/
this.noiseMaterial = new DropletNoiseMaterial(options.highQuality);
this.resolution = (options.resolution === undefined) ? 512 : options.resolution;
/**
* Distortion shader material.
*
* @property distortionMaterial
* @type DistortionMaterial
* @private
*/
this.distortionMaterial = new DistortionMaterial({
perturbMap: this.renderTargetPerturb.texture,
rollOffSpeed: options.rollOffSpeed,
waveStrength: options.waveStrength,
tint: options.color
});
this.quad.material = this.distortionMaterial;
/**
* The effect speed.
*
* @property speed
* @type Number
*/
this.speed = (options.speed === undefined) ? 1.0 : options.speed;
/**
* Maximum duration of the dissolution effect in seconds.
*
* @property dissolutionEnd
* @type Number
* @private
*/
this.dissolutionEnd = Number.parseFloat(this.distortionMaterial.defines.T_DROPLETS);
/**
* Dissolution flag.
*
* @property _dissolve
* @type Boolean
* @default false
* @private
*/
this._dissolve = false;
}
/**
* The resolution of the noise texture.
* The value should be a power of two.
*
* @property resolution
* @type Number
* @default 512
*/
get resolution() { return this.renderTargetPerturb.width; }
set resolution(x) {
if(typeof x === "number" && x > 0) {
this.noiseMaterial.uniforms.tWidth.value = x;
this.noiseMaterial.uniforms.tHeight.value = x;
this.noiseMaterial.uniforms.texelSize.value = 1.0 / x;
this.noiseMaterial.uniforms.halfTexelSize.value = 0.5 / x;
this.renderTargetPerturb.setSize(x, x);
}
}
/**
* Dissolution flag.
*
* Set to false for wavy distortion,
* set to true for dissolution into droplets.
*
* @property dissolve
* @type Boolean
* @default false
*/
get dissolve() { return this._dissolve; }
set dissolve(x) {
this._dissolve = x;
if(!this._dissolve) {
this.distortionMaterial.uniforms.resetTimer.value = 0.0;
this.distortionMaterial.uniforms.time.value = Math.random() * 1000.0;
this.noiseMaterial.uniforms.randomTime.value = Math.random() * 10.0 - 1.0;
}
}
/**
* Renders the effect.
*
* @method render
* @param {WebGLRenderer} renderer - The renderer to use.
* @param {WebGLRenderTarget} readBuffer - The read buffer.
* @param {WebGLRenderTarget} writeBuffer - The write buffer.
* @param {Number} delta - The render delta time.
*/
render(renderer, readBuffer, writeBuffer, delta) {
let t = delta * this.speed;
this.distortionMaterial.uniforms.tDiffuse.value = readBuffer;
this.distortionMaterial.uniforms.time.value += t;
//this.renderPerturbationMap(renderer); // Debug.
if(this.dissolve && this.distortionMaterial.uniforms.resetTimer.value <= this.dissolutionEnd) {
this.distortionMaterial.uniforms.resetTimer.value += t;
this.renderPerturbationMap(renderer);
}
if(this.renderToScreen) {
renderer.render(this.scene, this.camera);
} else {
renderer.render(this.scene, this.camera, writeBuffer, false);
}
}
/**
* Renders a perturbation noise map.
*
* @method renderPerturbationMap
* @param {WebGLRenderer} renderer - The renderer to use.
* @private
*/
renderPerturbationMap(renderer) {
this.quad.material = this.noiseMaterial;
this.noiseMaterial.uniforms.time.value = this.distortionMaterial.uniforms.time.value;
//renderer.render(this.scene, this.camera); // Renders the perturb map to screen.
renderer.render(this.scene, this.camera, this.renderTargetPerturb, false);
this.quad.material = this.distortionMaterial;
}
/**
* Warms up the perturbation render target to avoid start-up hiccups.
*
* @method initialise
* @param {WebGLRenderer} renderer - The renderer.
*/
initialise(renderer) {
this.renderPerturbationMap(renderer);
}
}
// Static computation helpers.
const cameraWorldPosition = new THREE.Vector3();
const lookAtPosition = new THREE.Vector3();
const worldPosition = new THREE.Vector3();
const viewPosition = new THREE.Vector3();
const clipPlane = new THREE.Vector4();
const rotation = new THREE.Matrix4();
const normal = new THREE.Vector3();
const target = new THREE.Vector3();
const plane = new THREE.Plane();
const q = new THREE.Vector4();
/**
* A reflection pass.
*
* This pass renders the reflection of a given scene into a texture.
*
* @class ReflectionPass
* @constructor
* @extends Pass
* @param {Scene} scene - The scene to render.
* @param {Camera} camera - The main camera.
* @param {Object} [options] - Additional options.
* @param {Object3D} [options.object] - An object that represents the reflection plane in terms of position and rotation.
* @param {Number} [options.resolution=256] - The render texture resolution.
* @param {Number} [options.clipBias=0.2] - The clip plane offset.
*/
class ReflectionPass extends Pass {
constructor(scene, camera, options) {
super(scene, camera, null);
if(options === undefined) { options = {}; }
/**
* The reflection render target.
*
* @property renderTargetReflection
* @type WebGLRenderTarget
* @private
*/
this.renderTargetReflection = new THREE.WebGLRenderTarget(1, 1, {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
generateMipmaps: false,
stencilBuffer: false
});
this.resolution = (options.resolution === undefined) ? 256 : options.resolution;
/**
* The reflection texture.
*
* @property reflectionTexture
* @type Texture
*/
this.reflectionTexture = this.renderTargetReflection.texture;
/**
* The reflection camera.
*
* @property reflectionCamera
* @type Camera
* @private
*/
this.reflectionCamera = this.camera.clone();
/**
* An object that represents the internal reflection plane.
* If no object was provided a plane object will be created.
*
* Please assign a material that uses the reflection texture
* to this object and add the object to your scene.
*
* @property object
* @type Object3D
*/
let geometry, tangents;
if(options.object === undefined) {
geometry = new THREE.PlaneBufferGeometry(1, 1);
tangents = new Float32Array(4 * 4);
tangents[0] = 1; tangents[1] = 0; tangents[2] = 0; tangents[3] = -1;
tangents[4] = 1; tangents[5] = 0; tangents[6] = 0; tangents[7] = -1;
tangents[8] = 1; tangents[9] = 0; tangents[10] = 0; tangents[11] = -1;
tangents[12] = 1; tangents[13] = 0; tangents[14] = 0; tangents[15] = -1;
geometry.addAttribute("tangent", new THREE.BufferAttribute(tangents, 4));
options.object = new THREE.Mesh(geometry, null);
}
this.object = options.object;
/**
* A collection of materials that use the reflection texture.
* These will temporarily be disabled during the render process.
*
* @property materials
* @type Array
*/
this.materials = [];
/**
* Clip bias.
*
* @property clipBias
* @type Number
*/
this.clipBias = (options.clipBias !== undefined) ? options.clipBias : 0.2;
// Update the matrices.
this.update();
}
/**
* The resolution of the render target. Must be a power of two.
*
* @property resolution
* @type Number
* @default 256
*/
get resolution() { return this.renderTargetReflection.width; }
set resolution(x) {
if(typeof x === "number" && x > 0) {
this.renderTargetReflection.setSize(x, x);
}
}
/**
* Renders the reflection texture.
*
* @method render
* @param {WebGLRenderer} renderer - The renderer to use.
*/
render(renderer) {
let i, l, m;
let visible = false;
for(i = 0, l = this.materials.length; i < l; ++i) {
this.materials[i].visible = false;
}
if(this.object.material !== null) {
visible = this.object.material.visible;
this.object.material.visible = false;
}
this.update();
renderer.render(this.scene, this.reflectionCamera, this.renderTargetReflection, true);
for(i = 0; i < l; ++i) {
this.materials[i].visible = true;
}
}
/**
* Updates the projection camera.
*
* Uses oblique camera frustum clipping.
* http://www.terathon.com/lengyel/Lengyel-Oblique.pdf
*
* @method update
* @private
*/
update() {
// Adjust the position and rotation of the reflection camera.
this.object.updateMatrixWorld();
this.camera.updateMatrixWorld();
worldPosition.setFromMatrixPosition(this.object.matrixWorld);
cameraWorldPosition.setFromMatrixPosition(this.camera.matrixWorld);
rotation.extractRotation(this.object.matrixWorld);
normal.set(0, 0, 1);
normal.applyMatrix4(rotation);
viewPosition.copy(worldPosition).sub(cameraWorldPosition);
viewPosition.reflect(normal).negate();
viewPosition.add(worldPosition);
rotation.extractRotation(this.camera.matrixWorld);
lookAtPosition.set(0, 0, -1);
lookAtPosition.applyMatrix4(rotation);
lookAtPosition.add(cameraWorldPosition);
target.copy(worldPosition).sub(lookAtPosition);
target.reflect(normal).negate();
target.add(worldPosition);
this.object.up.set(0, -1, 0);
this.object.up.applyMatrix4(rotation);
this.object.up.reflect(normal).negate();
this.reflectionCamera.position.copy(viewPosition);
this.reflectionCamera.up = this.object.up;
this.reflectionCamera.lookAt(target);
this.reflectionCamera.updateProjectionMatrix();
this.reflectionCamera.updateMatrixWorld();
this.reflectionCamera.matrixWorldInverse.getInverse(this.reflectionCamera.matrixWorld);
// Update the projection matrix with the clip plane.
plane.setFromNormalAndCoplanarPoint(normal, worldPosition);
plane.applyMatrix4(this.reflectionCamera.matrixWorldInverse);
clipPlane.set(plane.normal.x, plane.normal.y, plane.normal.z, plane.constant);
let projectionMatrix = this.reflectionCamera.projectionMatrix;
let elements = projectionMatrix.elements;
q.x = (Math.sign(clipPlane.x) + elements[8]) / elements[0];
q.y = (Math.sign(clipPlane.y) + elements[9]) / elements[5];
q.z = -1.0;
q.w = (1.0 + elements[10]) / elements[14];
// Calculate the scaled plane vector.
let c = clipPlane.multiplyScalar(2.0 / clipPlane.dot(q));
// Replace the third row