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

View File

@ -43,9 +43,8 @@ public class MeshBuilder
var cellIdxOffset = 0;
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
// - Water surfaces
// - Door vision blockers
//
// 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)
{
primType = SurfaceType.Water;
// we no longer want water polys :)
continue;
}
else if ((cell.Flags & 8) != 0)
{