Created
March 30, 2019 06:59
-
-
Save jarcode-foss/0520c41c339beffeca46ed0e3997c25c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Lightmap fragment shader | |
Lighting is calculated per-fragment, and checks for collisions with boxes | |
provided to this shader in real-time (allowing for accurate, dynamic lights). | |
This shader is ran once for each light, which is blended per-component (max) | |
with other lights. | |
The final lightmap is later smoothed in the processing shader. | |
*/ | |
#define MAX_LIGHTS ARRAY_LIMIT | |
#define MAX_BOXES ARRAY_LIMIT | |
layout(pixel_center_integer) in vec4 gl_FragCoord; | |
uniform ivec2 fb; /* framebuffer geometry */ | |
uniform ivec2 mouse; /* mouse position (is already scaled) */ | |
uniform vec3 light_color; /* light color */ | |
uniform ivec4 light; /* light position info (only x,y is used for spot) */ | |
uniform ivec4 light_data; /* x: cast range, y: direction (+/-), z: radius, w: effect */ | |
uniform int light_type; /* light type */ | |
uniform ivec4 boxes[MAX_BOXES]; | |
uniform int boxes_sz; | |
uniform float nclock; /* [0, 1) floating point value that loops every 10 seconds */ | |
uniform float ambience; /* amount of light applied to shadowed areas */ | |
uniform ivec2 screen_pos; /* screen position */ | |
uniform sampler2D depth_tex; /* depth texture */ | |
out vec4 fragment; /* output */ | |
vec2 pos; /* position of fragment in world space */ | |
/* returns the higher r, g, or b value from v */ | |
#define maxcomp(v) (v.r > v.g ? (v.r > v.b ? v.r : v.b) : (v.g > v.b ? v.g : v.b)) | |
#define maxv(r, g, b) (r > g ? (r > b ? r : b) : (g > b ? g : b)) | |
#define PI 3.1415926535897932384626433832795 | |
/* shadow tracing parameters for effect field (mostly for particles and other effects) */ | |
#define SHADE_PARAMETER 255 | |
/* (only for light bar) only trace to the first point for the given light */ | |
#define SHADE_ONCE (SHADE_PARAMETER + 1) | |
/* disable shadow tracing completely for a light */ | |
#define SHADE_NEVER (SHADE_PARAMETER + 2) | |
/* light_type values */ | |
#define TYPE_SPOT 0 | |
#define TYPE_BAR 1 | |
/* for lines p0 and p1, return true if they intersect */ | |
bool intersect(vec4 p0, vec4 p1) { | |
float d = ((p0.x - p0.z) * (p1.y - p1.w)) - ((p0.y - p0.w) * (p1.x - p1.z)); | |
if (d == 0f) return false; /* parallel */ | |
float a = (p0.x * p0.w) - (p0.y * p0.z); | |
float b = (p1.x * p1.w) - (p1.y * p1.z); | |
float px = ((a * (p1.x - p1.z)) - ((p0.x - p0.z) * b)) / float(d); | |
float py = ((a * (p1.y - p1.w)) - ((p0.y - p0.w) * b)) / float(d); | |
if (px > max(p0.x, p0.z) + 0.01f || | |
px < min(p0.x, p0.z) - 0.01f || | |
px > max(p1.x, p1.z) + 0.01f || | |
px < min(p1.x, p1.z) - 0.01f) | |
return false; | |
if (py > max(p0.y, p0.w) + 0.01f || | |
py < min(p0.y, p0.w) - 0.01f || | |
py > max(p1.y, p1.w) + 0.01f || | |
py < min(p1.y, p1.w) - 0.01f) | |
return false; | |
return true; | |
} | |
vec2 intersect_coords(vec4 p0, vec4 p1) { | |
float d = ((p0.x - p0.z) * (p1.y - p1.w)) - ((p0.y - p0.w) * (p1.x - p1.z)); | |
// if (d == 0f) return vec2(0, 0); /* parallel */ | |
float a = (p0.x * p0.w) - (p0.y * p0.z); | |
float b = (p1.x * p1.w) - (p1.y * p1.z); | |
float px = ((a * (p1.x - p1.z)) - ((p0.x - p0.z) * b)) / float(d); | |
float py = ((a * (p1.y - p1.w)) - ((p0.y - p0.w) * b)) / float(d); | |
return vec2(px, py); | |
} | |
/* returns true if the current fragment is behind 'box', relative to light (x, y) */ | |
bool behind_box(ivec4 box, int px, int py) { | |
/* return false if the point in inside the box */ | |
if (px >= box.x && px <= box.x + (box.z - 1) && | |
py >= box.y && py <= box.y + (box.w - 1)) | |
return false; | |
vec4 p = vec4(pos.x, pos.y, px, py); | |
float w = box.z - 0.02f, h = box.w - 0.02f, x = box.x, y = box.y; | |
return | |
intersect(p, vec4(x, y, x + w, y )) || /* bottom */ | |
intersect(p, vec4(x, y + h, x + w, y + h)) || /* top */ | |
intersect(p, vec4(x + w, y, x + w, y + h)) || /* right */ | |
intersect(p, vec4(x, y, x, y + h)); /* left */ | |
} | |
float apply_effect(int effect, int lx, int ly) { | |
float f_in = nclock + (float(lx % 10) / 10f) + (float(ly % 100) / 100f); | |
if (f_in > 1.0f) f_in -= 1.0f; | |
switch (effect) { | |
case 1: /* torch flicker */ | |
return -0.1f * (((0.2f * sin(2f * PI * f_in)) + 0.5f) | |
* ((0.5f * sin(40f * PI * f_in)) + 0.5f)); | |
case 2: /* flickering/broken florescent light */ | |
f_in = abs((0.5f * sin(4f * PI * f_in)) + (0.5f * sin(8f * PI * f_in))); | |
if (f_in > 0.85f) { | |
return -0.25f; | |
} | |
break; | |
case 3: /* inverse of above */ | |
f_in = abs((0.5f * sin(4f * PI * f_in)) + (0.5f * sin(8f * PI * f_in))); | |
if (f_in < 0.8f) { | |
return -0.25f; | |
} | |
break; | |
} | |
return 0f; | |
} | |
void main() { | |
/* translate into world space */ | |
pos.x = gl_FragCoord.x + screen_pos.x; | |
pos.y = gl_FragCoord.y + screen_pos.y; | |
/* calc light from all light point */ | |
float dx, dy, intensity, d, squared_radius; | |
int t, b; | |
ivec4 l = light; | |
vec3 light = vec3(0f, 0f, 0f), result = vec3(0f, 0f, 0f); | |
switch (light_type) { | |
case TYPE_SPOT: | |
{ | |
/* calc light from point */ | |
dx = l.x - pos.x; | |
dy = l.y - pos.y; | |
d = (dx * dx) + (dy * dy); /* distance squared */ | |
squared_radius = light_data.z * light_data.z; | |
if (d <= squared_radius) { | |
bool shadow = false; | |
/* ignore if behind a box */ | |
for (b = 0; b < boxes_sz; ++b) { | |
if (behind_box(boxes[b], l.x, l.y)) { | |
shadow = true; | |
break; | |
} | |
} | |
/* | |
using squared distance here makes the intensity 'curve' a bit, rather than | |
linear light, but that's fine (avoiding sqrt usage) | |
*/ | |
intensity = clamp((squared_radius - d) / squared_radius, 0f, 1f); | |
switch (l.a) { | |
case SHADE_NEVER: break; | |
default: | |
intensity += apply_effect(light_data.w, l.x, l.y); | |
case 0: | |
/* ignore if behind a box */ | |
for (b = 0; b < boxes_sz; ++b) { | |
if (behind_box(boxes[b], l.x, l.y)) { | |
shadow = true; | |
break; | |
} | |
} | |
} | |
result = light_color; | |
result *= intensity; | |
if (shadow) result *= ambience; | |
} | |
} | |
break; | |
case TYPE_BAR: | |
{ | |
/* calc light from all light bars */ | |
float d1x, d1y, d2x, d2y; | |
ivec4 ld; | |
vec2 ic; | |
ld = light_data; | |
ic = vec2(pos.x, pos.y); | |
ic = intersect_coords(l, vec4(ic, ic + vec2(l.w - l.y, -(l.z - l.x)))); | |
/* if intersection out of bounds */ | |
if (ic.x < min(l.x, l.z) || ic.y < min(l.y, l.w) || | |
ic.x > max(l.z, l.x) || ic.y > max(l.w, l.y)) { | |
/* calculate distance via points l.xy, l.zw */ | |
d1x = pos.x - l.x; | |
d1y = pos.y - l.y; | |
d2x = pos.x - l.z; | |
d2y = pos.y - l.w; | |
d = min(sqrt((d1x * d1x) + (d1y * d1y)), sqrt((d2x * d2x) + (d2y * d2y))); | |
} else { | |
/* calculate distance between fragment and intersection */ | |
dx = pos.x - ic.x; | |
dy = pos.y - ic.y; | |
d = sqrt((dx * dx) + (dy * dy)); | |
} | |
if (d <= ld.z) { | |
bool shadow = false, ignore = false; | |
/* light boundaries */ | |
if (ld.x != 0) { | |
dx = l.z - l.x; | |
dy = l.w - l.y; | |
vec2 mo = vec2(l.x + (dx / 2f), l.y + (dy / 2f)); /* center of light bar */ | |
vec2 md = normalize(vec2(dy, -dx)) * sign(ld.y); /* light bar normal */ | |
vec2 m = mo + (md * ld.z); /* light target center */ | |
vec2 nd = normalize(vec2(dx, dy)) * (ld.x / 2f); /* light target offset */ | |
vec2 p1 = m + nd, p2 = m - nd; /* light boundaries */ | |
vec4 line = vec4(mo, pos.xy); /* line to center of bar */ | |
/* test for intersection with light boundaries p1 and p2 */ | |
if (intersect(line, vec4(l.zw, p1.xy)) || intersect(line, vec4(l.xy, p2.xy))) { | |
ignore = true; | |
} else { | |
/* ignore if behind the light: acos(dot(normal, frag_to_center)) <= PI / 2 */ | |
float dotp = dot(normalize(vec2(line.x - line.z, line.y - line.w)), md); | |
if (dotp > -0.001f && dotp < 1.001f) | |
ignore = true; | |
} | |
} | |
/* ignore if behind a box */ | |
if (!ignore) { | |
intensity = clamp((ld.z - d) / ld.z, 0f, 1f); | |
switch (ld.a) { | |
case SHADE_ONCE: | |
for (b = 0; b < boxes_sz; ++b) { | |
/* only check the first point */ | |
if (behind_box(boxes[b], l.x, l.y)) { | |
shadow = true; | |
break; | |
} | |
}; | |
break; | |
case SHADE_NEVER: break; | |
default: | |
intensity += apply_effect(ld.a, l.x, l.y); | |
case 0: | |
{ | |
bool sa = false, sb = false; | |
for (b = 0; b < boxes_sz; ++b) { | |
/* | |
Because we only check the corners of the light, the shadows here | |
can be imperfectly drawn if the corners of the bar are blocked. | |
*/ | |
if (behind_box(boxes[b], l.x, l.y)) | |
sa = true; | |
if (behind_box(boxes[b], l.z, l.w)) | |
sb = true; | |
if (sa && sb) { | |
shadow = true; | |
break; | |
} | |
}; | |
} | |
break; | |
} | |
result = light_color; | |
result *= intensity; | |
if (shadow) result *= ambience; | |
} | |
} | |
} | |
} | |
fragment = vec4(result, 1f); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment