Initial implementation of recursive portal pointlight propagation

This commit is contained in:
RaveYard 2022-10-29 21:59:07 +02:00
parent b003565ad2
commit cb9416269e
6 changed files with 189 additions and 31 deletions

View file

@ -329,6 +329,19 @@ struct ThingLight
IntSector *sector;
MapSubsectorEx *ssect;
vec3 relativePosition = vec3(0);
// Portal aware position
vec3 LightRelativeOrigin() const
{
if (relativePosition != vec3(0))
{
return relativePosition;
}
return LightOrigin();
}
// Absolute X, Y, Z position of the light
vec3 LightOrigin() const
{
float originZ;

View file

@ -52,14 +52,16 @@ struct LightInfo
{
vec3 Origin;
float Padding0;
vec3 RelativeOrigin;
float Padding1;
float Radius;
float Intensity;
float InnerAngleCos;
float OuterAngleCos;
vec3 SpotDir;
float Padding1;
vec3 Color;
float Padding2;
vec3 Color;
float Padding3;
};
layout(set = 0, binding = 1) buffer SurfaceIndexBuffer { uint surfaceIndices[]; };
@ -91,6 +93,7 @@ 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);
@ -117,10 +120,10 @@ vec3 TraceLight(vec3 origin, vec3 normal, LightInfo light)
{
const float minDistance = 0.01;
vec3 incoming = vec3(0.0);
float dist = distance(light.Origin, origin);
float dist = distance(light.RelativeOrigin, origin);
if (dist > minDistance && dist < light.Radius)
{
vec3 dir = normalize(light.Origin - origin);
vec3 dir = normalize(light.RelativeOrigin - origin);
float distAttenuation = max(1.0 - (dist / light.Radius), 0.0);
float angleAttenuation = 1.0f;
@ -139,12 +142,13 @@ vec3 TraceLight(vec3 origin, vec3 normal, LightInfo light)
float attenuation = distAttenuation * angleAttenuation * spotAttenuation;
if (attenuation > 0.0)
{
if (!TraceAnyHit(origin, minDistance, dir, dist))
if(TracePoint(origin, light.Origin, minDistance, dir, dist))
{
incoming.rgb += light.Color * (attenuation * light.Intensity);
}
}
}
return incoming;
}
@ -236,6 +240,7 @@ int TraceFirstHitTriangleNoPortal(vec3 origin, float tmin, vec3 dir, float tmax,
}
else
{
t = tmax;
return -1;
}
}
@ -466,6 +471,8 @@ int TraceFirstHitTriangleNoPortal(vec3 origin, float tmin, vec3 dir, float tmax,
return hit.triangle;
}
}
tparam = tracedist;
return -1;
}
@ -512,4 +519,48 @@ 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;
}
)glsl";

View file

@ -216,6 +216,7 @@ void GPURaytracer::RenderAtlasImage(size_t pageIndex)
for (ThingLight* light : surface->LightList)
{
lightinfo->Origin = light->LightOrigin();
lightinfo->RelativeOrigin = light->LightRelativeOrigin();
lightinfo->Radius = light->LightRadius();
lightinfo->Intensity = light->intensity;
lightinfo->InnerAngleCos = light->innerAngleCos;

View file

@ -50,16 +50,20 @@ struct LightInfo
{
vec3 Origin;
float Padding0;
vec3 RelativeOrigin;
float Padding1;
float Radius;
float Intensity;
float InnerAngleCos;
float OuterAngleCos;
vec3 SpotDir;
float Padding1;
vec3 Color;
float Padding2;
vec3 Color;
float Padding3;
};
static_assert(sizeof(LightInfo) == sizeof(float) * 20);
struct CollisionNodeBufferHeader
{
int root;

View file

@ -148,30 +148,7 @@ LevelMesh::LevelMesh(FLevel &doomMap, int sampleDistance, int textureSize)
Collision = std::make_unique<TriangleMeshShape>(MeshVertices.Data(), MeshVertices.Size(), MeshElements.Data(), MeshElements.Size());
printf("Building light list...\n\n");
for (ThingLight& light : map->ThingLights)
{
SphereShape sphere;
sphere.center = light.LightOrigin();
sphere.radius = light.LightRadius();
for (int triangleIndex : TriangleMeshShape::find_all_hits(Collision.get(), &sphere))
{
Surface* surface = surfaces[MeshSurfaces[triangleIndex]].get();
bool found = false;
for (ThingLight* light2 : surface->LightList)
{
if (light2 == &light)
{
found = true;
break;
}
}
if (!found)
surface->LightList.push_back(&light);
}
}
BuildLightLists(doomMap);
/*
std::map<int, int> lightStats;
for (auto& surface : surfaces)
@ -182,6 +159,73 @@ LevelMesh::LevelMesh(FLevel &doomMap, int sampleDistance, int textureSize)
*/
}
#include <set> // hack
std::set<Portal, RejectRecursivePortals> touchedPortals;
int depth = 0;
void LevelMesh::PropagateLight(FLevel& doomMap, ThingLight *light)
{
if (depth > 4)
{
return;
}
SphereShape sphere;
sphere.center = light->LightRelativeOrigin();
sphere.radius = light->LightRadius();
depth++;
std::set<Portal, RejectRecursivePortals> portalsToErase;
for (int triangleIndex : TriangleMeshShape::find_all_hits(Collision.get(), &sphere))
{
Surface* surface = surfaces[MeshSurfaces[triangleIndex]].get();
if (surface->portalIndex >= 0)
{
auto portal = portals[surface->portalIndex].get();
if (touchedPortals.insert(*portal).second)
{
auto fakeLight = std::make_unique<ThingLight>(*light);
fakeLight->relativePosition = portal->TransformPosition(light->LightRelativeOrigin());
PropagateLight(doomMap, fakeLight.get());
portalsToErase.insert(*portal);
portalLights.push_back(std::move(fakeLight));
}
}
// Add light to the list if it isn't already there
bool found = false;
for (ThingLight* light2 : surface->LightList)
{
if (light2 == light)
{
found = true;
break;
}
}
if (!found)
surface->LightList.push_back(light);
}
for (auto& portal : portalsToErase)
{
touchedPortals.erase(portal);
}
depth--;
}
void LevelMesh::BuildLightLists(FLevel& doomMap)
{
for (ThingLight& light : map->ThingLights)
{
PropagateLight(doomMap, &light);
}
}
// Determines a lightmap block in which to map to the lightmap texture.
// Width and height of the block is calcuated and steps are computed to determine where each texel will be positioned on the surface
void LevelMesh::BuildSurfaceParams(Surface* surface)

View file

@ -63,6 +63,45 @@ enum SurfaceType
struct Portal
{
mat4 transformation = mat4::identity();
inline vec3 TransformPosition(const vec3& pos) const
{
auto v = transformation * vec4(pos, 1.0);
return vec3(v.x, v.y, v.z);
}
inline vec3 TransformRotation(const vec3& dir) const
{
auto v = transformation * vec4(dir, 0.0);
return vec3(v.x, v.y, v.z);
}
// Check if the portal is inverse
inline bool IsInversePortal(const Portal& portal) const
{
auto diff = portal.TransformPosition(TransformPosition(vec3(0)));
return abs(diff.x) < 0.001 && abs(diff.y) < 0.001 && abs(diff.z) < 0.001;
}
// Check if the portal transformation is equal
inline bool IsEqualPortal(const Portal& portal) const
{
auto diff = portal.TransformPosition(vec3(0)) - TransformPosition(vec3(0));
return (abs(diff.x) < 0.001 && abs(diff.y) < 0.001 && abs(diff.z) < 0.001);
}
};
// for use with std::set to recursively go through portals
struct RejectRecursivePortals
{
bool operator()(const Portal& a, const Portal& b) const
{
static_assert(sizeof(Portal) == sizeof(float) * 4 * 4);
return !(a.IsInversePortal(b) || a.IsEqualPortal(b)) && std::memcmp(&a.transformation, &b.transformation, sizeof(mat4)) < 0;
}
};
struct Surface
@ -148,6 +187,8 @@ public:
std::unique_ptr<TriangleMeshShape> Collision;
private:
std::vector<std::unique_ptr<ThingLight>> portalLights; // Portal generated lights
void CreateSubsectorSurfaces(FLevel &doomMap);
void CreateCeilingSurface(FLevel &doomMap, MapSubsectorEx *sub, IntSector *sector, int typeIndex, bool is3DFloor);
void CreateFloorSurface(FLevel &doomMap, MapSubsectorEx *sub, IntSector *sector, int typeIndex, bool is3DFloor);
@ -155,6 +196,10 @@ private:
void BuildSurfaceParams(Surface* surface);
BBox GetBoundsFromSurface(const Surface* surface);
void BuildLightLists(FLevel &doomMap);
void PropagateLight(FLevel& doomMap, ThingLight* thing);
void BlurSurfaces();
void FinishSurface(RectPacker& packer, Surface* surface);