Atlas tile adjustments

This commit is contained in:
Magnus Norddahl 2024-02-28 00:59:41 +01:00
commit c98930a88c
5 changed files with 63 additions and 58 deletions

View file

@ -127,13 +127,13 @@ void LevelMesh::BuildTileSurfaceLists()
if (surface->SectorGroup == PlaneGroups[j].sectorGroup)
{
float direction = PlaneGroups[j].plane.XYZ() | surface->Plane.XYZ();
if (direction >= 0.9999f && direction <= 1.001f)
if (direction >= 0.999f && direction <= 1.01f)
{
auto point = (surface->Plane.XYZ() * surface->Plane.W);
auto planeDistance = (PlaneGroups[j].plane.XYZ() | point) - PlaneGroups[j].plane.W;
float dist = std::abs(planeDistance);
if (dist <= 0.01f)
if (dist <= 0.1f)
{
planeGroupIndex = (int)j;
break;
@ -198,18 +198,14 @@ void LevelMesh::PackLightmapAtlas(int lightmapStartIndex)
std::sort(sortedTiles.begin(), sortedTiles.end(), [](LightmapTile* a, LightmapTile* b) { return a->AtlasLocation.Height != b->AtlasLocation.Height ? a->AtlasLocation.Height > b->AtlasLocation.Height : a->AtlasLocation.Width > b->AtlasLocation.Width; });
RectPacker packer(LMTextureSize, LMTextureSize, RectPacker::Spacing(0));
// We do not need to add spacing here as this is already built into the tile size itself.
RectPacker packer(LMTextureSize, LMTextureSize, RectPacker::Spacing(0), RectPacker::Padding(0));
for (LightmapTile* tile : sortedTiles)
{
int sampleWidth = tile->AtlasLocation.Width;
int sampleHeight = tile->AtlasLocation.Height;
auto result = packer.insert(sampleWidth, sampleHeight);
int x = result.pos.x, y = result.pos.y;
tile->AtlasLocation.X = x;
tile->AtlasLocation.Y = y;
auto result = packer.insert(tile->AtlasLocation.Width, tile->AtlasLocation.Height);
tile->AtlasLocation.X = result.pos.x;
tile->AtlasLocation.Y = result.pos.y;
tile->AtlasLocation.ArrayIndex = lightmapStartIndex + (int)result.pageIndex;
}

View file

@ -57,8 +57,8 @@ struct LightmapTile
FVector2 ToUV(const FVector3& vert) const
{
FVector3 localPos = vert - Transform.TranslateWorldToLocal;
float u = (1.0f + (localPos | Transform.ProjLocalToU)) / (AtlasLocation.Width + 2);
float v = (1.0f + (localPos | Transform.ProjLocalToV)) / (AtlasLocation.Height + 2);
float u = (localPos | Transform.ProjLocalToU) / AtlasLocation.Width;
float v = (localPos | Transform.ProjLocalToV) / AtlasLocation.Height;
return FVector2(u, v);
}
@ -101,63 +101,66 @@ struct LightmapTile
void SetupTileTransform(int textureSize)
{
BBox bounds = Bounds;
// These calculations align the tile so that there's a one texel border around the actual surface in the tile.
//
// This removes sampling artifacts as a linear sampler reads from a 2x2 area.
// The tile is also aligned to the grid to keep aliasing artifacts consistent.
// round off dimensions
FVector3 roundedSize;
for (int i = 0; i < 3; i++)
{
bounds.min[i] = SampleDimension * (floor(bounds.min[i] / SampleDimension) - 1);
bounds.max[i] = SampleDimension * (ceil(bounds.max[i] / SampleDimension) + 1);
roundedSize[i] = (bounds.max[i] - bounds.min[i]) / SampleDimension;
}
FVector3 uvMin;
uvMin.X = std::floor(Bounds.min.X / SampleDimension) - 1.0f;
uvMin.Y = std::floor(Bounds.min.Y / SampleDimension) - 1.0f;
uvMin.Z = std::floor(Bounds.min.Z / SampleDimension) - 1.0f;
FVector3 uvMax;
uvMax.X = std::floor(Bounds.max.X / SampleDimension) + 2.0f;
uvMax.Y = std::floor(Bounds.max.Y / SampleDimension) + 2.0f;
uvMax.Z = std::floor(Bounds.max.Z / SampleDimension) + 2.0f;
FVector3 tCoords[2] = { FVector3(0.0f, 0.0f, 0.0f), FVector3(0.0f, 0.0f, 0.0f) };
PlaneAxis axis = BestAxis(Plane);
int width;
int height;
switch (axis)
int width, height;
switch (BestAxis(Plane))
{
default:
case AXIS_YZ:
width = (int)roundedSize.Y;
height = (int)roundedSize.Z;
width = (int)(uvMax.Y - uvMin.Y);
height = (int)(uvMax.Z - uvMin.Z);
tCoords[0].Y = 1.0f / SampleDimension;
tCoords[1].Z = 1.0f / SampleDimension;
break;
case AXIS_XZ:
width = (int)roundedSize.X;
height = (int)roundedSize.Z;
width = (int)(uvMax.X - uvMin.X);
height = (int)(uvMax.Z - uvMin.Z);
tCoords[0].X = 1.0f / SampleDimension;
tCoords[1].Z = 1.0f / SampleDimension;
break;
case AXIS_XY:
width = (int)roundedSize.X;
height = (int)roundedSize.Y;
width = (int)(uvMax.X - uvMin.X);
height = (int)(uvMax.Y - uvMin.Y);
tCoords[0].X = 1.0f / SampleDimension;
tCoords[1].Y = 1.0f / SampleDimension;
break;
}
// clamp width
if (width > textureSize - 2)
textureSize -= 6; // Lightmapper needs some padding when baking
// Tile can never be bigger than the texture.
if (width > textureSize)
{
tCoords[0] *= ((float)(textureSize - 2) / (float)width);
width = (textureSize - 2);
tCoords[0] *= textureSize / (float)width;
width = textureSize;
}
if (height > textureSize)
{
tCoords[1] *= textureSize / (float)height;
height = textureSize;
}
// clamp height
if (height > textureSize - 2)
{
tCoords[1] *= ((float)(textureSize - 2) / (float)height);
height = (textureSize - 2);
}
Transform.TranslateWorldToLocal.X = uvMin.X * SampleDimension;
Transform.TranslateWorldToLocal.Y = uvMin.Y * SampleDimension;
Transform.TranslateWorldToLocal.Z = uvMin.Z * SampleDimension;
Transform.TranslateWorldToLocal = bounds.min;
Transform.ProjLocalToU = tCoords[0];
Transform.ProjLocalToV = tCoords[1];

View file

@ -119,8 +119,8 @@ void VkLightmapper::SelectTiles(const TArray<LightmapTile*>& tiles)
bakeImage.maxY = 0;
selectedTiles.Clear();
const int spacing = 5; // Note: the spacing is here to avoid that the resolve sampler finds data from other surface tiles
RectPacker packer(bakeImageSize - spacing, bakeImageSize - spacing, RectPacker::Spacing(spacing));
// We use a 3 texel spacing between rectangles so that the blur pass will not pick up anything from a neighbour tile.
RectPacker packer(bakeImageSize, bakeImageSize, RectPacker::Spacing(3), RectPacker::Padding(3));
for (int i = 0, count = tiles.Size(); i < count; i++)
{
@ -130,21 +130,25 @@ void VkLightmapper::SelectTiles(const TArray<LightmapTile*>& tiles)
continue;
// Only grab surfaces until our bake texture is full
auto result = packer.insert(tile->AtlasLocation.Width + 2, tile->AtlasLocation.Height + 2);
auto result = packer.insert(tile->AtlasLocation.Width, tile->AtlasLocation.Height);
if (result.pageIndex == 0)
{
SelectedTile selected;
selected.Tile = tile;
selected.X = result.pos.x + 1;
selected.Y = result.pos.y + 1;
selected.X = result.pos.x;
selected.Y = result.pos.y;
selectedTiles.Push(selected);
bakeImage.maxX = std::max<uint16_t>(bakeImage.maxX, uint16_t(selected.X + tile->AtlasLocation.Width + spacing));
bakeImage.maxY = std::max<uint16_t>(bakeImage.maxY, uint16_t(selected.Y + tile->AtlasLocation.Height + spacing));
bakeImage.maxX = std::max<uint16_t>(bakeImage.maxX, uint16_t(result.pos.x + tile->AtlasLocation.Width));
bakeImage.maxY = std::max<uint16_t>(bakeImage.maxY, uint16_t(result.pos.y + tile->AtlasLocation.Height));
tile->NeedsUpdate = false;
}
}
// Include the padding
bakeImage.maxX += 3;
bakeImage.maxY += 3;
}
void VkLightmapper::Render()
@ -155,7 +159,7 @@ void VkLightmapper::Render()
RenderPassBegin()
.RenderPass(raytrace.renderPass.get())
.RenderArea(0, 0, bakeImageSize, bakeImageSize)
.RenderArea(0, 0, bakeImage.maxX, bakeImage.maxY)
.Framebuffer(bakeImage.raytrace.Framebuffer.get())
.AddClearColor(0.0f, 0.0f, 0.0f, 0.0f)
.Execute(cmdbuffer);
@ -878,6 +882,9 @@ void VkLightmapper::CreateBlurPipeline()
.Create(fb->GetDevice());
blur.sampler = SamplerBuilder()
.MinFilter(VK_FILTER_NEAREST)
.MagFilter(VK_FILTER_NEAREST)
.MipmapMode(VK_SAMPLER_MIPMAP_MODE_NEAREST)
.DebugName("blur.Sampler")
.Create(fb->GetDevice());
}

View file

@ -21,7 +21,7 @@ void main()
uint LightEnd = surfaces[SurfaceIndex].LightEnd;
vec3 normal = surfaces[SurfaceIndex].Normal;
vec3 origin = worldpos + normal * 0.01;
vec3 origin = worldpos;
#if defined(USE_SUNLIGHT)
vec3 incoming = TraceSunLight(origin, normal);

View file

@ -29,9 +29,8 @@ void main()
gl_Position = vec4(vec2(TileX + x, TileY + y) / TextureSize * 2.0 - 1.0, 0.0, 1.0);
// Clip all surfaces to the edge of the tile (effectly we are applying a viewport/scissor to the tile)
// Note: the tile has a 1px border around it that we also draw into
gl_ClipDistance[0] = x + 1.0;
gl_ClipDistance[1] = y + 1.0;
gl_ClipDistance[2] = TileWidth + 1.0 - x;
gl_ClipDistance[3] = TileHeight + 1.0 - y;
gl_ClipDistance[0] = x;
gl_ClipDistance[1] = y;
gl_ClipDistance[2] = TileWidth - x;
gl_ClipDistance[3] = TileHeight - y;
}