// Rusting shader packs, version 1: the built-ins a `#rusting composite 1`
// pass sees. A composite pass runs over the whole HDR image before bloom;
// fields are only ever added at the end.
struct CompositeInput {
// 0..1 across the image, (0, 0) at the top left.
vec2 uv;
// This pass's pixel, (0, 0) at the top left.
ivec2 pixel;
// One over this pass's target size in pixels.
vec2 texel;
// Seconds since start, the same as `frame.time.x`.
float time;
};
// Linear HDR color at uv: the scene, or the output of the last full-size
// composite pass before this one.
vec4 color_at(vec2 uv) {
return textureLod(chain, uv, 0.0);
}
// Stored depth at uv: 0 at the near plane, 1 at the far plane or the sky.
float depth_at(vec2 uv) {
return textureLod(scene_depth, uv, 0.0).r;
}
// Camera-space position of the surface at uv (the camera looks down -z).
vec3 view_position_at(vec2 uv) {
vec4 position = frame.inverse_projection
* vec4(uv * 2.0 - 1.0, depth_at(uv), 1.0);
return position.xyz / position.w;
}
// World position of the surface at uv.
vec3 world_position_at(vec2 uv) {
return (frame.inverse_view * vec4(view_position_at(uv), 1.0)).xyz;
}
// World normal of the surface at uv, rebuilt from the depth of the pixels
// around it and facing the camera.
vec3 normal_at(vec2 uv) {
vec2 texel = frame.screen.zw;
vec3 center = world_position_at(uv);
vec3 right = world_position_at(uv + vec2(texel.x, 0.0)) - center;
vec3 left = center - world_position_at(uv - vec2(texel.x, 0.0));
vec3 down = world_position_at(uv + vec2(0.0, texel.y)) - center;
vec3 up = center - world_position_at(uv - vec2(0.0, texel.y));
// The shorter difference stays on this surface at an edge.
vec3 dx = dot(right, right) < dot(left, left) ? right : left;
vec3 dy = dot(down, down) < dot(up, up) ? down : up;
vec3 normal = normalize(cross(dy, dx));
return dot(normal, frame.camera_position.xyz - center) < 0.0
? -normal
: normal;
}
// Last frame's result of the composite chain at uv. The engine keeps this
// image only for packs whose files call history_at.
vec4 history_at(vec2 uv) {
return textureLod(history, uv, 0.0);
}
// 1 where the shadowed light reaches world_position, 0 in its shadow; one
// tap of the shadow map, so move the point a little off its surface.
float shadow_at(vec3 world_position) {
int count = int(shadow.cascade_info.x);
int tiles = int(shadow.cascade_info.y);
if (count == 0) {
return 1.0;
}
int cascade = 0;
float fade = 0.0;
if (shadow.cascade_info.z > 0.5) {
vec3 away = world_position - shadow.cascade_forward.xyz;
vec3 size = abs(away);
if (size.x >= size.y && size.x >= size.z) {
cascade = away.x > 0.0 ? 0 : 1;
} else if (size.y >= size.z) {
cascade = away.y > 0.0 ? 2 : 3;
} else {
cascade = away.z > 0.0 ? 4 : 5;
}
} else {
float depth = dot(world_position, shadow.cascade_forward.xyz)
- shadow.cascade_forward.w;
while (cascade < count - 1 && depth > shadow.cascade_far[cascade]) {
cascade += 1;
}
if (depth > shadow.cascade_far[count - 1]) {
return 1.0;
}
// Fades out over the last fifth of the reach, like the scene's.
float reach = shadow.cascade_far[count - 1];
fade = smoothstep(reach * 0.8, reach, depth);
}
vec4 clip = shadow.cascades[cascade] * vec4(world_position, 1.0);
if (clip.w <= 0.0) {
return 1.0;
}
vec3 coords = clip.xyz / clip.w;
if (coords.z > 1.0) {
return 1.0;
}
vec2 texel = float(tiles) / vec2(textureSize(shadow_map, 0));
vec2 uv = clamp(coords.xy * 0.5 + 0.5, texel * 0.5, 1.0 - texel * 0.5);
vec2 tile = vec2(cascade % tiles, cascade / tiles);
return mix(texture(shadow_map, vec3((tile + uv) / float(tiles), coords.z)), 1.0, fade);
}
// Output of composite pass k (0 is the first in the pack's list) at uv.
// Only passes before this one have run; the others read black.
vec4 composite_at(int k, vec2 uv) {
return textureLod(composite_targets[k], uv, 0.0);
}
#ifndef RUSTING_CUSTOM_COMPOSITE
void composite(CompositeInput i, inout vec4 color) {}
#endif