mirror of
https://github.com/ZDoom/ZDRay.git
synced 2024-11-24 21:01:15 +00:00
Initial implementation of recursive portal pointlight propagation
This commit is contained in:
parent
b003565ad2
commit
cb9416269e
6 changed files with 189 additions and 31 deletions
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue