Extend the Lighting module with a per-cell lightmap. LightmapBuilder (pure, tested) fills an ambient base, shades occluder cells, and adds point lights that attenuate with distance and are blocked by occluders between source and cell (grid-traced soft shadows). Lightmap holds the grid plus a greyscale texture (Upload) and a bilinear SampleAt for the simulation; a PointLight component places lights in the world. LightmapSystem rebuilds the grid a few times a second (ambient from the day/night cycle with a night floor, occluders from a game-supplied grid, point lights from the ECS); LightmapRenderSystem multiplies the lightmap over the world after the sprite flush and under the HUD. Wired via scene.UseLighting(...), sampled through Lighting.SampleAt. Docs updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
d0104df304
commit
79d406a9f4
@@ -0,0 +1,69 @@
|
||||
using MrGameEng.Lighting;
|
||||
using Xunit;
|
||||
|
||||
namespace MrGameEng.Lighting.Tests;
|
||||
|
||||
public class LightmapBuilderTests
|
||||
{
|
||||
private static float[] Build(
|
||||
int width,
|
||||
int height,
|
||||
float ambient,
|
||||
bool[] occluders,
|
||||
params LightSample[] lights
|
||||
)
|
||||
{
|
||||
var light = new float[width * height];
|
||||
LightmapBuilder.Build(light, width, height, ambient, occluders, lights);
|
||||
return light;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Ambient_WithoutOccluders_IsUniform()
|
||||
{
|
||||
var light = Build(4, 4, 0.6f, new bool[16]);
|
||||
Assert.All(light, v => Assert.Equal(0.6f, v, 5));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OccluderCell_IsDarkerThanOpenCell()
|
||||
{
|
||||
var occ = new bool[9];
|
||||
occ[4] = true; // centre cell shaded
|
||||
var light = Build(3, 3, 0.8f, occ);
|
||||
Assert.True(light[4] < light[0]);
|
||||
Assert.Equal(0.8f * LightmapBuilder.OccluderShade, light[4], 5);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PointLight_IsBrighterNearSourceAndFadesToRadius()
|
||||
{
|
||||
var light = Build(7, 1, 0f, new bool[7], new LightSample(0, 0, 6f, 1f));
|
||||
Assert.True(light[0] > light[2] && light[2] > light[5]);
|
||||
Assert.InRange(light[6], 0f, 0.01f); // на радиусе свет угасает
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Occluder_CastsShadowBehindIt()
|
||||
{
|
||||
var occ = new bool[7];
|
||||
occ[3] = true; // препятствие между источником (0) и дальними клетками
|
||||
var light = Build(7, 1, 0f, occ, new LightSample(0, 0, 10f, 1f));
|
||||
|
||||
Assert.True(light[1] > 0f); // перед препятствием — освещено
|
||||
Assert.Equal(0f, light[5], 5); // за препятствием — тень
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Values_StayWithinUnitRange()
|
||||
{
|
||||
var light = Build(
|
||||
5,
|
||||
5,
|
||||
0.5f,
|
||||
new bool[25],
|
||||
new LightSample(2, 2, 4f, 2f) // яркий свет — проверяем клампинг
|
||||
);
|
||||
Assert.All(light, v => Assert.InRange(v, 0f, 1f));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user