Add the shaders

This commit is contained in:
Magnus Norddahl 2023-10-06 00:34:04 +02:00
parent 0b09fa9793
commit f8b8005d4a
17 changed files with 1056 additions and 2 deletions

View file

@ -63,6 +63,21 @@ set(ZDRAY_SOURCES
src/lightmapper/gpuraytracer.h
src/lightmapper/stacktrace.cpp
src/lightmapper/stacktrace.h
src/lightmapper/glsl/binding_lightmapper.glsl.h
src/lightmapper/glsl/binding_raytrace.glsl.h
src/lightmapper/glsl/binding_textures.glsl.h
src/lightmapper/glsl/frag_blur.glsl.h
src/lightmapper/glsl/frag_copy.glsl.h
src/lightmapper/glsl/frag_raytrace.glsl.h
src/lightmapper/glsl/frag_resolve.glsl.h
src/lightmapper/glsl/polyfill_rayquery.glsl.h
src/lightmapper/glsl/trace_ambient_occlusion.glsl.h
src/lightmapper/glsl/trace_levelmesh.glsl.h
src/lightmapper/glsl/trace_light.glsl.h
src/lightmapper/glsl/trace_sunlight.glsl.h
src/lightmapper/glsl/vert_copy.glsl.h
src/lightmapper/glsl/vert_raytrace.glsl.h
src/lightmapper/glsl/vert_screenquad.glsl.h
src/models/model.cpp
src/models/model.h
src/models/model_md2.h
@ -97,6 +112,7 @@ source_group("src\\Platform" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sr
source_group("src\\Platform\\Windows" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/platform/windows/.+")
source_group("src\\Wad" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/wad/.+")
source_group("src\\Lightmapper" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/lightmapper/.+")
source_group("src\\Lightmapper\\glsl" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/lightmapper/glsl/.+")
source_group("src\\Models" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/src/models/.+")
source_group("thirdparty" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/.+")

View file

@ -0,0 +1,91 @@
static const char* binding_lightmapper_glsl = R"glsl(
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;
int TextureIndex;
float Alpha;
};
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[]; };
#if defined(USE_DRAWINDIRECT)
struct LightmapRaytracePC
{
uint LightStart;
uint LightEnd;
int SurfaceIndex;
int PushPadding1;
vec3 WorldToLocal;
float TextureSize;
vec3 ProjLocalToU;
float PushPadding2;
vec3 ProjLocalToV;
float PushPadding3;
float TileX;
float TileY;
float TileWidth;
float TileHeight;
};
layout(std430, set = 0, binding = 5) buffer ConstantsBuffer { LightmapRaytracePC constants[]; };
#else
layout(push_constant) uniform LightmapRaytracePC
{
uint LightStart;
uint LightEnd;
int SurfaceIndex;
int PushPadding1;
vec3 WorldToLocal;
float TextureSize;
vec3 ProjLocalToU;
float PushPadding2;
vec3 ProjLocalToV;
float PushPadding3;
float TileX;
float TileY;
float TileWidth;
float TileHeight;
};
#endif
)glsl";

View file

@ -0,0 +1,42 @@
static const char* binding_raytrace_glsl = R"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[];
};
#endif
struct SurfaceVertex
{
vec4 pos;
vec2 uv;
float Padding1, Padding2;
};
layout(std430, set = 1, binding = 1) buffer VertexBuffer { SurfaceVertex vertices[]; };
layout(std430, set = 1, binding = 2) buffer ElementBuffer { int elements[]; };
)glsl";

View file

@ -0,0 +1,5 @@
static const char* binding_textures_glsl = R"glsl(
layout(set = 2, binding = 0) uniform sampler2D textures[];
)glsl";

View file

@ -0,0 +1,35 @@
static const char* frag_blur_glsl = R"glsl(
layout(set = 0, binding = 0) uniform sampler2D tex;
layout(location = 0) in vec2 TexCoord;
layout(location = 0) out vec4 fragcolor;
vec4 centerFragColor;
vec4 clampedSample(vec4 f)
{
return f != vec4(0, 0, 0, 0) ? f : centerFragColor;
}
void main()
{
ivec2 size = textureSize(tex, 0);
vec2 texCoord = gl_FragCoord.xy / vec2(size);
centerFragColor = textureOffset(tex, texCoord, ivec2(0, 0));
#if defined(BLUR_HORIZONTAL)
fragcolor =
centerFragColor * 0.5 +
clampedSample(textureOffset(tex, texCoord, ivec2( 1, 0))) * 0.25 +
clampedSample(textureOffset(tex, texCoord, ivec2(-1, 0))) * 0.25;
#else
fragcolor =
centerFragColor * 0.5 +
clampedSample(textureOffset(tex, texCoord, ivec2(0, 1))) * 0.25 +
clampedSample(textureOffset(tex, texCoord, ivec2(0,-1))) * 0.25;
#endif
}
)glsl";

View file

@ -0,0 +1,13 @@
static const char* frag_copy_glsl = R"glsl(
layout(set = 0, binding = 0) uniform sampler2D Tex;
layout(location = 0) in vec2 TexCoord;
layout(location = 0) out vec4 FragColor;
void main()
{
FragColor = texture(Tex, TexCoord);
}
)glsl";

View file

@ -0,0 +1,50 @@
static const char* frag_raytrace_glsl = R"glsl(
#include <shaders/lightmap/binding_lightmapper.glsl>
#include <shaders/lightmap/binding_raytrace.glsl>
#include <shaders/lightmap/binding_textures.glsl>
#include <shaders/lightmap/polyfill_rayquery.glsl>
#include <shaders/lightmap/trace_levelmesh.glsl>
#include <shaders/lightmap/trace_sunlight.glsl>
#include <shaders/lightmap/trace_light.glsl>
#include <shaders/lightmap/trace_ambient_occlusion.glsl>
#if defined(USE_DRAWINDIRECT)
layout(location = 1) in flat int InstanceIndex;
#endif
layout(location = 0) centroid in vec3 worldpos;
layout(location = 0) out vec4 fragcolor;
void main()
{
#if defined(USE_DRAWINDIRECT)
uint LightStart = constants[InstanceIndex].LightStart;
uint LightEnd = constants[InstanceIndex].LightEnd;
int SurfaceIndex = constants[InstanceIndex].SurfaceIndex;
#endif
vec3 normal = surfaces[SurfaceIndex].Normal;
vec3 origin = worldpos + normal * 0.1;
#if defined(USE_SUNLIGHT)
vec3 incoming = TraceSunLight(origin, normal, SurfaceIndex);
#else
vec3 incoming = vec3(0.0);
#endif
for (uint j = LightStart; j < LightEnd; j++)
{
incoming += TraceLight(origin, normal, lights[j], SurfaceIndex);
}
#if defined(USE_AO)
incoming.rgb *= TraceAmbientOcclusion(origin, normal);
#endif
fragcolor = vec4(incoming, 1.0);
}
)glsl";

View file

@ -0,0 +1,49 @@
static const char* frag_resolve_glsl = R"glsl(
layout(set = 0, binding = 0) uniform sampler2DMS tex;
layout(location = 0) in vec2 TexCoord;
layout(location = 0) out vec4 fragcolor;
vec4 samplePixel(ivec2 pos, int count)
{
vec4 c = vec4(0.0);
for (int i = 0; i < count; i++)
{
c += texelFetch(tex, pos, i);
}
if (c.a > 0.0)
c /= c.a;
return c;
}
void main()
{
int count = textureSamples(tex);
ivec2 size = textureSize(tex);
ivec2 pos = ivec2(gl_FragCoord.xy);
vec4 c = samplePixel(pos, count);
if (c.a == 0.0)
{
for (int y = -1; y <= 1; y++)
{
for (int x = -1; x <= 1; x++)
{
if (x != 0 || y != 0)
{
ivec2 pos2;
pos2.x = clamp(pos.x + x, 0, size.x - 1);
pos2.y = clamp(pos.y + y, 0, size.y - 1);
c += samplePixel(pos2, count);
}
}
}
if (c.a > 0.0)
c /= c.a;
}
fragcolor = c;
}
)glsl";

View file

@ -0,0 +1,269 @@
static const char* polyfill_rayquery_glsl = R"glsl(
#if defined(USE_RAYQUERY)
int TraceFirstHitTriangleNoPortal(vec3 origin, float tmin, vec3 dir, float tmax, out float t, out vec3 primitiveWeights)
{
rayQueryEXT rayQuery;
rayQueryInitializeEXT(rayQuery, acc, gl_RayFlagsCullBackFacingTrianglesEXT, 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);
primitiveWeights.xy = rayQueryGetIntersectionBarycentricsEXT(rayQuery, true);
primitiveWeights.z = 1.0 - primitiveWeights.x - primitiveWeights.y;
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 | gl_RayFlagsCullBackFacingTrianglesEXT, 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]].pos.xyz;
p[1] = vertices[elements[start_element + 1]].pos.xyz;
p[2] = vertices[elements[start_element + 2]].pos.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, out vec3 primitiveWeights)
{
// 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;
primitiveWeights.x = hit.b;
primitiveWeights.y = hit.c;
primitiveWeights.z = 1.0 - hit.b - hit.c;
return hit.triangle;
}
}
tparam = tracedist;
return -1;
}
#endif
)glsl";

View file

@ -0,0 +1,57 @@
static const char* trace_ambient_occlusion_glsl = R"glsl(
vec2 Hammersley(uint i, uint N);
float RadicalInverse_VdC(uint bits);
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
}
)glsl";

View file

@ -0,0 +1,139 @@
static const char* trace_levelmesh_glsl = R"glsl(
vec4 rayColor;
vec4 alphaBlend(vec4 a, vec4 b);
vec4 BeerLambertSimple(vec4 medium, vec4 ray_color);
vec4 blend(vec4 a, vec4 b);
int TraceFirstHitTriangleT(vec3 origin, float tmin, vec3 dir, float tmax, out float t)
{
int primitiveID = -1;
vec3 primitiveWeights;
for (int i = 0; i < 4; i++)
{
primitiveID = TraceFirstHitTriangleNoPortal(origin, tmin, dir, tmax, t, primitiveWeights);
if(primitiveID < 0)
{
break;
}
SurfaceInfo surface = surfaces[surfaceIndices[primitiveID]];
if(surface.PortalIndex == 0)
{
int index = primitiveID * 3;
vec2 uv = vertices[elements[index + 1]].uv * primitiveWeights.x + vertices[elements[index + 2]].uv * primitiveWeights.y + vertices[elements[index + 0]].uv * primitiveWeights.z;
if (surface.TextureIndex < 0)
{
break;
}
vec4 color = texture(textures[surface.TextureIndex], uv);
color.w *= surface.Alpha;
if (color.w > 0.999 || all(lessThan(rayColor.rgb, vec3(0.001))))
{
break;
}
rayColor = blend(color, rayColor);
}
// 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;
vec3 primitiveWeights;
for (int i = 0; i < 4; i++)
{
t = tmax;
primitiveID = TraceFirstHitTriangleNoPortal(origin, tmin, dir, tmax, t, primitiveWeights);
origin += dir * t;
tmax -= t;
if(primitiveID < 0)
{
// We didn't hit anything
break;
}
SurfaceInfo surface = surfaces[surfaceIndices[primitiveID]];
if (surface.PortalIndex == 0)
{
int index = primitiveID * 3;
vec2 uv = vertices[elements[index + 1]].uv * primitiveWeights.x + vertices[elements[index + 2]].uv * primitiveWeights.y + vertices[elements[index + 0]].uv * primitiveWeights.z;
if (surface.TextureIndex < 0)
{
break;
}
vec4 color = texture(textures[surface.TextureIndex], uv);
color.w *= surface.Alpha;
if (color.w > 0.999 || all(lessThan(rayColor.rgb, vec3(0.001))))
{
break;
}
rayColor = blend(color, rayColor);
}
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;
}
return distance(origin, target) <= 1.0;
}
vec4 alphaBlend(vec4 a, vec4 b)
{
float na = a.w + b.w * (1.0 - a.w);
return vec4((a.xyz * a.w + b.xyz * b.w * (1.0 - a.w)) / na, max(0.001, na));
}
vec4 BeerLambertSimple(vec4 medium, vec4 ray_color) // based on Beer-Lambert law
{
float z = medium.w;
ray_color.rgb *= exp(-medium.rgb * vec3(z));
return ray_color;
}
vec4 blend(vec4 a, vec4 b)
{
return BeerLambertSimple(vec4(1.0 - a.rgb, a.w), b);
}
)glsl";

View file

@ -0,0 +1,79 @@
static const char* trace_light_glsl = R"glsl(
vec2 getVogelDiskSample(int sampleIndex, int sampleCount, float phi);
vec3 TraceLight(vec3 origin, vec3 normal, LightInfo light, int surfaceIndex)
{
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 defined(USE_SOFTSHADOWS)
vec3 v = (abs(dir.x) > abs(dir.y)) ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);
vec3 xdir = normalize(cross(dir, v));
vec3 ydir = cross(dir, xdir);
float lightsize = 10;
int step_count = 10;
for (int i = 0; i < step_count; i++)
{
vec2 gridoffset = getVogelDiskSample(i, step_count, gl_FragCoord.x + gl_FragCoord.y * 13.37) * lightsize;
vec3 pos = light.Origin + xdir * gridoffset.x + ydir * gridoffset.y;
rayColor = vec4(light.Color.rgb, 1.0);
if (TracePoint(origin, pos, minDistance, normalize(pos - origin), distance(origin, pos)))
{
incoming.rgb += (rayColor.rgb * rayColor.w) * (attenuation * light.Intensity) / float(step_count);
}
}
#else
rayColor = vec4(light.Color.rgb, 1.0);
if(TracePoint(origin, light.Origin, minDistance, dir, dist))
{
incoming.rgb += (rayColor.rgb * rayColor.w) * (attenuation * light.Intensity);
}
#endif
}
}
return incoming;
}
vec2 getVogelDiskSample(int sampleIndex, int sampleCount, float phi)
{
const float goldenAngle = radians(180.0) * (3.0 - sqrt(5.0));
float sampleIndexF = float(sampleIndex);
float sampleCountF = float(sampleCount);
float r = sqrt((sampleIndexF + 0.5) / sampleCountF); // Assuming index and count are positive
float theta = sampleIndexF * goldenAngle + phi;
float sine = sin(theta);
float cosine = cos(theta);
return vec2(cosine, sine) * r;
}
)glsl";

View file

@ -0,0 +1,60 @@
static const char* trace_sunlight_glsl = R"glsl(
vec2 getVogelDiskSample(int sampleIndex, int sampleCount, float phi);
vec3 TraceSunLight(vec3 origin, vec3 normal, int surfaceIndex)
{
float angleAttenuation = 1.0f;
if (surfaceIndex >= 0)
{
angleAttenuation = max(dot(normal, SunDir), 0.0);
if (angleAttenuation == 0.0)
return vec3(0.0);
}
const float minDistance = 0.01;
vec3 incoming = vec3(0.0);
const float dist = 32768.0;
#if defined(USE_SOFTSHADOWS)
vec3 target = origin + SunDir * dist;
vec3 dir = SunDir;
vec3 v = (abs(dir.x) > abs(dir.y)) ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);
vec3 xdir = normalize(cross(dir, v));
vec3 ydir = cross(dir, xdir);
float lightsize = 100;
int step_count = 10;
for (int i = 0; i < step_count; i++)
{
vec2 gridoffset = getVogelDiskSample(i, step_count, gl_FragCoord.x + gl_FragCoord.y * 13.37) * lightsize;
vec3 pos = target + xdir * gridoffset.x + ydir * gridoffset.y;
rayColor = vec4(SunColor.rgb * SunIntensity, 1.0);
int primitiveID = TraceFirstHitTriangle(origin, minDistance, normalize(pos - origin), dist);
if (primitiveID != -1)
{
SurfaceInfo surface = surfaces[surfaceIndices[primitiveID]];
incoming.rgb += rayColor.rgb * rayColor.w * surface.Sky / float(step_count);
}
}
#else
rayColor = vec4(SunColor.rgb * SunIntensity, 1.0);
int primitiveID = TraceFirstHitTriangle(origin, minDistance, SunDir, dist);
if (primitiveID != -1)
{
SurfaceInfo surface = surfaces[surfaceIndices[primitiveID]];
incoming.rgb = rayColor.rgb * rayColor.w * surface.Sky;
}
#endif
return incoming * angleAttenuation;
}
)glsl";

View file

@ -0,0 +1,44 @@
static const char* vert_copy_glsl = R"glsl(
layout(push_constant) uniform PushConstants
{
int SrcTexSize;
int DestTexSize;
int Padding1;
int Padding2;
};
struct TileCopy
{
ivec2 SrcPos;
ivec2 DestPos;
ivec2 TileSize;
int Padding1, Padding2;
};
layout(std430, set = 0, binding = 1) buffer CopyBuffer
{
TileCopy tiles[];
};
layout(location = 0) out vec2 TexCoord;
vec2 positions[4] = vec2[](
vec2(0.0, 0.0),
vec2(1.0, 0.0),
vec2(0.0, 1.0),
vec2(1.0, 1.0)
);
void main()
{
TileCopy tile = tiles[gl_InstanceIndex];
vec2 uv = positions[gl_VertexIndex];
vec2 src = (vec2(tile.SrcPos) + uv * vec2(tile.TileSize)) / float(SrcTexSize);
vec2 dest = (vec2(tile.DestPos) + uv * vec2(tile.TileSize)) / float(DestTexSize);
gl_Position = vec4(dest * 2.0 - 1.0, 0.0, 1.0);
TexCoord = src;
}
)glsl";

View file

@ -0,0 +1,44 @@
static const char* vert_raytrace_glsl = R"glsl(
#include <shaders/lightmap/binding_lightmapper.glsl>
layout(location = 0) in vec3 aPosition;
layout(location = 0) out vec3 worldpos;
#if defined(USE_DRAWINDIRECT)
layout(location = 1) out flat int InstanceIndex;
#endif
void main()
{
#if defined(USE_DRAWINDIRECT)
vec3 WorldToLocal = constants[gl_InstanceIndex].WorldToLocal;
float TextureSize = constants[gl_InstanceIndex].TextureSize;
vec3 ProjLocalToU = constants[gl_InstanceIndex].ProjLocalToU;
vec3 ProjLocalToV = constants[gl_InstanceIndex].ProjLocalToV;
float TileX = constants[gl_InstanceIndex].TileX;
float TileY = constants[gl_InstanceIndex].TileY;
float TileWidth = constants[gl_InstanceIndex].TileWidth;
float TileHeight = constants[gl_InstanceIndex].TileHeight;
InstanceIndex = gl_InstanceIndex;
#endif
worldpos = aPosition;
// Project to position relative to tile
vec3 localPos = aPosition - WorldToLocal;
float x = dot(localPos, ProjLocalToU);
float y = dot(localPos, ProjLocalToV);
// Find the position in the output texture
gl_Position = vec4(vec2(TileX + x, TileY + y) / TextureSize * 2.0 - 1.0, 0.0, 1.0);
// Clip all surfaces to the edge of the tile (effectly we are applying a viewport/scissor to the tile)
// Note: the tile has a 1px border around it that we also draw into
gl_ClipDistance[0] = x + 1.0;
gl_ClipDistance[1] = y + 1.0;
gl_ClipDistance[2] = TileWidth + 1.0 - x;
gl_ClipDistance[3] = TileHeight + 1.0 - y;
}
)glsl";

View file

@ -0,0 +1,23 @@
static const char* vert_screenquad_glsl = R"glsl(
layout(location = 0) out vec2 TexCoord;
vec2 positions[3] = vec2[](
vec2(-1.0, -1.0),
vec2( 3.0, -1.0),
vec2(-1.0, 3.0)
);
vec2 uvs[3] = vec2[](
vec2(0.0, 0.0),
vec2(2.0, 0.0),
vec2(0.0, 2.0)
);
void main()
{
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
TexCoord = uvs[gl_VertexIndex];
}
)glsl";

View file

@ -5,6 +5,23 @@
#include "zvulkan/vulkanbuilders.h"
#include "Framework/halffloat.h"
#include "framework/zstring.h"
#include <map>
#include "glsl/binding_lightmapper.glsl.h"
#include "glsl/binding_raytrace.glsl.h"
#include "glsl/binding_textures.glsl.h"
#include "glsl/frag_blur.glsl.h"
#include "glsl/frag_copy.glsl.h"
#include "glsl/frag_raytrace.glsl.h"
#include "glsl/frag_resolve.glsl.h"
#include "glsl/polyfill_rayquery.glsl.h"
#include "glsl/trace_ambient_occlusion.glsl.h"
#include "glsl/trace_levelmesh.glsl.h"
#include "glsl/trace_light.glsl.h"
#include "glsl/trace_sunlight.glsl.h"
#include "glsl/vert_copy.glsl.h"
#include "glsl/vert_raytrace.glsl.h"
#include "glsl/vert_screenquad.glsl.h"
#define USE_DRAWINDIRECT
@ -626,9 +643,30 @@ int VkLightmap::GetRaytracePipelineIndex()
FString VkLightmap::LoadPrivateShaderLump(const char* lumpname)
{
// To do: load the shader
static std::map<FString, FString> sources =
{
{ "shaders/lightmap/binding_lightmapper.glsl", binding_lightmapper_glsl },
{ "shaders/lightmap/binding_raytrace.glsl", binding_raytrace_glsl },
{ "shaders/lightmap/binding_textures.glsl", binding_textures_glsl },
{ "shaders/lightmap/frag_blur.glsl", frag_blur_glsl },
{ "shaders/lightmap/frag_copy.glsl", frag_copy_glsl },
{ "shaders/lightmap/frag_raytrace.glsl", frag_raytrace_glsl },
{ "shaders/lightmap/frag_resolve.glsl", frag_resolve_glsl },
{ "shaders/lightmap/polyfill_rayquery.glsl", polyfill_rayquery_glsl },
{ "shaders/lightmap/trace_ambient_occlusion.glsl", trace_ambient_occlusion_glsl },
{ "shaders/lightmap/trace_levelmesh.glsl", trace_levelmesh_glsl },
{ "shaders/lightmap/trace_light.glsl", trace_light_glsl },
{ "shaders/lightmap/trace_sunlight.glsl", trace_sunlight_glsl },
{ "shaders/lightmap/vert_copy.glsl", vert_copy_glsl },
{ "shaders/lightmap/vert_raytrace.glsl", vert_raytrace_glsl },
{ "shaders/lightmap/vert_screenquad.glsl", vert_screenquad_glsl }
};
return FString();
auto it = sources.find(lumpname);
if (it != sources.end())
return it->second;
else
return FString();
}
ShaderIncludeResult VkLightmap::OnInclude(FString headerName, FString includerName, size_t depth, bool system)