|
@@ -0,0 +1,199 @@
|
|
|
+import {
|
|
|
+ BufferGeometry,
|
|
|
+ Color,
|
|
|
+ FrontSide,
|
|
|
+ LinearFilter,
|
|
|
+ Matrix4,
|
|
|
+ RGBFormat,
|
|
|
+ ShaderMaterial,
|
|
|
+ UniformsLib,
|
|
|
+ UniformsUtils,
|
|
|
+ Vector3,
|
|
|
+ WebGLRenderTarget,
|
|
|
+} from "three-platformize";
|
|
|
+
|
|
|
+export interface WaterParams {
|
|
|
+ textureWidth?: number;
|
|
|
+ textureHeight?: number;
|
|
|
+ alpha?: number;
|
|
|
+ time?: number;
|
|
|
+ waterNormals?: THREE.Texture | null;
|
|
|
+ sunDirection?: THREE.Vector3;
|
|
|
+ sunColor?: THREE.Color;
|
|
|
+ waterColor?: THREE.Color;
|
|
|
+ eye?: THREE.Vector3;
|
|
|
+ distortionScale?: number;
|
|
|
+ side?: THREE.Side;
|
|
|
+ fog?: boolean;
|
|
|
+}
|
|
|
+
|
|
|
+export class Water {
|
|
|
+ material: ShaderMaterial;
|
|
|
+
|
|
|
+ constructor(geometry: BufferGeometry, params: WaterParams) {
|
|
|
+ const {
|
|
|
+ textureWidth = 512,
|
|
|
+ textureHeight = 512,
|
|
|
+ alpha = 1,
|
|
|
+ time = 0,
|
|
|
+ waterNormals = null,
|
|
|
+ sunDirection = new Vector3(0.70707, 0.70707, 0),
|
|
|
+ sunColor = new Color(16777215),
|
|
|
+ waterColor = new Color(8355711),
|
|
|
+ eye = new Vector3(0, 0, 0),
|
|
|
+ distortionScale = 20,
|
|
|
+ side = FrontSide,
|
|
|
+ fog = true,
|
|
|
+ } = params;
|
|
|
+
|
|
|
+ const mirrorShader = {
|
|
|
+ uniforms: UniformsUtils.merge([
|
|
|
+ UniformsLib.fog,
|
|
|
+ UniformsLib.lights,
|
|
|
+ {
|
|
|
+ normalSampler: { value: null },
|
|
|
+ mirrorSampler: { value: null },
|
|
|
+ alpha: { value: 1 },
|
|
|
+ time: { value: 0 },
|
|
|
+ size: { value: 1 },
|
|
|
+ distortionScale: { value: 20 },
|
|
|
+ textureMatrix: { value: new Matrix4() },
|
|
|
+ sunColor: { value: new Color(8355711) },
|
|
|
+ sunDirection: { value: new Vector3(0.70707, 0.70707, 0) },
|
|
|
+ eye: { value: new Vector3() },
|
|
|
+ waterColor: { value: new Color(5592405) },
|
|
|
+ },
|
|
|
+ ]),
|
|
|
+ vertexShader: `
|
|
|
+ uniform mat4 textureMatrix;
|
|
|
+ uniform float time;
|
|
|
+
|
|
|
+ varying vec4 mirrorCoord;
|
|
|
+ varying vec4 worldPosition;
|
|
|
+
|
|
|
+ #include <common>
|
|
|
+ #include <fog_pars_vertex>
|
|
|
+ #include <shadowmap_pars_vertex>
|
|
|
+ #include <logdepthbuf_pars_vertex>
|
|
|
+
|
|
|
+ void main() {
|
|
|
+ mirrorCoord = modelMatrix * vec4(position, 1.0);
|
|
|
+ worldPosition = mirrorCoord.xyzw;
|
|
|
+ mirrorCoord = textureMatrix * mirrorCoord;
|
|
|
+ vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
|
|
|
+ gl_Position = projectionMatrix * mvPosition;
|
|
|
+
|
|
|
+ #include <beginnormal_vertex>
|
|
|
+ #include <defaultnormal_vertex>
|
|
|
+ #include <logdepthbuf_vertex>
|
|
|
+ #include <fog_vertex>
|
|
|
+ #include <shadowmap_vertex>
|
|
|
+ }
|
|
|
+ `,
|
|
|
+ fragmentShader: `
|
|
|
+ uniform sampler2D mirrorSampler;
|
|
|
+ uniform float alpha;
|
|
|
+ uniform float time;
|
|
|
+ uniform float size;
|
|
|
+ uniform float distortionScale;
|
|
|
+ uniform sampler2D normalSampler;
|
|
|
+ uniform vec3 sunColor;
|
|
|
+ uniform vec3 sunDirection;
|
|
|
+ uniform vec3 eye;
|
|
|
+ uniform vec3 waterColor;
|
|
|
+
|
|
|
+ varying vec4 mirrorCoord;
|
|
|
+ varying vec4 worldPosition;
|
|
|
+
|
|
|
+ vec4 getNoise(vec2 uv) {
|
|
|
+ vec2 uv0 = (uv / 103.0) + vec2(time / 17.0, time / 29.0);
|
|
|
+ vec2 uv1 = uv / 107.0 - vec2(time / -19.0, time / 31.0);
|
|
|
+ vec2 uv2 = uv / vec2(8907.0, 9803.0) + vec2(time / 101.0, time / 97.0);
|
|
|
+ vec2 uv3 = uv / vec2(1091.0, 1027.0) - vec2(time / 109.0, time / -113.0);
|
|
|
+ vec4 noise = texture2D(normalSampler, uv0) +
|
|
|
+ texture2D(normalSampler, uv1) +
|
|
|
+ texture2D(normalSampler, uv2) +
|
|
|
+ texture2D(normalSampler, uv3);
|
|
|
+ return noise * 0.5 - 1.0;
|
|
|
+ }
|
|
|
+
|
|
|
+ void sunLight(const vec3 surfaceNormal, const vec3 eyeDirection, float shiny, float spec, float diffuse, inout vec3 diffuseColor, inout vec3 specularColor) {
|
|
|
+ vec3 reflection = normalize(reflect(-sunDirection, surfaceNormal));
|
|
|
+ float direction = max(0.0, dot(eyeDirection, reflection));
|
|
|
+ specularColor += pow(direction, shiny) * sunColor * spec;
|
|
|
+ diffuseColor += max(dot(sunDirection, surfaceNormal), 0.0) * sunColor * diffuse;
|
|
|
+ }
|
|
|
+ #include <common>
|
|
|
+ #include <packing>
|
|
|
+ #include <bsdfs>
|
|
|
+ #include <fog_pars_fragment>
|
|
|
+ #include <logdepthbuf_pars_fragment>
|
|
|
+ #include <lights_pars_begin>
|
|
|
+ #include <shadowmap_pars_fragment>
|
|
|
+ #include <shadowmask_pars_fragment>
|
|
|
+
|
|
|
+ void main() {
|
|
|
+ #include <logdepthbuf_fragment>
|
|
|
+
|
|
|
+ vec4 noise = getNoise(worldPosition.xz * size);
|
|
|
+ vec3 surfaceNormal = normalize(noise.xzy * vec3(1.5, 1.0, 1.5));
|
|
|
+
|
|
|
+ vec3 diffuseLight = vec3(0.0);
|
|
|
+ vec3 specularLight = vec3(0.0);
|
|
|
+
|
|
|
+ vec3 worldToEye = eye - worldPosition.xyz;
|
|
|
+ vec3 eyeDirection = normalize(worldToEye);
|
|
|
+ sunLight(surfaceNormal, eyeDirection, 100.0, 2.0, 0.5, diffuseLight, specularLight);
|
|
|
+
|
|
|
+ float distance = length(worldToEye);
|
|
|
+
|
|
|
+ vec2 distortion = surfaceNormal.xz * (0.001 + 1.0 / distance) * distortionScale;
|
|
|
+ vec3 reflectionSample = vec3(texture2D(mirrorSampler, mirrorCoord.xy / mirrorCoord.w + distortion));
|
|
|
+
|
|
|
+ float theta = max(dot(eyeDirection, surfaceNormal), 0.0);
|
|
|
+ float rf0 = 0.3;
|
|
|
+ float reflectance = rf0 + (1.0 - rf0) * pow((1.0 - theta), 5.0);
|
|
|
+
|
|
|
+ vec3 scatter = max(0.0, dot(surfaceNormal, eyeDirection)) * waterColor;
|
|
|
+ vec3 albedo = mix((sunColor * diffuseLight * 0.3 + scatter) * getShadowMask(), (vec3(0.1) + reflectionSample * 0.9 + reflectionSample * specularLight), reflectance);
|
|
|
+ vec3 outgoingLight = albedo;
|
|
|
+ gl_FragColor = vec4(outgoingLight, alpha);
|
|
|
+
|
|
|
+ #include <tonemapping_fragment>
|
|
|
+ #include <fog_fragment>
|
|
|
+ }
|
|
|
+ `,
|
|
|
+ };
|
|
|
+
|
|
|
+ const waterMaterial = new ShaderMaterial({
|
|
|
+ fragmentShader: mirrorShader.fragmentShader,
|
|
|
+ vertexShader: mirrorShader.vertexShader,
|
|
|
+ uniforms: UniformsUtils.clone(mirrorShader.uniforms),
|
|
|
+ lights: true,
|
|
|
+ side,
|
|
|
+ fog,
|
|
|
+ });
|
|
|
+
|
|
|
+ waterMaterial.uniforms.mirrorSampler.value = new WebGLRenderTarget(
|
|
|
+ textureWidth,
|
|
|
+ textureHeight,
|
|
|
+ {
|
|
|
+ minFilter: LinearFilter,
|
|
|
+ magFilter: LinearFilter,
|
|
|
+ format: RGBFormat,
|
|
|
+ }
|
|
|
+ ).texture;
|
|
|
+
|
|
|
+ waterMaterial.uniforms.textureMatrix.value = new Matrix4();
|
|
|
+ waterMaterial.uniforms.alpha.value = alpha;
|
|
|
+ waterMaterial.uniforms.time.value = time;
|
|
|
+ waterMaterial.uniforms.normalSampler.value = waterNormals;
|
|
|
+ waterMaterial.uniforms.sunColor.value = sunColor;
|
|
|
+ waterMaterial.uniforms.waterColor.value = waterColor;
|
|
|
+ waterMaterial.uniforms.sunDirection.value = sunDirection;
|
|
|
+ waterMaterial.uniforms.distortionScale.value = distortionScale;
|
|
|
+ waterMaterial.uniforms.eye.value = eye;
|
|
|
+
|
|
|
+ this.material = waterMaterial;
|
|
|
+ }
|
|
|
+}
|