201 lines
8.1 KiB
GLSL
201 lines
8.1 KiB
GLSL
// Shared frame data and helpers. Mirrors FrameUBO in Renderer.cpp (std140).
|
|
layout(std140, binding = 0) uniform Frame {
|
|
mat4 uView;
|
|
mat4 uProj;
|
|
mat4 uViewProj;
|
|
mat4 uInvViewProj;
|
|
mat4 uLightViewProj;
|
|
vec4 uCamPos; // xyz, w = time (seconds)
|
|
vec4 uCamParams; // near, far, focus distance, unused
|
|
vec4 uScreen; // target width, height, 1/width, 1/height
|
|
vec4 uSunDir; // xyz toward sun, w = intensity
|
|
vec4 uSunColor; // rgb
|
|
vec4 uSkyZenith; // rgb, w = ambient intensity
|
|
vec4 uSkyHorizon; // rgb, w = camera-side fill light intensity
|
|
vec4 uSkyGround; // rgb
|
|
vec4 uFogColor; // rgb, w = density
|
|
vec4 uFogParams; // height falloff, base height, noise amount, anisotropy g
|
|
vec4 uFogParams2; // god ray intensity, max distance, steps, unused
|
|
vec4 uWind; // dir.xy, strength, gust speed
|
|
vec4 uInteractor; // xyz, radius
|
|
vec4 uBlob; // xyz, radius
|
|
vec4 uClipPlane; // world-space plane for gl_ClipDistance[0]
|
|
vec4 uBillboardRight;// camera-facing billboard right vector
|
|
vec4 uShadowRight; // sun-facing billboard right vector (shadow cards)
|
|
vec4 uCookie; // strength, scale, speed, coverage
|
|
vec4 uShadowParams; // texel size, softness, unused, unused
|
|
vec4 uShaft; // god ray beam strength, coverage, pattern scale, unused
|
|
vec4 uWaterParams; // level, caustics intensity, has water, unused
|
|
vec4 uGroundAO; // world -> uv: minX, minZ, 1/sizeX, 1/sizeZ (z = 0: disabled)
|
|
vec4 uWeather; // rain intensity, surface wetness, rain fall speed, unused
|
|
ivec4 uLightCount;
|
|
vec4 uLightPos[16]; // xyz, w = radius
|
|
vec4 uLightColor[16];
|
|
};
|
|
|
|
#define uTime uCamPos.w
|
|
#define PI 3.14159265359
|
|
|
|
layout(binding = 8) uniform sampler2DShadow uShadowMap;
|
|
layout(binding = 9) uniform sampler2D uGroundAOTex;
|
|
|
|
// ---------------------------------------------------------------- noise ----
|
|
float hash12(vec2 p) {
|
|
vec3 p3 = fract(vec3(p.xyx) * 0.1031);
|
|
p3 += dot(p3, p3.yzx + 33.33);
|
|
return fract((p3.x + p3.y) * p3.z);
|
|
}
|
|
float hash13(vec3 p3) {
|
|
p3 = fract(p3 * 0.1031);
|
|
p3 += dot(p3, p3.zyx + 31.32);
|
|
return fract((p3.x + p3.y) * p3.z);
|
|
}
|
|
float vnoise(vec2 p) {
|
|
vec2 i = floor(p), f = fract(p);
|
|
vec2 u = f * f * (3.0 - 2.0 * f);
|
|
return mix(mix(hash12(i), hash12(i + vec2(1, 0)), u.x),
|
|
mix(hash12(i + vec2(0, 1)), hash12(i + vec2(1, 1)), u.x), u.y);
|
|
}
|
|
float vnoise3(vec3 p) {
|
|
vec3 i = floor(p), f = fract(p);
|
|
vec3 u = f * f * (3.0 - 2.0 * f);
|
|
float a = mix(mix(hash13(i), hash13(i + vec3(1, 0, 0)), u.x),
|
|
mix(hash13(i + vec3(0, 1, 0)), hash13(i + vec3(1, 1, 0)), u.x), u.y);
|
|
float b = mix(mix(hash13(i + vec3(0, 0, 1)), hash13(i + vec3(1, 0, 1)), u.x),
|
|
mix(hash13(i + vec3(0, 1, 1)), hash13(i + vec3(1, 1, 1)), u.x), u.y);
|
|
return mix(a, b, u.z);
|
|
}
|
|
float fbm2(vec2 p) {
|
|
float a = 0.5, s = 0.0;
|
|
for (int i = 0; i < 4; ++i) {
|
|
s += a * vnoise(p);
|
|
p = p * 2.03 + 17.1;
|
|
a *= 0.5;
|
|
}
|
|
return s / 0.9375;
|
|
}
|
|
// Interleaved gradient noise: cheap per-pixel dither for ray marching.
|
|
float ign(vec2 p) { return fract(52.9829189 * fract(dot(p, vec2(0.06711056, 0.00583715)))); }
|
|
|
|
// ---------------------------------------------------------------- depth ----
|
|
float linearDepth(float d) {
|
|
float n = uCamParams.x, f = uCamParams.y;
|
|
float z = d * 2.0 - 1.0;
|
|
return 2.0 * n * f / (f + n - z * (f - n));
|
|
}
|
|
vec3 worldFromDepth(vec2 uv, float d) {
|
|
vec4 p = uInvViewProj * vec4(uv * 2.0 - 1.0, d * 2.0 - 1.0, 1.0);
|
|
return p.xyz / p.w;
|
|
}
|
|
|
|
// ------------------------------------------------------------- lighting ----
|
|
vec3 shadowCoord(vec3 wp) {
|
|
vec4 lc = uLightViewProj * vec4(wp, 1.0);
|
|
return lc.xyz / lc.w * 0.5 + 0.5;
|
|
}
|
|
float sunShadow(vec3 wp) {
|
|
vec3 p = shadowCoord(wp);
|
|
if (any(lessThan(p.xy, vec2(0.0))) || any(greaterThan(p.xyz, vec3(1.0)))) return 1.0;
|
|
vec2 o = vec2(uShadowParams.x * uShadowParams.y);
|
|
float s = 0.0;
|
|
for (int y = -1; y <= 1; ++y)
|
|
for (int x = -1; x <= 1; ++x)
|
|
s += texture(uShadowMap, vec3(p.xy + vec2(x, y) * o, p.z));
|
|
return s / 9.0;
|
|
}
|
|
float sunShadowFast(vec3 wp) {
|
|
vec3 p = shadowCoord(wp);
|
|
if (any(lessThan(p.xy, vec2(0.0))) || any(greaterThan(p.xyz, vec3(1.0)))) return 1.0;
|
|
return texture(uShadowMap, p);
|
|
}
|
|
float canopyPattern(vec3 wp, float scale, float coverage, float octaves) {
|
|
const float canopyHeight = 14.0;
|
|
vec3 L = uSunDir.xyz;
|
|
vec2 p = wp.xz + L.xz / max(L.y, 0.15) * (canopyHeight - wp.y);
|
|
p = p * scale + uTime * uCookie.z * vec2(1.0, 0.6);
|
|
float n = octaves > 1.5 ? fbm2(p) : vnoise(p) * 0.75 + vnoise(p * 2.7 + 9.1) * 0.25;
|
|
return smoothstep(coverage - 0.06, coverage + 0.06, n);
|
|
}
|
|
// Dappled light from an implied tree canopy high above the scene, projected
|
|
// along the sun direction onto surfaces.
|
|
float canopyCookie(vec3 wp) {
|
|
if (uCookie.x <= 0.0) return 1.0;
|
|
return mix(1.0, canopyPattern(wp, uCookie.y, uCookie.w, 4.0), uCookie.x);
|
|
}
|
|
// Sparse beams through the same implied canopy, used inside fog so sunlight
|
|
// gathers into distinct god ray shafts instead of a uniform glow.
|
|
float shaftCookie(vec3 wp) {
|
|
if (uShaft.x <= 0.0) return 1.0;
|
|
return mix(1.0, canopyPattern(wp, uShaft.z, uShaft.y, 1.0), uShaft.x);
|
|
}
|
|
float sunVisibility(vec3 wp) { return sunShadow(wp) * canopyCookie(wp); }
|
|
|
|
vec3 hemiAmbient(vec3 n) {
|
|
vec3 sky = mix(uSkyHorizon.rgb, uSkyZenith.rgb, 0.45);
|
|
return mix(uSkyGround.rgb, sky, n.y * 0.5 + 0.5) * uSkyZenith.w;
|
|
}
|
|
vec3 pointLights(vec3 wp, vec3 n) {
|
|
vec3 sum = vec3(0.0);
|
|
for (int i = 0; i < uLightCount.x; ++i) {
|
|
vec3 d = uLightPos[i].xyz - wp;
|
|
float dist = length(d);
|
|
float r = uLightPos[i].w;
|
|
if (dist >= r) continue;
|
|
float att = 1.0 - (dist * dist) / (r * r);
|
|
att *= att;
|
|
float ndl = clamp((dot(n, d / max(dist, 1e-4)) + 0.4) / 1.4, 0.0, 1.0);
|
|
sum += uLightColor[i].rgb * att * ndl;
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
// Baked contact occlusion under scenery (tree bases, bushes, rocks). `height`
|
|
// is the shaded point's height above the ground it stands on: the darkening
|
|
// only applies near the ground.
|
|
float groundAO(vec3 wp, float height) {
|
|
if (uGroundAO.z <= 0.0) return 1.0;
|
|
float ao = texture(uGroundAOTex, (wp.xz - uGroundAO.xy) * uGroundAO.zw).r;
|
|
return mix(ao, 1.0, smoothstep(0.0, 1.2, height));
|
|
}
|
|
|
|
// Rain-soaked surfaces: darker albedo plus a sky/sun sheen on upward faces.
|
|
vec3 wetSurface(vec3 albedo, vec3 n, vec3 V, float vis, out vec3 sheen) {
|
|
float wet = uWeather.y * smoothstep(0.2, 0.8, n.y);
|
|
vec3 R = reflect(-V, n);
|
|
float F = 0.04 + 0.96 * pow(1.0 - max(dot(n, V), 0.0), 5.0);
|
|
sheen = wet * (uSkyHorizon.rgb * uSkyZenith.w * F * 0.6 +
|
|
uSunColor.rgb * uSunDir.w * pow(max(dot(R, uSunDir.xyz), 0.0), 60.0) * 0.5 * vis);
|
|
return albedo * mix(1.0, 0.68, uWeather.y);
|
|
}
|
|
|
|
|
|
vec3 skyColor(vec3 dir) {
|
|
float h = dir.y;
|
|
vec3 c = mix(uSkyHorizon.rgb, uSkyZenith.rgb, pow(clamp(h, 0.0, 1.0), 0.55));
|
|
if (h < 0.0) c = mix(uSkyHorizon.rgb, uSkyGround.rgb, clamp(-h * 3.0, 0.0, 1.0));
|
|
float sd = max(dot(dir, uSunDir.xyz), 0.0);
|
|
if (h > 0.0) {
|
|
vec2 cp = dir.xz / (h + 0.12) * 1.3 + uTime * vec2(0.012, 0.004);
|
|
float cl = smoothstep(0.5 - 0.45 * uWeather.x, 0.85 - 0.3 * uWeather.x, fbm2(cp)); // overcast when raining
|
|
c = mix(c, uSunColor.rgb * (0.75 + 0.6 * sd), cl * 0.55 * smoothstep(0.0, 0.2, h));
|
|
}
|
|
c += uSunColor.rgb * (pow(sd, 900.0) * 40.0 + pow(sd, 10.0) * 0.3);
|
|
return c;
|
|
}
|
|
|
|
// Animated caustic web (after joltz0r's "water turbulence").
|
|
float caustics(vec2 uv, float time) {
|
|
vec2 p = mod(uv * 6.28318, 6.28318) - 250.0;
|
|
vec2 i = p;
|
|
float c = 1.0;
|
|
const float inten = 0.005;
|
|
for (int n = 0; n < 4; ++n) {
|
|
float t = time * (1.0 - (3.5 / float(n + 1)));
|
|
i = p + vec2(cos(t - i.x) + sin(t + i.y), sin(t - i.y) + cos(t + i.x));
|
|
c += 1.0 / length(vec2(p.x / (sin(i.x + t) / inten), p.y / (cos(i.y + t) / inten)));
|
|
}
|
|
c /= 4.0;
|
|
c = 1.17 - pow(c, 1.4);
|
|
return pow(abs(c), 8.0);
|
|
}
|