UNPKG

solution

Version:

An animation library for different types of liquids.

1,101 lines (803 loc) 50.8 kB
/** * 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