72 lines
3.1 KiB
Text
72 lines
3.1 KiB
Text
|
|
shader_type canvas_item;
|
||
|
|
|
||
|
|
// #125: the dimensional breach, as a screen-space lens.
|
||
|
|
//
|
||
|
|
// The prologue's warp used to be a handful of lines turning around a point.
|
||
|
|
// This bends the ACTUAL SCENE around the breach instead - the ships, the
|
||
|
|
// rocks and the starfield all get dragged into it - which is most of what
|
||
|
|
// makes a hole in space read as a hole rather than as a decal drawn on top
|
||
|
|
// of one.
|
||
|
|
//
|
||
|
|
// Two effects, both radial and both fading to nothing at the edge of their
|
||
|
|
// reach, so the shader is invisible outside its own radius and costs a
|
||
|
|
// clamped texture fetch everywhere else:
|
||
|
|
//
|
||
|
|
// swirl angle offset that grows toward the centre, so the scene winds
|
||
|
|
// around the hole rather than merely sliding toward it
|
||
|
|
// pull inward displacement, strongest just outside the horizon, which
|
||
|
|
// is the lensing itself
|
||
|
|
//
|
||
|
|
// Inside the horizon nothing is sampled at all: the core is drawn as empty,
|
||
|
|
// the same decision BlackHoleVisual makes for the black-hole planet, and
|
||
|
|
// for the same reason - a "black hole" with something visible in the middle
|
||
|
|
// stops reading as one.
|
||
|
|
|
||
|
|
uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_linear;
|
||
|
|
|
||
|
|
// Where the breach is, in SCREEN pixels, and how big it has grown.
|
||
|
|
uniform vec2 breach_center = vec2(360.0, 640.0);
|
||
|
|
uniform vec2 viewport_size = vec2(720.0, 1280.0);
|
||
|
|
// The event horizon: everything inside is void.
|
||
|
|
uniform float horizon_radius = 0.0;
|
||
|
|
// How far the lens reaches past the horizon. Beyond this the scene is
|
||
|
|
// untouched.
|
||
|
|
uniform float lens_radius = 1.0;
|
||
|
|
// 0 while the breach is opening, 1 at full strength. Scales both effects
|
||
|
|
// together so one knob fades the whole thing in.
|
||
|
|
uniform float strength = 0.0;
|
||
|
|
uniform float swirl_turns = 2.4;
|
||
|
|
uniform vec4 rim_color : source_color = vec4(0.72, 0.45, 1.0, 1.0);
|
||
|
|
|
||
|
|
void fragment() {
|
||
|
|
vec2 pixel = UV * viewport_size;
|
||
|
|
vec2 offset = pixel - breach_center;
|
||
|
|
float distance_to_centre = length(offset);
|
||
|
|
|
||
|
|
if (distance_to_centre >= lens_radius || strength <= 0.001) {
|
||
|
|
COLOR = texture(screen_texture, SCREEN_UV);
|
||
|
|
} else if (distance_to_centre <= horizon_radius) {
|
||
|
|
// The core. Not black-with-alpha: genuinely nothing, so whatever is
|
||
|
|
// behind the breach is gone rather than dimmed.
|
||
|
|
COLOR = vec4(0.0, 0.0, 0.0, 1.0);
|
||
|
|
} else {
|
||
|
|
// 1 at the horizon, 0 at the edge of the lens. Squared so the bend
|
||
|
|
// is concentrated near the hole instead of smeared evenly outward.
|
||
|
|
float falloff = 1.0 - clamp(
|
||
|
|
(distance_to_centre - horizon_radius) / max(lens_radius - horizon_radius, 1.0),
|
||
|
|
0.0, 1.0);
|
||
|
|
float bend = falloff * falloff * strength;
|
||
|
|
|
||
|
|
float angle = atan(offset.y, offset.x) + bend * swirl_turns;
|
||
|
|
float pulled = distance_to_centre - bend * (lens_radius - horizon_radius) * 0.55;
|
||
|
|
pulled = max(pulled, horizon_radius);
|
||
|
|
vec2 sampled = breach_center + vec2(cos(angle), sin(angle)) * pulled;
|
||
|
|
|
||
|
|
COLOR = texture(screen_texture, sampled / viewport_size);
|
||
|
|
|
||
|
|
// A hot rim where the lensing is fiercest, which is what gives the
|
||
|
|
// horizon an edge rather than a fade.
|
||
|
|
float rim = smoothstep(0.72, 1.0, falloff) * strength;
|
||
|
|
COLOR.rgb = mix(COLOR.rgb, rim_color.rgb, rim * 0.65);
|
||
|
|
}
|
||
|
|
}
|