solution
Version:
An animation library for different types of liquids.
128 lines (89 loc) • 9.78 kB
JavaScript
import THREE from "three";
const fragment = "#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";
const vertex = "#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.
*/
export 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,
vertexShader: vertex,
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;
}
}