mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-12-03 17:32:54 +00:00
384 lines
8.1 KiB
GLSL
384 lines
8.1 KiB
GLSL
|
|
// Check if light is in shadow
|
|
|
|
#if defined(USE_RAYTRACE)
|
|
|
|
#if defined(SUPPORTS_RAYQUERY)
|
|
|
|
bool traceHit(vec3 origin, vec3 direction, float dist)
|
|
{
|
|
rayQueryEXT rayQuery;
|
|
rayQueryInitializeEXT(rayQuery, TopLevelAS, gl_RayFlagsTerminateOnFirstHitEXT, 0xFF, origin, 0.01f, direction, dist);
|
|
while(rayQueryProceedEXT(rayQuery)) { }
|
|
return rayQueryGetIntersectionTypeEXT(rayQuery, true) != gl_RayQueryCommittedIntersectionNoneEXT;
|
|
}
|
|
|
|
#else
|
|
|
|
struct RayBBox
|
|
{
|
|
vec3 start, end;
|
|
vec3 c, w, v;
|
|
};
|
|
|
|
RayBBox create_ray(vec3 ray_start, vec3 ray_end)
|
|
{
|
|
RayBBox ray;
|
|
ray.start = ray_start;
|
|
ray.end = ray_end;
|
|
ray.c = (ray_start + ray_end) * 0.5;
|
|
ray.w = ray_end - ray.c;
|
|
ray.v = abs(ray.w);
|
|
return ray;
|
|
}
|
|
|
|
bool overlap_bv_ray(RayBBox ray, int a)
|
|
{
|
|
vec3 v = ray.v;
|
|
vec3 w = ray.w;
|
|
vec3 h = nodes[a].extents;
|
|
vec3 c = ray.c - nodes[a].center;
|
|
|
|
if (abs(c.x) > v.x + h.x ||
|
|
abs(c.y) > v.y + h.y ||
|
|
abs(c.z) > v.z + h.z)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (abs(c.y * w.z - c.z * w.y) > h.y * v.z + h.z * v.y ||
|
|
abs(c.x * w.z - c.z * w.x) > h.x * v.z + h.z * v.x ||
|
|
abs(c.x * w.y - c.y * w.x) > h.x * v.y + h.y * v.x)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#define FLT_EPSILON 1.192092896e-07F // smallest such that 1.0+FLT_EPSILON != 1.0
|
|
|
|
float intersect_triangle_ray(RayBBox ray, int a, out float barycentricB, out float barycentricC)
|
|
{
|
|
int start_element = nodes[a].element_index;
|
|
|
|
vec3 p[3];
|
|
p[0] = vertices[elements[start_element]].xyz;
|
|
p[1] = vertices[elements[start_element + 1]].xyz;
|
|
p[2] = vertices[elements[start_element + 2]].xyz;
|
|
|
|
// Moeller-Trumbore ray-triangle intersection algorithm:
|
|
|
|
vec3 D = ray.end - ray.start;
|
|
|
|
// Find vectors for two edges sharing p[0]
|
|
vec3 e1 = p[1] - p[0];
|
|
vec3 e2 = p[2] - p[0];
|
|
|
|
// Begin calculating determinant - also used to calculate u parameter
|
|
vec3 P = cross(D, e2);
|
|
float det = dot(e1, P);
|
|
|
|
// Backface check
|
|
//if (det < 0.0f)
|
|
// return 1.0f;
|
|
|
|
// If determinant is near zero, ray lies in plane of triangle
|
|
if (det > -FLT_EPSILON && det < FLT_EPSILON)
|
|
return 1.0f;
|
|
|
|
float inv_det = 1.0f / det;
|
|
|
|
// Calculate distance from p[0] to ray origin
|
|
vec3 T = ray.start - p[0];
|
|
|
|
// Calculate u parameter and test bound
|
|
float u = dot(T, P) * inv_det;
|
|
|
|
// Check if the intersection lies outside of the triangle
|
|
if (u < 0.f || u > 1.f)
|
|
return 1.0f;
|
|
|
|
// Prepare to test v parameter
|
|
vec3 Q = cross(T, e1);
|
|
|
|
// Calculate V parameter and test bound
|
|
float v = dot(D, Q) * inv_det;
|
|
|
|
// The intersection lies outside of the triangle
|
|
if (v < 0.f || u + v > 1.f)
|
|
return 1.0f;
|
|
|
|
float t = dot(e2, Q) * inv_det;
|
|
if (t <= FLT_EPSILON)
|
|
return 1.0f;
|
|
|
|
// Return hit location on triangle in barycentric coordinates
|
|
barycentricB = u;
|
|
barycentricC = v;
|
|
|
|
return t;
|
|
}
|
|
|
|
bool is_leaf(int node_index)
|
|
{
|
|
return nodes[node_index].element_index != -1;
|
|
}
|
|
|
|
bool TraceAnyHit(vec3 origin, float tmin, vec3 dir, float tmax)
|
|
{
|
|
if (tmax <= 0.0f)
|
|
return false;
|
|
|
|
RayBBox ray = create_ray(origin, origin + dir * tmax);
|
|
tmin /= tmax;
|
|
|
|
int stack[64];
|
|
int stackIndex = 0;
|
|
stack[stackIndex++] = nodesRoot;
|
|
do
|
|
{
|
|
int a = stack[--stackIndex];
|
|
if (overlap_bv_ray(ray, a))
|
|
{
|
|
if (is_leaf(a))
|
|
{
|
|
float baryB, baryC;
|
|
float t = intersect_triangle_ray(ray, a, baryB, baryC);
|
|
if (t >= tmin && t < 1.0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
stack[stackIndex++] = nodes[a].right;
|
|
stack[stackIndex++] = nodes[a].left;
|
|
}
|
|
}
|
|
} while (stackIndex > 0);
|
|
return false;
|
|
}
|
|
|
|
bool traceHit(vec3 origin, vec3 direction, float dist)
|
|
{
|
|
return TraceAnyHit(origin, 0.01f, direction, dist);
|
|
}
|
|
|
|
#endif
|
|
|
|
vec2 softshadow[9 * 3] = vec2[](
|
|
vec2( 0.0, 0.0),
|
|
vec2(-2.0,-2.0),
|
|
vec2( 2.0, 2.0),
|
|
vec2( 2.0,-2.0),
|
|
vec2(-2.0, 2.0),
|
|
vec2(-1.0,-1.0),
|
|
vec2( 1.0, 1.0),
|
|
vec2( 1.0,-1.0),
|
|
vec2(-1.0, 1.0),
|
|
|
|
vec2( 0.0, 0.0),
|
|
vec2(-1.5,-1.5),
|
|
vec2( 1.5, 1.5),
|
|
vec2( 1.5,-1.5),
|
|
vec2(-1.5, 1.5),
|
|
vec2(-0.5,-0.5),
|
|
vec2( 0.5, 0.5),
|
|
vec2( 0.5,-0.5),
|
|
vec2(-0.5, 0.5),
|
|
|
|
vec2( 0.0, 0.0),
|
|
vec2(-1.25,-1.75),
|
|
vec2( 1.75, 1.25),
|
|
vec2( 1.25,-1.75),
|
|
vec2(-1.75, 1.75),
|
|
vec2(-0.75,-0.25),
|
|
vec2( 0.25, 0.75),
|
|
vec2( 0.75,-0.25),
|
|
vec2(-0.25, 0.75)
|
|
);
|
|
|
|
float traceShadow(vec4 lightpos, int quality)
|
|
{
|
|
vec3 origin = pixelpos.xzy;
|
|
vec3 target = lightpos.xzy + 0.01; // nudge light position slightly as Doom maps tend to have their lights perfectly aligned with planes
|
|
|
|
vec3 direction = normalize(target - origin);
|
|
float dist = distance(origin, target);
|
|
|
|
if (quality == 0)
|
|
{
|
|
return traceHit(origin, direction, dist) ? 0.0 : 1.0;
|
|
}
|
|
else
|
|
{
|
|
vec3 v = (abs(direction.x) > abs(direction.y)) ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);
|
|
vec3 xdir = normalize(cross(direction, v));
|
|
vec3 ydir = cross(direction, xdir);
|
|
|
|
float sum = 0.0;
|
|
int step_count = quality * 9;
|
|
for (int i = 0; i <= step_count; i++)
|
|
{
|
|
vec3 pos = target + xdir * softshadow[i].x + ydir * softshadow[i].y;
|
|
sum += traceHit(origin, normalize(pos - origin), dist) ? 0.0 : 1.0;
|
|
}
|
|
return sum / step_count;
|
|
}
|
|
}
|
|
|
|
float shadowAttenuation(vec4 lightpos, float lightcolorA)
|
|
{
|
|
if (lightpos.w > 1000000.0)
|
|
return 1.0; // Sunlight
|
|
return traceShadow(lightpos, uShadowmapFilter);
|
|
}
|
|
|
|
#elif defined(USE_SHADOWMAP)
|
|
|
|
float shadowDirToU(vec2 dir)
|
|
{
|
|
if (abs(dir.y) > abs(dir.x))
|
|
{
|
|
float x = dir.x / dir.y * 0.125;
|
|
if (dir.y >= 0.0)
|
|
return 0.125 + x;
|
|
else
|
|
return (0.50 + 0.125) + x;
|
|
}
|
|
else
|
|
{
|
|
float y = dir.y / dir.x * 0.125;
|
|
if (dir.x >= 0.0)
|
|
return (0.25 + 0.125) - y;
|
|
else
|
|
return (0.75 + 0.125) - y;
|
|
}
|
|
}
|
|
|
|
vec2 shadowUToDir(float u)
|
|
{
|
|
u *= 4.0;
|
|
vec2 raydir;
|
|
switch (int(u))
|
|
{
|
|
case 0: raydir = vec2(u * 2.0 - 1.0, 1.0); break;
|
|
case 1: raydir = vec2(1.0, 1.0 - (u - 1.0) * 2.0); break;
|
|
case 2: raydir = vec2(1.0 - (u - 2.0) * 2.0, -1.0); break;
|
|
case 3: raydir = vec2(-1.0, (u - 3.0) * 2.0 - 1.0); break;
|
|
}
|
|
return raydir;
|
|
}
|
|
|
|
float sampleShadowmap(vec3 planePoint, float v)
|
|
{
|
|
float bias = 1.0;
|
|
float negD = dot(vWorldNormal.xyz, planePoint);
|
|
|
|
vec3 ray = planePoint;
|
|
|
|
vec2 isize = textureSize(ShadowMap, 0);
|
|
float scale = float(isize.x) * 0.25;
|
|
|
|
// Snap to shadow map texel grid
|
|
if (abs(ray.z) > abs(ray.x))
|
|
{
|
|
ray.y = ray.y / abs(ray.z);
|
|
ray.x = ray.x / abs(ray.z);
|
|
ray.x = (floor((ray.x + 1.0) * 0.5 * scale) + 0.5) / scale * 2.0 - 1.0;
|
|
ray.z = sign(ray.z);
|
|
}
|
|
else
|
|
{
|
|
ray.y = ray.y / abs(ray.x);
|
|
ray.z = ray.z / abs(ray.x);
|
|
ray.z = (floor((ray.z + 1.0) * 0.5 * scale) + 0.5) / scale * 2.0 - 1.0;
|
|
ray.x = sign(ray.x);
|
|
}
|
|
|
|
float t = negD / dot(vWorldNormal.xyz, ray) - bias;
|
|
vec2 dir = ray.xz * t;
|
|
|
|
float u = shadowDirToU(dir);
|
|
float dist2 = dot(dir, dir);
|
|
return step(dist2, texture(ShadowMap, vec2(u, v)).x);
|
|
}
|
|
|
|
float sampleShadowmapPCF(vec3 planePoint, float v)
|
|
{
|
|
float bias = 1.0;
|
|
float negD = dot(vWorldNormal.xyz, planePoint);
|
|
|
|
vec3 ray = planePoint;
|
|
|
|
if (abs(ray.z) > abs(ray.x))
|
|
ray.y = ray.y / abs(ray.z);
|
|
else
|
|
ray.y = ray.y / abs(ray.x);
|
|
|
|
vec2 isize = textureSize(ShadowMap, 0);
|
|
float scale = float(isize.x);
|
|
float texelPos = floor(shadowDirToU(ray.xz) * scale);
|
|
|
|
float sum = 0.0;
|
|
float step_count = uShadowmapFilter;
|
|
|
|
texelPos -= step_count + 0.5;
|
|
for (float x = -step_count; x <= step_count; x++)
|
|
{
|
|
float u = fract(texelPos / scale);
|
|
vec2 dir = shadowUToDir(u);
|
|
|
|
ray.x = dir.x;
|
|
ray.z = dir.y;
|
|
float t = negD / dot(vWorldNormal.xyz, ray) - bias;
|
|
dir = ray.xz * t;
|
|
|
|
float dist2 = dot(dir, dir);
|
|
sum += step(dist2, texture(ShadowMap, vec2(u, v)).x);
|
|
texelPos++;
|
|
}
|
|
return sum / (uShadowmapFilter * 2.0 + 1.0);
|
|
}
|
|
|
|
float shadowmapAttenuation(vec4 lightpos, float shadowIndex)
|
|
{
|
|
vec3 planePoint = pixelpos.xyz - lightpos.xyz;
|
|
planePoint += 0.01; // nudge light position slightly as Doom maps tend to have their lights perfectly aligned with planes
|
|
|
|
if (dot(planePoint.xz, planePoint.xz) < 1.0)
|
|
return 1.0; // Light is too close
|
|
|
|
float v = (shadowIndex + 0.5) / 1024.0;
|
|
|
|
if (uShadowmapFilter == 0)
|
|
{
|
|
return sampleShadowmap(planePoint, v);
|
|
}
|
|
else
|
|
{
|
|
return sampleShadowmapPCF(planePoint, v);
|
|
}
|
|
}
|
|
|
|
float shadowAttenuation(vec4 lightpos, float lightcolorA)
|
|
{
|
|
if (lightpos.w > 1000000.0)
|
|
return 1.0; // Sunlight
|
|
|
|
float shadowIndex = abs(lightcolorA) - 1.0;
|
|
if (shadowIndex >= 1024.0)
|
|
return 1.0; // No shadowmap available for this light
|
|
|
|
return shadowmapAttenuation(lightpos, shadowIndex);
|
|
}
|
|
|
|
#else
|
|
|
|
float shadowAttenuation(vec4 lightpos, float lightcolorA)
|
|
{
|
|
return 1.0;
|
|
}
|
|
|
|
#endif
|