underwaterFragmentShader
strata-game-library/shaders / underwaterFragmentShader
Variable: underwaterFragmentShader
constunderwaterFragmentShader: "\n uniform sampler2D tDiffuse;\n uniform sampler2D tDepth;\n uniform float uTime;\n uniform vec3 uWaterColor;\n uniform float uWaterDensity;\n uniform float uCausticStrength;\n uniform float uCausticScale;\n uniform float uWaterSurface;\n uniform vec3 uCameraPosition;\n uniform float uCameraNear;\n uniform float uCameraFar;\n uniform mat4 uProjectionMatrixInverse;\n uniform mat4 uViewMatrixInverse;\n \n varying vec2 vUv;\n \n \n// Hash function\nfloat hash(vec3 p) {\n p = fract(p * vec3(443.897, 441.423, 437.195));\n p += dot(p, p.yxz + 19.19);\n return fract((p.x + p.y) * p.z);\n}\n\n// 3D Value noise\nfloat noise(vec3 p) {\n vec3 i = floor(p);\n vec3 f = fract(p);\n f = f * f * (3.0 - 2.0 * f);\n \n float n = mix(\n mix(\n mix(hash(i + vec3(0,0,0)), hash(i + vec3(1,0,0)), f.x),\n mix(hash(i + vec3(0,1,0)), hash(i + vec3(1,1,0)), f.x),\n f.y\n ),\n mix(\n mix(hash(i + vec3(0,0,1)), hash(i + vec3(1,0,1)), f.x),\n mix(hash(i + vec3(0,1,1)), hash(i + vec3(1,1,1)), f.x),\n f.y\n ),\n f.z\n );\n return n;\n}\n\n// FBM (Fractal Brownian Motion)\nfloat fbm(vec3 p, int octaves) {\n float value = 0.0;\n float amplitude = 0.5;\n float frequency = 1.0;\n \n for (int i = 0; i < 6; i++) {\n if (i >= octaves) break;\n value += amplitude * noise(p * frequency);\n amplitude *= 0.5;\n frequency *= 2.0;\n }\n \n return value;\n}\n\n \n // Caustic pattern using overlapping waves\n float caustics(vec2 uv, float time) {\n float c = 0.0;\n \n // Multiple layers of sine waves\n for (int i = 0; i < 3; i++) {\n float fi = float(i);\n vec2 p = uv * (1.0 + fi * 0.5) + time * (0.05 + fi * 0.02);\n float wave1 = sin(p.x * 10.0 + sin(p.y * 8.0 + time));\n float wave2 = sin(p.y * 12.0 + sin(p.x * 9.0 - time * 0.7));\n c += abs(wave1 * wave2) * (1.0 / (1.0 + fi));\n }\n \n return c / 3.0;\n }\n \n // Light rays from surface\n float godRays(vec2 uv, float depth, float time) {\n float rays = 0.0;\n \n // Multiple ray angles\n for (int i = 0; i < 4; i++) {\n float angle = float(i) * 0.78539; // PI/4\n vec2 dir = vec2(cos(angle), sin(angle));\n float ray = noise(vec3(uv * 3.0 + dir * time * 0.1, depth * 0.5));\n rays += ray * (1.0 / float(i + 1));\n }\n \n return rays * 0.5;\n }\n \n float linearizeDepth(float depth) {\n float z = depth * 2.0 - 1.0;\n return (2.0 * uCameraNear * uCameraFar) / (uCameraFar + uCameraNear - z * (uCameraFar - uCameraNear));\n }\n \n vec3 getWorldPosition(vec2 uv, float depth) {\n vec4 clipSpace = vec4(uv * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);\n vec4 viewSpace = uProjectionMatrixInverse * clipSpace;\n if (abs(viewSpace.w) < 0.0001) {\n return vec3(0.0);\n }\n viewSpace /= viewSpace.w;\n vec4 worldSpace = uViewMatrixInverse * viewSpace;\n return worldSpace.xyz;\n }\n \n void main() {\n vec4 sceneColor = texture2D(tDiffuse, vUv);\n float depth = texture2D(tDepth, vUv).r;\n \n // Check if camera is underwater\n if (uCameraPosition.y >= uWaterSurface) {\n gl_FragColor = sceneColor;\n return;\n }\n \n float linearDepth = linearizeDepth(depth);\n \n // Distance-based absorption\n float absorption = 1.0 - exp(-uWaterDensity * linearDepth);\n \n // Caustics on surfaces\n vec3 worldPos = getWorldPosition(vUv, depth);\n float causticPattern = caustics(worldPos.xz * uCausticScale, uTime);\n \n // Stronger caustics near surface\n float surfaceProximity = 1.0 - clamp((uWaterSurface - worldPos.y) / 10.0, 0.0, 1.0);\n causticPattern *= surfaceProximity * uCausticStrength;\n \n // God rays effect\n float rays = godRays(vUv, linearDepth, uTime) * surfaceProximity * 0.3;\n \n // Apply underwater color grading\n vec3 underwaterTint = mix(sceneColor.rgb, uWaterColor, absorption * 0.7);\n \n // Add caustics and rays\n underwaterTint += vec3(causticPattern) * (1.0 - absorption);\n underwaterTint += vec3(0.2, 0.4, 0.5) * rays;\n \n // Depth fog\n underwaterTint = mix(underwaterTint, uWaterColor, absorption * 0.5);\n \n gl_FragColor = vec4(underwaterTint, 1.0);\n }\n"
Defined in: volumetrics.ts:222
