152 lines
4.1 KiB
TypeScript
152 lines
4.1 KiB
TypeScript
export const WATER_VERTEX_SHADER = /* glsl */ `
|
|
varying vec2 vUv;
|
|
varying vec2 vWorldPos;
|
|
|
|
void main() {
|
|
vUv = uv;
|
|
vec4 worldPosition = modelMatrix * vec4(position, 1.0);
|
|
vWorldPos = worldPosition.xz;
|
|
gl_Position = projectionMatrix * viewMatrix * worldPosition;
|
|
}
|
|
`;
|
|
|
|
export const WATER_FRAGMENT_SHADER = /* glsl */ `
|
|
uniform float uTime;
|
|
uniform float uScale;
|
|
uniform float uSmoothness;
|
|
uniform float uEdgeThreshold;
|
|
uniform float uEdgeSoftness;
|
|
uniform float uFlowX;
|
|
uniform float uFlowZ;
|
|
uniform float uCellSpeed;
|
|
uniform float uNoiseScale;
|
|
uniform float uNoiseFlowSpeed;
|
|
uniform float uDistortAmount;
|
|
uniform float uBorderRadius;
|
|
uniform float uBorderSoftness;
|
|
uniform vec3 uDeepColor;
|
|
uniform vec3 uMidColor;
|
|
uniform float uMidPos;
|
|
uniform vec3 uHighlight;
|
|
uniform float uOpacity;
|
|
uniform float uDeepOpacity;
|
|
|
|
varying vec2 vUv;
|
|
varying vec2 vWorldPos;
|
|
|
|
float roundedBoxMask(vec2 uv, float radius, float softness) {
|
|
vec2 centeredUv = uv * 2.0 - 1.0;
|
|
vec2 boxSize = vec2(1.0 - radius);
|
|
vec2 distanceToEdge = abs(centeredUv) - boxSize;
|
|
float outsideDistance = length(max(distanceToEdge, 0.0)) - radius;
|
|
|
|
return 1.0 - smoothstep(-softness, softness, outsideDistance);
|
|
}
|
|
|
|
vec2 hash2(vec2 p) {
|
|
p = vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3)));
|
|
return fract(sin(p) * 43758.5453);
|
|
}
|
|
|
|
float smin(float a, float b, float k) {
|
|
float h = max(k - abs(a - b), 0.0) / k;
|
|
return min(a, b) - h * h * h * k / 6.0;
|
|
}
|
|
|
|
vec2 cellPoint(vec2 seed) {
|
|
return 0.5 + 0.5 * sin(uTime * uCellSpeed + 6.2831 * seed);
|
|
}
|
|
|
|
float voronoiF1(vec2 p) {
|
|
vec2 i = floor(p);
|
|
vec2 f = fract(p);
|
|
float nearest = 8.0;
|
|
|
|
for (int y = -1; y <= 1; y++) {
|
|
for (int x = -1; x <= 1; x++) {
|
|
vec2 neighbor = vec2(float(x), float(y));
|
|
vec2 point = cellPoint(hash2(i + neighbor));
|
|
nearest = min(nearest, length(neighbor + point - f));
|
|
}
|
|
}
|
|
|
|
return nearest;
|
|
}
|
|
|
|
float voronoiSmoothF1(vec2 p) {
|
|
vec2 i = floor(p);
|
|
vec2 f = fract(p);
|
|
float result = 8.0;
|
|
|
|
for (int y = -1; y <= 1; y++) {
|
|
for (int x = -1; x <= 1; x++) {
|
|
vec2 neighbor = vec2(float(x), float(y));
|
|
vec2 point = cellPoint(hash2(i + neighbor));
|
|
result = smin(result, length(neighbor + point - f), uSmoothness);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
float noiseHash(vec2 p) {
|
|
p = fract(p * vec2(127.1, 311.7));
|
|
p += dot(p, p + 45.32);
|
|
return fract(p.x * p.y);
|
|
}
|
|
|
|
float valueNoise(vec2 p) {
|
|
vec2 i = floor(p);
|
|
vec2 f = fract(p);
|
|
f = f * f * (3.0 - 2.0 * f);
|
|
|
|
return mix(
|
|
mix(noiseHash(i), noiseHash(i + vec2(1.0, 0.0)), f.x),
|
|
mix(noiseHash(i + vec2(0.0, 1.0)), noiseHash(i + vec2(1.0, 1.0)), f.x),
|
|
f.y
|
|
);
|
|
}
|
|
|
|
float fbm(vec2 p) {
|
|
float value = 0.0;
|
|
float amplitude = 0.5;
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
value += amplitude * valueNoise(p);
|
|
p *= 2.0;
|
|
amplitude *= 0.5;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
void main() {
|
|
vec2 noiseUv = vWorldPos * uNoiseScale + vec2(uTime * uNoiseFlowSpeed, 0.0);
|
|
float noiseFactor = fbm(noiseUv);
|
|
vec2 distortion = vec2(noiseFactor - 0.5) * uDistortAmount;
|
|
vec2 uv = vWorldPos * uScale + vec2(uFlowX, uFlowZ) * uTime + distortion;
|
|
|
|
float f1 = voronoiF1(uv);
|
|
float smoothF1 = voronoiSmoothF1(uv);
|
|
float edge = f1 - smoothF1;
|
|
float ramp = smoothstep(uEdgeThreshold - uEdgeSoftness, uEdgeThreshold + uEdgeSoftness, edge);
|
|
float safeMidPosition = max(uMidPos, 0.0001);
|
|
float firstSegment = clamp(ramp / safeMidPosition, 0.0, 1.0);
|
|
float secondSegment = clamp((ramp - safeMidPosition) / max(1.0 - safeMidPosition, 0.0001), 0.0, 1.0);
|
|
float inSecondSegment = step(safeMidPosition, ramp);
|
|
vec3 color = mix(
|
|
mix(uDeepColor, uMidColor, firstSegment),
|
|
mix(uMidColor, uHighlight, secondSegment),
|
|
inSecondSegment
|
|
);
|
|
float alpha = mix(uDeepOpacity, 1.0, ramp) * uOpacity;
|
|
alpha *= roundedBoxMask(vUv, uBorderRadius, uBorderSoftness);
|
|
|
|
if (alpha < 0.01) {
|
|
discard;
|
|
}
|
|
|
|
gl_FragColor = vec4(color, alpha);
|
|
}
|
|
`;
|