Rewrite the GLSL ray trace code so it does not use global variables!!

Fix portals not working
This commit is contained in:
Magnus Norddahl 2024-02-09 12:08:16 +01:00
commit c1d0963da4
8 changed files with 151 additions and 171 deletions

View file

@ -630,7 +630,7 @@ void VkLevelMeshUploader::UploadPortals()
size_t copysize = range.Size * sizeof(PortalInfo);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->PortalBuffer.get(), range.Offset * sizeof(PortalInfo), 0, copysize);
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->PortalBuffer.get(), datapos, range.Offset * sizeof(PortalInfo), copysize);
datapos += copysize;
}
}

View file

@ -198,7 +198,9 @@ bool DoomLevelMesh::TraceSky(const FVector3& start, FVector3 direction, float di
int DoomLevelMesh::AddSurfaceLights(const LevelMeshSurface* surface, LevelMeshLight* list, int listMaxSize)
{
FLightNode* node = GetSurfaceLightNode(static_cast<const DoomLevelMeshSurface*>(surface));
std::pair<FLightNode*, int> nodePortalGroup = GetSurfaceLightNode(static_cast<const DoomLevelMeshSurface*>(surface));
FLightNode* node = nodePortalGroup.first;
int portalgroup = nodePortalGroup.second;
if (!node)
return 0;
@ -208,7 +210,7 @@ int DoomLevelMesh::AddSurfaceLights(const LevelMeshSurface* surface, LevelMeshLi
FDynamicLight* light = node->lightsource;
if (light && light->Trace())
{
DVector3 pos = light->Pos; //light->PosRelative(portalgroup);
DVector3 pos = light->PosRelative(portalgroup);
LevelMeshLight& meshlight = list[listpos++];
meshlight.Origin = { (float)pos.X, (float)pos.Y, (float)pos.Z };
@ -251,12 +253,14 @@ int DoomLevelMesh::AddSurfaceLights(const LevelMeshSurface* surface, LevelMeshLi
return listpos;
}
FLightNode* DoomLevelMesh::GetSurfaceLightNode(const DoomLevelMeshSurface* doomsurf)
std::pair<FLightNode*, int> DoomLevelMesh::GetSurfaceLightNode(const DoomLevelMeshSurface* doomsurf)
{
FLightNode* node = nullptr;
int portalgroup = 0;
if (doomsurf->Type == ST_FLOOR || doomsurf->Type == ST_CEILING)
{
node = doomsurf->Subsector->section->lighthead;
portalgroup = doomsurf->Subsector->sector->PortalGroup;
}
else if (doomsurf->Type == ST_MIDDLESIDE || doomsurf->Type == ST_UPPERSIDE || doomsurf->Type == ST_LOWERSIDE)
{
@ -265,18 +269,21 @@ FLightNode* DoomLevelMesh::GetSurfaceLightNode(const DoomLevelMeshSurface* dooms
{
subsector_t* subsector = level.PointInRenderSubsector((doomsurf->Side->V1()->fPos() + doomsurf->Side->V2()->fPos()) * 0.5);
node = subsector->section->lighthead;
portalgroup = subsector->sector->PortalGroup;
}
else if (!doomsurf->ControlSector)
{
node = doomsurf->Side->lighthead;
portalgroup = doomsurf->Side->sector->PortalGroup;
}
else // 3d floor needs light from the sidedef on the other side
{
int otherside = doomsurf->Side->linedef->sidedef[0] == doomsurf->Side ? 1 : 0;
node = doomsurf->Side->linedef->sidedef[otherside]->lighthead;
portalgroup = doomsurf->Side->linedef->sidedef[otherside]->sector->PortalGroup;
}
}
return node;
return { node, portalgroup };
}
void DoomLevelMesh::CreateSurfaces(FLevelLocals& doomMap)
@ -1021,9 +1028,10 @@ void DoomLevelMesh::CreatePortals(FLevelLocals& doomMap)
auto d = sector->GetPortalDisplacement(plane);
if (!d.isZero())
{
// Note: Y and Z is swapped in the shader due to how the hwrenderer was implemented
VSMatrix transformation;
transformation.loadIdentity();
transformation.translate((float)d.X, (float)d.Y, 0.0f);
transformation.translate((float)d.X, 0.0f, (float)d.Y);
int targetSectorGroup = 0;
auto portalDestination = sector->GetPortal(plane)->mDestination;
@ -1089,8 +1097,9 @@ void DoomLevelMesh::CreatePortals(FLevelLocals& doomMap)
z = tz - sz;
}
transformation.rotate((float)sourceLine->getPortalAngleDiff().Degrees(), 0.0f, 0.0f, 1.0f);
transformation.translate((float)(targetXYZ.X - sourceXYZ.X), (float)(targetXYZ.Y - sourceXYZ.Y), (float)z);
// Note: Y and Z is swapped in the shader due to how the hwrenderer was implemented
transformation.rotate((float)sourceLine->getPortalAngleDiff().Degrees(), 0.0f, 1.0f, 0.0f);
transformation.translate((float)(targetXYZ.X - sourceXYZ.X), (float)z, (float)(targetXYZ.Y - sourceXYZ.Y));
int targetSectorGroup = 0;
if (auto sector = targetLine->frontsector ? targetLine->frontsector : targetLine->backsector)

View file

@ -87,7 +87,7 @@ private:
int GetSampleDimension(const DoomLevelMeshSurface& surf);
void CreatePortals(FLevelLocals& doomMap);
FLightNode* GetSurfaceLightNode(const DoomLevelMeshSurface* doomsurf);
std::pair<FLightNode*, int> GetSurfaceLightNode(const DoomLevelMeshSurface* doomsurf);
TArray<SideSurfaceRange> Sides;
TArray<FlatSurfaceRange> Flats;

View file

@ -1,8 +1,17 @@
struct TraceResult
{
float t;
vec3 primitiveWeights;
int primitiveIndex;
};
#if defined(USE_RAYQUERY)
int TraceFirstHitTriangleNoPortal(vec3 origin, float tmin, vec3 dir, float tmax, out float t, out vec3 primitiveWeights)
TraceResult TraceFirstHit(vec3 origin, float tmin, vec3 dir, float tmax)
{
TraceResult result;
rayQueryEXT rayQuery;
rayQueryInitializeEXT(rayQuery, acc, gl_RayFlagsCullBackFacingTrianglesEXT, 0xFF, origin, tmin, dir, tmax);
@ -16,18 +25,20 @@ int TraceFirstHitTriangleNoPortal(vec3 origin, float tmin, vec3 dir, float tmax,
if (rayQueryGetIntersectionTypeEXT(rayQuery, true) == gl_RayQueryCommittedIntersectionTriangleEXT)
{
t = rayQueryGetIntersectionTEXT(rayQuery, true);
result.t = rayQueryGetIntersectionTEXT(rayQuery, true);
primitiveWeights.xy = rayQueryGetIntersectionBarycentricsEXT(rayQuery, true);
primitiveWeights.z = 1.0 - primitiveWeights.x - primitiveWeights.y;
result.primitiveWeights.xy = rayQueryGetIntersectionBarycentricsEXT(rayQuery, true);
result.primitiveWeights.z = 1.0 - result.primitiveWeights.x - result.primitiveWeights.y;
return rayQueryGetIntersectionPrimitiveIndexEXT(rayQuery, true);
result.primitiveIndex = rayQueryGetIntersectionPrimitiveIndexEXT(rayQuery, true);
}
else
{
t = tmax;
return -1;
result.t = tmax;
result.primitiveIndex = -1;
}
return result;
}
/*
@ -234,8 +245,10 @@ TraceHit find_first_hit(RayBBox ray)
return hit;
}
int TraceFirstHitTriangleNoPortal(vec3 origin, float tmin, vec3 dir, float tmax, out float tparam, out vec3 primitiveWeights)
TraceResult TraceFirstHit(vec3 origin, float tmin, vec3 dir, float tmax)
{
TraceResult result;
// Perform segmented tracing to keep the ray AABB box smaller
vec3 ray_start = origin;
vec3 ray_end = origin + dir * tmax;
@ -251,16 +264,17 @@ int TraceFirstHitTriangleNoPortal(vec3 origin, float tmin, vec3 dir, float tmax,
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;
result.t = mix(segstart, segend, hit.fraction);
result.primitiveWeights.x = hit.b;
result.primitiveWeights.y = hit.c;
result.primitiveWeights.z = 1.0 - hit.b - hit.c;
result.primitiveIndex = hit.triangle;
}
}
tparam = tracedist;
return -1;
result.t = tracedist;
result.primitiveIndex = -1;
return result;
}
#endif

View file

@ -20,14 +20,19 @@ float TraceAmbientOcclusion(vec3 origin, vec3 normal)
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)
TraceResult result = TraceFirstHit(origin, minDistance, L, aoDistance);
// Ignore surfaces with textures, skies or portals
if (result.primitiveIndex != -1)
{
SurfaceInfo surface = surfaces[surfaceIndices[primitiveID]];
if (surface.Sky == 0.0)
SurfaceInfo surface = GetSurface(result.primitiveIndex);
if (surface.Sky == 0.0 && surface.TextureIndex == 0 && surface.PortalIndex == 0)
{
ambience += clamp(hitDistance / aoDistance, 0.0, 1.0);
ambience += clamp(result.t / aoDistance, 0.0, 1.0);
}
else
{
ambience += 1.0;
}
}
else

View file

@ -1,126 +1,38 @@
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)
SurfaceInfo GetSurface(int primitiveIndex)
{
int primitiveID = -1;
vec3 primitiveWeights;
for (int i = 0; i < 4; i++)
return surfaces[surfaceIndices[primitiveIndex]];
}
vec2 GetSurfaceUV(int primitiveIndex, vec3 primitiveWeights)
{
int index = primitiveIndex * 3;
return
vertices[elements[index + 1]].uv * primitiveWeights.x +
vertices[elements[index + 2]].uv * primitiveWeights.y +
vertices[elements[index + 0]].uv * primitiveWeights.z;
}
vec4 BlendTexture(SurfaceInfo surface, vec2 uv, vec4 rayColor)
{
if (surface.TextureIndex == 0)
{
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 rayColor;
}
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++)
else
{
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;
vec4 color = texture(textures[surface.TextureIndex], uv);
return BeerLambertSimple(vec4(1.0 - color.rgb, color.a * surface.Alpha), rayColor);
}
return distance(origin, target) <= 1.0;
}
vec4 alphaBlend(vec4 a, vec4 b)
void TransformRay(uint portalIndex, inout vec3 origin, inout vec3 dir)
{
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));
mat4 transformationMatrix = portals[portalIndex].Transformation;
origin = (transformationMatrix * vec4(origin, 1.0)).xyz;
dir = (transformationMatrix * vec4(dir, 0.0)).xyz;
}
vec4 BeerLambertSimple(vec4 medium, vec4 ray_color) // based on Beer-Lambert law
@ -129,9 +41,3 @@ vec4 BeerLambertSimple(vec4 medium, vec4 ray_color) // based on Beer-Lambert law
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);
}

View file

@ -1,4 +1,5 @@
vec4 TracePointLightRay(vec3 origin, vec3 lightpos, float tmin, vec4 rayColor);
vec2 getVogelDiskSample(int sampleIndex, int sampleCount, float phi);
vec3 TraceLight(vec3 origin, vec3 normal, LightInfo light, int surfaceIndex)
@ -27,6 +28,8 @@ vec3 TraceLight(vec3 origin, vec3 normal, LightInfo light, int surfaceIndex)
float attenuation = distAttenuation * angleAttenuation * spotAttenuation;
if (attenuation > 0.0)
{
vec4 rayColor = vec4(light.Color.rgb * (attenuation * light.Intensity), 1.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);
@ -40,19 +43,11 @@ vec3 TraceLight(vec3 origin, vec3 normal, LightInfo light, int surfaceIndex)
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);
}
incoming.rgb += TracePointLightRay(origin, pos, minDistance, rayColor).rgb / 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);
}
incoming.rgb += TracePointLightRay(origin, light.Origin, minDistance, rayColor).rgb;
#endif
}
}
@ -60,6 +55,38 @@ vec3 TraceLight(vec3 origin, vec3 normal, LightInfo light, int surfaceIndex)
return incoming;
}
vec4 TracePointLightRay(vec3 origin, vec3 lightpos, float tmin, vec4 rayColor)
{
vec3 dir = normalize(lightpos - origin);
float tmax = distance(origin, lightpos);
for (int i = 0; i < 3; i++)
{
TraceResult result = TraceFirstHit(origin, tmin, dir, tmax);
// We hit nothing. Point light is visible.
if (result.primitiveIndex == -1)
return rayColor;
SurfaceInfo surface = GetSurface(result.primitiveIndex);
// Blend with surface texture
rayColor = BlendTexture(surface, GetSurfaceUV(result.primitiveIndex, result.primitiveWeights), rayColor);
// Stop if it isn't a portal, or there is no light left
if (surface.PortalIndex == 0 || rayColor.r + rayColor.g + rayColor.b <= 0.0)
return vec4(0.0);
// Move to surface hit point
origin += dir * result.t;
tmax -= result.t;
// Move through the portal
TransformRay(surface.PortalIndex, origin, dir);
}
return vec4(0.0);
}
vec2 getVogelDiskSample(int sampleIndex, int sampleCount, float phi)
{
const float goldenAngle = radians(180.0) * (3.0 - sqrt(5.0));

View file

@ -1,5 +1,6 @@
vec2 getVogelDiskSample(int sampleIndex, int sampleCount, float phi);
vec4 TraceSunRay(vec3 origin, float tmin, vec3 dir, float tmax, vec4 rayColor);
vec3 TraceSunLight(vec3 origin, vec3 normal, int surfaceIndex)
{
@ -15,6 +16,8 @@ vec3 TraceSunLight(vec3 origin, vec3 normal, int surfaceIndex)
vec3 incoming = vec3(0.0);
const float dist = 65536.0;
vec4 rayColor = vec4(SunColor.rgb * SunIntensity, 1.0);
#if defined(USE_SOFTSHADOWS)
vec3 target = origin + SunDir * dist;
@ -29,29 +32,45 @@ vec3 TraceSunLight(vec3 origin, vec3 normal, int surfaceIndex)
{
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);
}
incoming.rgb += TraceSunRay(origin, minDistance, normalize(pos - origin), dist, rayColor).rgb / 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;
}
incoming.rgb = TraceSunRay(origin, minDistance, SunDir, dist, rayColor).rgb;
#endif
return incoming * angleAttenuation;
}
vec4 TraceSunRay(vec3 origin, float tmin, vec3 dir, float tmax, vec4 rayColor)
{
for (int i = 0; i < 3; i++)
{
TraceResult result = TraceFirstHit(origin, tmin, dir, tmax);
// We hit nothing. We have to hit a sky surface to hit the sky.
if (result.primitiveIndex == -1)
return vec4(0.0);
SurfaceInfo surface = GetSurface(result.primitiveIndex);
// Blend with surface texture
rayColor = BlendTexture(surface, GetSurfaceUV(result.primitiveIndex, result.primitiveWeights), rayColor);
// Stop if it isn't a portal, or there is no light left
if (surface.PortalIndex == 0 || rayColor.r + rayColor.g + rayColor.b <= 0.0)
return rayColor * surface.Sky;
// Move to surface hit point
origin += dir * result.t;
tmax -= result.t;
if (tmax <= tmin)
return vec4(0.0);
// Move through the portal
TransformRay(surface.PortalIndex, origin, dir);
}
return vec4(0.0);
}