mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-06-22 02:27:01 +08:00
* feat(shaders): add lighting to block accumulation OIT shaders * feat(renderer): add water rendering with OIT and new shaders * feat(water): implement screen-space reflection and refraction for water with depth fade and perturbation * refactor(shader): replace glUniform calls with generic set_loc and remove cached locations * feat(water): add noise-based caustics and configurable fog density * fix(water shader): update depth fade and remove underwater check - Increase DEPTH_FADE_DISTANCE from 8 to 10 - Remove conditional so depth fade always applies - Darken deepColor from (0, 0.08, 0.15) to (0, 0.015, 0.045) * feat(underwater): add volume scattering and shadow mapping for light shafts
137 lines
3.9 KiB
GLSL
137 lines
3.9 KiB
GLSL
#version 460
|
|
|
|
in vec2 TexCoord;
|
|
in vec3 rayDir;
|
|
|
|
out vec4 FragColor;
|
|
|
|
layout (binding = 0) uniform sampler2D u_sceneTexture;
|
|
layout (binding = 1) uniform sampler2D u_depthTexture;
|
|
layout (binding = 2) uniform sampler2D u_shadowMap;
|
|
uniform mat4 u_lightSpaceMatrix;
|
|
uniform float u_time;
|
|
uniform bool u_underwater;
|
|
uniform vec3 u_waterColor;
|
|
uniform float u_fogDensity;
|
|
uniform mat4 InverseViewProjection;
|
|
uniform vec3 cameraPos;
|
|
uniform vec3 sunDir;
|
|
uniform vec3 sunColor;
|
|
uniform float waterDensity;
|
|
|
|
float hash(vec2 p) {
|
|
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
|
|
}
|
|
|
|
float noise(vec2 p) {
|
|
vec2 i = floor(p);
|
|
vec2 f = fract(p);
|
|
f = f * f * (3.0 - 2.0 * f);
|
|
|
|
return mix(
|
|
mix(hash(i), hash(i + vec2(1.0, 0.0)), f.x),
|
|
mix(hash(i + vec2(0.0, 1.0)), hash(i + vec2(1.0, 1.0)), f.x),
|
|
f.y
|
|
);
|
|
}
|
|
|
|
float fbm(vec2 p) {
|
|
float value = 0.0;
|
|
float amp = 0.5;
|
|
float freq = 1.0;
|
|
for (int i = 0; i < 4; ++i) {
|
|
value += amp * noise(p * freq);
|
|
freq *= 2.0;
|
|
amp *= 0.5;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
float getCausticValue(float x, float y, float z) {
|
|
float w = 8.0;
|
|
float strength = 4.0;
|
|
vec2 coord = vec2(x, y) * w + z * 0.5;
|
|
|
|
float n = fbm(coord);
|
|
|
|
float caustic = pow(n, 3.0) * strength;
|
|
return caustic;
|
|
}
|
|
|
|
float getShadow(vec3 worldPos) {
|
|
vec4 posLightSpace = u_lightSpaceMatrix * vec4(worldPos, 1.0);
|
|
vec3 projCoords = posLightSpace.xyz / posLightSpace.w;
|
|
projCoords = projCoords * 0.5 + 0.5;
|
|
|
|
if (projCoords.z > 1.0) return 1.0;
|
|
|
|
float closestDepth = texture(u_shadowMap, projCoords.xy).r;
|
|
float currentDepth = projCoords.z;
|
|
|
|
return currentDepth - 0.005 > closestDepth ? 0.0 : 1.0;
|
|
}
|
|
|
|
void main() {
|
|
vec4 original = texture(u_sceneTexture, TexCoord);
|
|
|
|
if (!u_underwater) {
|
|
FragColor = original;
|
|
return;
|
|
}
|
|
|
|
float rawDepth = texture(u_depthTexture, TexCoord).r;
|
|
vec3 ndc = vec3(TexCoord * 2.0 - 1.0, rawDepth * 2.0 - 1.0);
|
|
vec4 worldPosH = InverseViewProjection * vec4(ndc, 1.0);
|
|
vec3 worldPos = worldPosH.xyz / worldPosH.w;
|
|
float sceneDistance = distance(cameraPos, worldPos);
|
|
|
|
vec2 distoredUV = TexCoord;
|
|
float strength = 0.003;
|
|
distoredUV.x += sin(TexCoord.y * 15.0 + u_time * 5.0) * strength;
|
|
distoredUV.y += cos(TexCoord.x * 15.0 + u_time * 4.3) * strength;
|
|
distoredUV = clamp(distoredUV, 0.001, 0.999);
|
|
vec4 distorted = texture(u_sceneTexture, distoredUV);
|
|
|
|
float rawCaustic = getCausticValue(TexCoord.x, TexCoord.y, u_time * 0.3);
|
|
float caustic = 0.5 + rawCaustic * 0.5;
|
|
vec3 causticLight = vec3(caustic * 0.9, caustic, caustic * 0.7);
|
|
|
|
float fogFactor = clamp(1.0 - (TexCoord.y * u_fogDensity * 10.0), 0.0, 1.0);
|
|
vec3 mixed = mix(u_waterColor, distorted.rgb * causticLight, fogFactor);
|
|
// Volume Scattering
|
|
vec3 rayOrigin = cameraPos;
|
|
vec3 rayDirection = normalize(rayDir);
|
|
|
|
float maxDist = 10.0; // Maximum visible distance
|
|
float stepSize = 0.4;
|
|
float phasePower = 8.0; // Beam Sharpness
|
|
|
|
vec3 scattering = vec3(0.0);
|
|
float transmittance = 1.0;
|
|
|
|
float cosTheta = dot(rayDirection, sunDir);
|
|
float phase = pow(max(cosTheta, 0.0), phasePower);
|
|
|
|
for (float t = 0.0; t < maxDist; t += stepSize) {
|
|
|
|
if (t > sceneDistance) break;
|
|
vec3 pos = rayOrigin + rayDirection * t;
|
|
|
|
float shadow = getShadow(pos);
|
|
|
|
float density = waterDensity;
|
|
|
|
|
|
vec3 absorption = vec3(0.3, 0.08, 0.02); // Absorption coefficient per meter
|
|
vec3 sunLightAtPos = sunColor * exp(-absorption * t);
|
|
|
|
scattering += density * phase * sunLightAtPos * transmittance * stepSize * shadow;
|
|
|
|
float extinction = waterDensity * 1.5; // Extinction coefficient, tunable
|
|
transmittance *= exp(-extinction * stepSize);
|
|
if (transmittance < 0.01) break;
|
|
}
|
|
|
|
FragColor.rgb = mixed + scattering;
|
|
FragColor.a = 1.0;
|
|
} |