This commit is contained in:
Gabrieal Jimmy
2026-10-02 12:42:00 -05:00
commit 2c99305fb0
145 changed files with 6525 additions and 0 deletions

200
assets/shaders/common.glsl Normal file
View File

@@ -0,0 +1,200 @@
// 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);
}