Use occlusion rays for non-sun tracing

More performant, no longer requires annoying fiddling to check distances, and removes the need for water surfaces in the mesh. Also reduces the number of edge case misses
This commit is contained in:
Jarrod Doyle 2025-01-15 20:26:23 +00:00
parent 4b3016d895
commit a5078b0f2b
Signed by: Jayrude
GPG Key ID: 38B57B16E7C0ADF7
2 changed files with 29 additions and 52 deletions

View File

@ -730,7 +730,7 @@ public class LightMapper
continue; continue;
} }
if (TraceRay(light.Position, point)) if (!TraceOcclusion(light.Position, point))
{ {
strength += targetWeights[idx] * light.StrengthAtPoint(point, plane, settings.AnimLightCutoff); strength += targetWeights[idx] * light.StrengthAtPoint(point, plane, settings.AnimLightCutoff);
} }
@ -818,26 +818,15 @@ public class LightMapper
var offset = offsets[i]; var offset = offsets[i];
var pos = basePosition + offset; var pos = basePosition + offset;
// Embree has robustness issues when hitting poly edges which
// results in false misses. To alleviate this we pre-push everything
// slightly towards the center of the poly.
var centerOffset = polyCenter - pos;
if (centerOffset.LengthSquared() > MathUtils.Epsilon)
{
pos += Vector3.Normalize(centerOffset) * MathUtils.Epsilon;
}
// If we can't see our target point from the center of the poly // If we can't see our target point from the center of the poly
// then it's outside the world. We need to clip the point to slightly // then we need to clip the point to slightly inside the poly
// inside the poly and retrace to avoid three problems: // and retrace to avoid two problems:
// 1. Darkened spots from lightmap pixels whose center is outside // 1. Darkened spots from lightmap pixels whose center is outside
// the polygon but is partially contained in the polygon // the polygon but is partially contained in the polygon
// 2. Darkened spots from linear filtering of points outside the // 2. Darkened spots from linear filtering of points outside the
// polygon which have missed // polygon which have missed
// 3. Darkened spots where centers are on the exact edge of a poly var occluded = TraceOcclusion(polyCenter + planeMapper.Normal * 0.25f, pos);
// which can sometimes cause Embree to miss casts if (occluded)
var inPoly = TraceRay(polyCenter + planeMapper.Normal * 0.25f, pos);
if (!inPoly)
{ {
var p2d = planeMapper.MapTo2d(pos); var p2d = planeMapper.MapTo2d(pos);
p2d = MathUtils.ClipPointToPoly2d(p2d, v2ds); p2d = MathUtils.ClipPointToPoly2d(p2d, v2ds);
@ -850,48 +839,36 @@ public class LightMapper
return tracePoints; return tracePoints;
} }
private bool TraceRay(Vector3 origin, Vector3 target) private bool TraceOcclusion(Vector3 origin, Vector3 target)
{ {
var hitDistanceFromTarget = float.MinValue; var direction = target - origin;
var hitSurfaceType = SurfaceType.Water; var ray = new Ray
while (hitDistanceFromTarget < -MathUtils.Epsilon && hitSurfaceType == SurfaceType.Water)
{ {
var direction = target - origin; Origin = origin,
var hitResult = _scene.Trace(new Ray Direction = Vector3.Normalize(direction),
{ };
Origin = origin,
Direction = Vector3.Normalize(direction),
});
hitDistanceFromTarget = hitResult.Distance - direction.Length(); // Epsilon is used here to avoid occlusion when origin lies exactly on a poly
hitSurfaceType = _triangleTypeMap[(int)hitResult.PrimId]; return _scene.IsOccluded(new ShadowRay(ray, direction.Length() - MathUtils.Epsilon));
origin = hitResult.Position += direction * MathUtils.Epsilon;
}
// A large epsilon is used here to fix shadow acne on sloped surfaces :)
return Math.Abs(hitDistanceFromTarget) < 10 * MathUtils.Epsilon;
} }
// TODO: Can this be merged with the above? // TODO: direction should already be normalised here
private bool TraceSunRay(Vector3 origin, Vector3 direction) private bool TraceSunRay(Vector3 origin, Vector3 direction)
{ {
// Avoid self intersection // Avoid self intersection
origin += direction * MathUtils.Epsilon; origin += direction * MathUtils.Epsilon;
var hitSurfaceType = SurfaceType.Water; var hitResult = _scene.Trace(new Ray
while (hitSurfaceType == SurfaceType.Water)
{ {
var hitResult = _scene.Trace(new Ray Origin = origin,
{ Direction = Vector3.Normalize(direction),
Origin = origin, });
Direction = Vector3.Normalize(direction),
});
hitSurfaceType = _triangleTypeMap[(int)hitResult.PrimId]; if (hitResult)
origin = hitResult.Position += direction * MathUtils.Epsilon; {
return _triangleTypeMap[(int)hitResult.PrimId] == SurfaceType.Sky;
} }
return false;
return hitSurfaceType == SurfaceType.Sky;
} }
private void SetAnimLightCellMaps() private void SetAnimLightCellMaps()

View File

@ -43,9 +43,8 @@ public class MeshBuilder
var cellIdxOffset = 0; var cellIdxOffset = 0;
for (var polyIdx = 0; polyIdx < numPolys; polyIdx++) for (var polyIdx = 0; polyIdx < numPolys; polyIdx++)
{ {
// There's 3 types of poly that we need to include in the mesh: // There's 2 types of poly that we need to include in the mesh:
// - Terrain // - Terrain
// - Water surfaces
// - Door vision blockers // - Door vision blockers
// //
// Door vision blockers are the interesting one. They're not RenderPolys at all, just flagged Polys. // Door vision blockers are the interesting one. They're not RenderPolys at all, just flagged Polys.
@ -56,7 +55,8 @@ public class MeshBuilder
} }
else if (polyIdx < numRenderPolys) else if (polyIdx < numRenderPolys)
{ {
primType = SurfaceType.Water; // we no longer want water polys :)
continue;
} }
else if ((cell.Flags & 8) != 0) else if ((cell.Flags & 8) != 0)
{ {