mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-29 23:33:00 +00:00
563 lines
13 KiB
GLSL
563 lines
13 KiB
GLSL
|
|
#if defined(USE_RAYQUERY)
|
|
layout(set = 1, binding = 0) uniform accelerationStructureEXT acc;
|
|
#else
|
|
struct CollisionNode
|
|
{
|
|
vec3 center;
|
|
float padding1;
|
|
vec3 extents;
|
|
float padding2;
|
|
int left;
|
|
int right;
|
|
int element_index;
|
|
int padding3;
|
|
};
|
|
layout(std430, set = 1, binding = 0) buffer NodeBuffer
|
|
{
|
|
int nodesRoot;
|
|
int nodebufferPadding1;
|
|
int nodebufferPadding2;
|
|
int nodebufferPadding3;
|
|
CollisionNode nodes[];
|
|
};
|
|
layout(std430, set = 1, binding = 1) buffer VertexBuffer { vec4 vertices[]; };
|
|
layout(std430, set = 1, binding = 2) buffer ElementBuffer { int elements[]; };
|
|
#endif
|
|
|
|
layout(set = 0, binding = 0) uniform Uniforms
|
|
{
|
|
vec3 SunDir;
|
|
float Padding1;
|
|
vec3 SunColor;
|
|
float SunIntensity;
|
|
};
|
|
|
|
struct SurfaceInfo
|
|
{
|
|
vec3 Normal;
|
|
float Sky;
|
|
float SamplingDistance;
|
|
uint PortalIndex;
|
|
float Padding1, Padding2;
|
|
};
|
|
|
|
struct PortalInfo
|
|
{
|
|
mat4 Transformation;
|
|
};
|
|
|
|
struct LightInfo
|
|
{
|
|
vec3 Origin;
|
|
float Padding0;
|
|
vec3 RelativeOrigin;
|
|
float Padding1;
|
|
float Radius;
|
|
float Intensity;
|
|
float InnerAngleCos;
|
|
float OuterAngleCos;
|
|
vec3 SpotDir;
|
|
float Padding2;
|
|
vec3 Color;
|
|
float Padding3;
|
|
};
|
|
|
|
layout(set = 0, binding = 1) buffer SurfaceIndexBuffer { uint surfaceIndices[]; };
|
|
layout(set = 0, binding = 2) buffer SurfaceBuffer { SurfaceInfo surfaces[]; };
|
|
layout(set = 0, binding = 3) buffer LightBuffer { LightInfo lights[]; };
|
|
layout(set = 0, binding = 4) buffer PortalBuffer { PortalInfo portals[]; };
|
|
|
|
layout(push_constant) uniform PushConstants
|
|
{
|
|
uint LightStart;
|
|
uint LightEnd;
|
|
int SurfaceIndex;
|
|
int PushPadding1;
|
|
vec3 LightmapOrigin;
|
|
float PushPadding2;
|
|
vec3 LightmapStepX;
|
|
float PushPadding3;
|
|
vec3 LightmapStepY;
|
|
float PushPadding4;
|
|
};
|
|
|
|
layout(location = 0) centroid in vec3 worldpos;
|
|
layout(location = 0) out vec4 fragcolor;
|
|
|
|
vec3 TraceSunLight(vec3 origin);
|
|
vec3 TraceLight(vec3 origin, vec3 normal, LightInfo light);
|
|
float TraceAmbientOcclusion(vec3 origin, vec3 normal);
|
|
vec2 Hammersley(uint i, uint N);
|
|
float RadicalInverse_VdC(uint bits);
|
|
|
|
bool TraceAnyHit(vec3 origin, float tmin, vec3 dir, float tmax);
|
|
bool TracePoint(vec3 origin, vec3 target, float tmin, vec3 dir, float tmax);
|
|
int TraceFirstHitTriangle(vec3 origin, float tmin, vec3 dir, float tmax);
|
|
int TraceFirstHitTriangleT(vec3 origin, float tmin, vec3 dir, float tmax, out float t);
|
|
|
|
void main()
|
|
{
|
|
vec3 normal = surfaces[SurfaceIndex].Normal;
|
|
vec3 origin = worldpos + normal * 0.1;
|
|
|
|
vec3 incoming = TraceSunLight(origin);
|
|
|
|
for (uint j = LightStart; j < LightEnd; j++)
|
|
{
|
|
incoming += TraceLight(origin, normal, lights[j]);
|
|
}
|
|
|
|
#if defined(USE_RAYQUERY) // The non-rtx version of TraceFirstHitTriangle is too slow to do AO without the shader getting killed ;(
|
|
incoming.rgb *= TraceAmbientOcclusion(origin, normal);
|
|
#endif
|
|
|
|
fragcolor = vec4(incoming, 1.0);
|
|
}
|
|
|
|
vec3 TraceLight(vec3 origin, vec3 normal, LightInfo light)
|
|
{
|
|
const float minDistance = 0.01;
|
|
vec3 incoming = vec3(0.0);
|
|
float dist = distance(light.RelativeOrigin, origin);
|
|
if (dist > minDistance && dist < light.Radius)
|
|
{
|
|
vec3 dir = normalize(light.RelativeOrigin - origin);
|
|
|
|
float distAttenuation = max(1.0 - (dist / light.Radius), 0.0);
|
|
float angleAttenuation = 1.0f;
|
|
if (SurfaceIndex >= 0)
|
|
{
|
|
angleAttenuation = max(dot(normal, dir), 0.0);
|
|
}
|
|
float spotAttenuation = 1.0;
|
|
if (light.OuterAngleCos > -1.0)
|
|
{
|
|
float cosDir = dot(dir, light.SpotDir);
|
|
spotAttenuation = smoothstep(light.OuterAngleCos, light.InnerAngleCos, cosDir);
|
|
spotAttenuation = max(spotAttenuation, 0.0);
|
|
}
|
|
|
|
float attenuation = distAttenuation * angleAttenuation * spotAttenuation;
|
|
if (attenuation > 0.0)
|
|
{
|
|
if(TracePoint(origin, light.Origin, minDistance, dir, dist))
|
|
{
|
|
incoming.rgb += light.Color * (attenuation * light.Intensity);
|
|
}
|
|
}
|
|
}
|
|
|
|
return incoming;
|
|
}
|
|
|
|
vec3 TraceSunLight(vec3 origin)
|
|
{
|
|
const float minDistance = 0.01;
|
|
vec3 incoming = vec3(0.0);
|
|
const float dist = 32768.0;
|
|
|
|
int primitiveID = TraceFirstHitTriangle(origin, minDistance, SunDir, dist);
|
|
if (primitiveID != -1)
|
|
{
|
|
SurfaceInfo surface = surfaces[surfaceIndices[primitiveID]];
|
|
incoming.rgb += SunColor * SunIntensity * surface.Sky;
|
|
}
|
|
return incoming;
|
|
}
|
|
|
|
float TraceAmbientOcclusion(vec3 origin, vec3 normal)
|
|
{
|
|
const float minDistance = 0.05;
|
|
const float aoDistance = 100;
|
|
const int SampleCount = 128;
|
|
|
|
vec3 N = normal;
|
|
vec3 up = abs(N.x) < abs(N.y) ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);
|
|
vec3 tangent = normalize(cross(up, N));
|
|
vec3 bitangent = cross(N, tangent);
|
|
|
|
float ambience = 0.0f;
|
|
for (uint i = 0; i < SampleCount; i++)
|
|
{
|
|
vec2 Xi = Hammersley(i, SampleCount);
|
|
vec3 H = normalize(vec3(Xi.x * 2.0f - 1.0f, Xi.y * 2.0f - 1.0f, 1.5 - length(Xi)));
|
|
vec3 L = H.x * tangent + H.y * bitangent + H.z * N;
|
|
|
|
float hitDistance;
|
|
int primitiveID = TraceFirstHitTriangleT(origin, minDistance, L, aoDistance, hitDistance);
|
|
if (primitiveID != -1)
|
|
{
|
|
SurfaceInfo surface = surfaces[surfaceIndices[primitiveID]];
|
|
if (surface.Sky == 0.0)
|
|
{
|
|
ambience += clamp(hitDistance / aoDistance, 0.0, 1.0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ambience += 1.0;
|
|
}
|
|
}
|
|
return ambience / float(SampleCount);
|
|
}
|
|
|
|
vec2 Hammersley(uint i, uint N)
|
|
{
|
|
return vec2(float(i) / float(N), RadicalInverse_VdC(i));
|
|
}
|
|
|
|
float RadicalInverse_VdC(uint bits)
|
|
{
|
|
bits = (bits << 16u) | (bits >> 16u);
|
|
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
|
|
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
|
|
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
|
|
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
|
|
return float(bits) * 2.3283064365386963e-10f; // / 0x100000000
|
|
}
|
|
|
|
#if defined(USE_RAYQUERY)
|
|
|
|
int TraceFirstHitTriangleNoPortal(vec3 origin, float tmin, vec3 dir, float tmax, out float t)
|
|
{
|
|
rayQueryEXT rayQuery;
|
|
rayQueryInitializeEXT(rayQuery, acc, gl_RayFlagsTerminateOnFirstHitEXT, 0xFF, origin, tmin, dir, tmax);
|
|
|
|
while(rayQueryProceedEXT(rayQuery))
|
|
{
|
|
if (rayQueryGetIntersectionTypeEXT(rayQuery, false) == gl_RayQueryCommittedIntersectionTriangleEXT)
|
|
{
|
|
rayQueryConfirmIntersectionEXT(rayQuery);
|
|
}
|
|
}
|
|
|
|
if (rayQueryGetIntersectionTypeEXT(rayQuery, true) == gl_RayQueryCommittedIntersectionTriangleEXT)
|
|
{
|
|
t = rayQueryGetIntersectionTEXT(rayQuery, true);
|
|
return rayQueryGetIntersectionPrimitiveIndexEXT(rayQuery, true);
|
|
}
|
|
else
|
|
{
|
|
t = tmax;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
bool TraceAnyHit(vec3 origin, float tmin, vec3 dir, float tmax)
|
|
{
|
|
rayQueryEXT rayQuery;
|
|
rayQueryInitializeEXT(rayQuery, acc, gl_RayFlagsTerminateOnFirstHitEXT, 0xFF, origin, tmin, dir, tmax);
|
|
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;
|
|
}
|
|
*/
|
|
|
|
struct TraceHit
|
|
{
|
|
float fraction;
|
|
int triangle;
|
|
float b;
|
|
float c;
|
|
};
|
|
|
|
TraceHit find_first_hit(RayBBox ray)
|
|
{
|
|
TraceHit hit;
|
|
hit.fraction = 1.0;
|
|
hit.triangle = -1;
|
|
hit.b = 0.0;
|
|
hit.c = 0.0;
|
|
|
|
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 < hit.fraction)
|
|
{
|
|
hit.fraction = t;
|
|
hit.triangle = nodes[a].element_index / 3;
|
|
hit.b = baryB;
|
|
hit.c = baryC;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
stack[stackIndex++] = nodes[a].right;
|
|
stack[stackIndex++] = nodes[a].left;
|
|
}
|
|
}
|
|
} while (stackIndex > 0);
|
|
return hit;
|
|
}
|
|
|
|
int TraceFirstHitTriangleNoPortal(vec3 origin, float tmin, vec3 dir, float tmax, out float tparam)
|
|
{
|
|
// Perform segmented tracing to keep the ray AABB box smaller
|
|
vec3 ray_start = origin;
|
|
vec3 ray_end = origin + dir * tmax;
|
|
vec3 ray_dir = dir;
|
|
float tracedist = tmax;
|
|
float segmentlen = max(200.0, tracedist / 20.0);
|
|
for (float t = 0.0; t < tracedist; t += segmentlen)
|
|
{
|
|
float segstart = t;
|
|
float segend = min(t + segmentlen, tracedist);
|
|
|
|
RayBBox ray = create_ray(ray_start + ray_dir * segstart, ray_start + ray_dir * segend);
|
|
TraceHit hit = find_first_hit(ray);
|
|
if (hit.fraction < 1.0)
|
|
{
|
|
tparam = hit.fraction = segstart * (1.0 - hit.fraction) + segend * hit.fraction;
|
|
return hit.triangle;
|
|
}
|
|
}
|
|
|
|
tparam = tracedist;
|
|
return -1;
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
int TraceFirstHitTriangleT(vec3 origin, float tmin, vec3 dir, float tmax, out float t)
|
|
{
|
|
int primitiveID;
|
|
while(true)
|
|
{
|
|
primitiveID = TraceFirstHitTriangleNoPortal(origin, tmin, dir, tmax, t);
|
|
|
|
if(primitiveID < 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
SurfaceInfo surface = surfaces[surfaceIndices[primitiveID]];
|
|
|
|
if(surface.PortalIndex == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Portal was hit: Apply transformation onto the ray
|
|
mat4 transformationMatrix = portals[surface.PortalIndex].Transformation;
|
|
|
|
origin = (transformationMatrix * vec4(origin + dir * t, 1.0)).xyz;
|
|
dir = (transformationMatrix * vec4(dir, 0.0)).xyz;
|
|
tmax -= t;
|
|
}
|
|
return primitiveID;
|
|
}
|
|
|
|
int TraceFirstHitTriangle(vec3 origin, float tmin, vec3 dir, float tmax)
|
|
{
|
|
float t;
|
|
return TraceFirstHitTriangleT(origin, tmin, dir, tmax, t);
|
|
}
|
|
|
|
bool TraceAnyHit(vec3 origin, float tmin, vec3 dir, float tmax)
|
|
{
|
|
return TraceFirstHitTriangle(origin, tmin, dir, tmax) >= 0;
|
|
}
|
|
|
|
bool TracePoint(vec3 origin, vec3 target, float tmin, vec3 dir, float tmax)
|
|
{
|
|
int primitiveID;
|
|
float t;
|
|
while(true)
|
|
{
|
|
t = tmax;
|
|
primitiveID = TraceFirstHitTriangleNoPortal(origin, tmin, dir, tmax, t);
|
|
|
|
origin += dir * t;
|
|
tmax -= t;
|
|
|
|
if(primitiveID < 0)
|
|
{
|
|
// We didn't hit anything
|
|
break;
|
|
}
|
|
|
|
SurfaceInfo surface = surfaces[surfaceIndices[primitiveID]];
|
|
|
|
if(surface.PortalIndex == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if(dot(surface.Normal, dir) >= 0.0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
mat4 transformationMatrix = portals[surface.PortalIndex].Transformation;
|
|
origin = (transformationMatrix * vec4(origin, 1.0)).xyz;
|
|
dir = (transformationMatrix * vec4(dir, 0.0)).xyz;
|
|
|
|
#if defined(USE_RAYQUERY)
|
|
#else
|
|
origin += dir * tmin;
|
|
tmax -= tmin;
|
|
#endif
|
|
}
|
|
|
|
return distance(origin, target) <= 1.0;
|
|
}
|