- make indirect light bounces optional (--bounce=1 to enable)

This commit is contained in:
Magnus Norddahl 2018-12-30 22:24:20 +01:00
parent 4dd0edbd3a
commit 78a3da5b8c
5 changed files with 123 additions and 101 deletions

View file

@ -44,7 +44,7 @@
#endif
extern int Multisample;
extern thread_local Vec3 *colorSamples;
extern int LightBounce;
LightmapBuilder::LightmapBuilder()
{
@ -59,7 +59,7 @@ void LightmapBuilder::NewTexture()
numTextures++;
allocBlocks.push_back(std::vector<int>(textureWidth));
textures.push_back(std::vector<uint16_t>(textureWidth * textureHeight * 3));
textures.push_back(std::make_unique<LightmapTexture>(textureWidth, textureHeight));
}
// Determines where to map a new block on to the lightmap texture
@ -398,30 +398,22 @@ void LightmapBuilder::BuildSurfaceParams(Surface *surface)
}
// Steps through each texel and traces a line to the world.
// For each non-occluded trace, color is accumulated and saved off into the lightmap texture based on what block is mapped to
void LightmapBuilder::TraceSurface(Surface *surface)
{
int sampleWidth;
int sampleHeight;
Vec3 normal;
Vec3 pos;
Vec3 tDelta;
int i;
int j;
uint16_t *currentTexture;
bool bShouldLookupTexture = false;
int sampleWidth = surface->lightmapDims[0];
int sampleHeight = surface->lightmapDims[1];
sampleWidth = surface->lightmapDims[0];
sampleHeight = surface->lightmapDims[1];
normal = surface->plane.Normal();
Vec3 normal = surface->plane.Normal();
int multisampleCount = Multisample;
surface->samples.resize(sampleWidth * sampleHeight);
Vec3 *colorSamples = surface->samples.data();
// start walking through each texel
for (i = 0; i < sampleHeight; i++)
for (int i = 0; i < sampleHeight; i++)
{
for (j = 0; j < sampleWidth; j++)
for (int j = 0; j < sampleWidth; j++)
{
Vec3 c(0.0f, 0.0f, 0.0f);
@ -440,35 +432,65 @@ void LightmapBuilder::TraceSurface(Surface *surface)
// convert the texel into world-space coordinates.
// this will be the origin in which a line will be traced from
pos = surface->lightmapOrigin + normal +
(surface->lightmapSteps[0] * multisamplePos.x) +
(surface->lightmapSteps[1] * multisamplePos.y);
Vec3 pos = surface->lightmapOrigin + normal + (surface->lightmapSteps[0] * multisamplePos.x) + (surface->lightmapSteps[1] * multisamplePos.y);
c += LightTexelSample(pos, surface);
}
c /= multisampleCount;
// if nothing at all was traced and color is completely black
// then this surface will not go through the extra rendering
// step in rendering the lightmap
if (c.x > 0.0f || c.y > 0.0f || c.z > 0.0f)
{
bShouldLookupTexture = true;
}
colorSamples[i * sampleWidth + j] = c;
}
}
colorSamples[i * LIGHTMAP_MAX_SIZE + j] = c;
// texture coordinates relative to block
for (int i = 0; i < surface->numVerts; i++)
{
Vec3 tDelta = surface->verts[i] - surface->bounds.min;
surface->lightmapCoords[i * 2 + 0] = tDelta.Dot(surface->textureCoords[0]);
surface->lightmapCoords[i * 2 + 1] = tDelta.Dot(surface->textureCoords[1]);
}
}
void LightmapBuilder::FinishSurface(Surface *surface)
{
int sampleWidth = surface->lightmapDims[0];
int sampleHeight = surface->lightmapDims[1];
Vec3 *colorSamples = surface->samples.data();
if (!surface->indirect.empty())
{
Vec3 *indirect = surface->indirect.data();
for (int i = 0; i < sampleHeight; i++)
{
for (int j = 0; j < sampleWidth; j++)
{
colorSamples[i * sampleWidth + j] += indirect[i * sampleWidth + j] * 0.5f;
}
}
}
// SVE redraws the scene for lightmaps, so for optimizations,
// tell the engine to ignore this surface if completely black
/*if (bShouldLookupTexture == false)
bool bShouldLookupTexture = false;
for (int i = 0; i < sampleHeight; i++)
{
for (int j = 0; j < sampleWidth; j++)
{
const auto &c = colorSamples[i * sampleWidth + j];
if (c.x > 0.0f || c.y > 0.0f || c.z > 0.0f)
{
bShouldLookupTexture = true;
break;
}
}
}
if (bShouldLookupTexture == false)
{
surface->lightmapNum = -1;
return;
}
else*/
else
{
int x = 0, y = 0;
int width = surface->lightmapDims[0];
@ -490,38 +512,34 @@ void LightmapBuilder::TraceSurface(Surface *surface)
}
}
uint16_t *currentTexture = textures[surface->lightmapNum]->Pixels();
lock.unlock();
// calculate texture coordinates
for (i = 0; i < surface->numVerts; i++)
for (int i = 0; i < surface->numVerts; i++)
{
tDelta = surface->verts[i] - surface->bounds.min;
surface->lightmapCoords[i * 2 + 0] =
(tDelta.Dot(surface->textureCoords[0]) + x + 0.5f) / (float)textureWidth;
surface->lightmapCoords[i * 2 + 1] =
(tDelta.Dot(surface->textureCoords[1]) + y + 0.5f) / (float)textureHeight;
Vec3 tDelta = surface->verts[i] - surface->bounds.min;
surface->lightmapCoords[i * 2 + 0] = (tDelta.Dot(surface->textureCoords[0]) + x + 0.5f) / (float)textureWidth;
surface->lightmapCoords[i * 2 + 1] = (tDelta.Dot(surface->textureCoords[1]) + y + 0.5f) / (float)textureHeight;
}
surface->lightmapOffs[0] = x;
surface->lightmapOffs[1] = y;
}
std::unique_lock<std::mutex> lock(mutex);
currentTexture = textures[surface->lightmapNum].data();
lock.unlock();
// store results to lightmap texture
for (i = 0; i < sampleHeight; i++)
{
for (j = 0; j < sampleWidth; j++)
// store results to lightmap texture
for (int i = 0; i < sampleHeight; i++)
{
// get texture offset
int offs = (((textureWidth * (i + surface->lightmapOffs[1])) + surface->lightmapOffs[0]) * 3);
for (int j = 0; j < sampleWidth; j++)
{
// get texture offset
int offs = (((textureWidth * (i + surface->lightmapOffs[1])) + surface->lightmapOffs[0]) * 3);
// convert RGB to bytes
currentTexture[offs + j * 3 + 0] = floatToHalf(colorSamples[i * LIGHTMAP_MAX_SIZE + j].x);
currentTexture[offs + j * 3 + 1] = floatToHalf(colorSamples[i * LIGHTMAP_MAX_SIZE + j].y);
currentTexture[offs + j * 3 + 2] = floatToHalf(colorSamples[i * LIGHTMAP_MAX_SIZE + j].z);
// convert RGB to bytes
currentTexture[offs + j * 3 + 0] = floatToHalf(colorSamples[i * sampleWidth + j].x);
currentTexture[offs + j * 3 + 1] = floatToHalf(colorSamples[i * sampleWidth + j].y);
currentTexture[offs + j * 3 + 2] = floatToHalf(colorSamples[i * sampleWidth + j].z);
}
}
}
}
@ -563,15 +581,13 @@ static Vec3 ImportanceSampleGGX(Vec2 Xi, Vec3 N, float roughness)
void LightmapBuilder::TraceIndirectLight(Surface *surface)
{
if (surface->lightmapNum == -1)
return;
int sampleWidth = surface->lightmapDims[0];
int sampleHeight = surface->lightmapDims[1];
Vec3 normal = surface->plane.Normal();
uint16_t *currentTexture = &indirectoutput[surface->lightmapNum * textureWidth * textureHeight * 3];
surface->indirect.resize(sampleWidth * sampleHeight);
Vec3 *indirect = surface->indirect.data();
for (int i = 0; i < sampleHeight; i++)
{
@ -616,16 +632,14 @@ void LightmapBuilder::TraceIndirectLight(Surface *surface)
hit.hitSurface->lightmapCoords[hit.indices[1] * 2 + 1] * hit.b +
hit.hitSurface->lightmapCoords[hit.indices[2] * 2 + 1] * hit.c;
int hitTexelX = clamp((int)(u * textureWidth + 0.5f), 0, textureWidth - 1);
int hitTexelY = clamp((int)(v * textureHeight + 0.5f), 0, textureHeight - 1);
int hitTexelX = clamp((int)(u + 0.5f), 0, hit.hitSurface->lightmapDims[0] - 1);
int hitTexelY = clamp((int)(v + 0.5f), 0, hit.hitSurface->lightmapDims[1] - 1);
uint16_t *hitTexture = textures[hit.hitSurface->lightmapNum].data();
uint16_t *hitPixel = hitTexture + (hitTexelX + hitTexelY * textureWidth) * 3;
Vec3 *hitTexture = hit.hitSurface->samples.data();
const Vec3 &hitPixel = hitTexture[hitTexelX + hitTexelY * hit.hitSurface->lightmapDims[0]];
float attenuation = (1.0f - hit.fraction);
surfaceLight.x = halfToFloat(hitPixel[0]) * attenuation;
surfaceLight.y = halfToFloat(hitPixel[1]) * attenuation;
surfaceLight.z = halfToFloat(hitPixel[2]) * attenuation;
surfaceLight = hitPixel * attenuation;
}
c += surfaceLight * NdotL;
}
@ -635,13 +649,7 @@ void LightmapBuilder::TraceIndirectLight(Surface *surface)
c = c / totalWeight;
// convert RGB to bytes
int tx = j + surface->lightmapOffs[0];
int ty = i + surface->lightmapOffs[1];
uint16_t *pixel = currentTexture + (tx + ty * textureWidth) * 3;
pixel[0] = floatToHalf(c.x);
pixel[1] = floatToHalf(c.y);
pixel[2] = floatToHalf(c.z);
indirect[i * sampleWidth + j] = c;
}
}
}
@ -752,28 +760,23 @@ void LightmapBuilder::CreateLightmaps(FLevel &doomMap, int sampleDistance, int t
printf("Texels traced: %i \n\n", tracedTexels);
printf("------------- Tracing indirect -------------\n");
indirectoutput.resize(textures.size() * textureWidth * textureHeight * 3);
tracedTexels = 0;
processed = 0;
Worker::RunJob(mesh->surfaces.size(), [=](int id) {
LightIndirect(id);
});
for (size_t i = 0; i < textures.size(); i++)
if (LightBounce > 0)
{
uint16_t *tex = textures[i].data();
uint16_t *indirect = &indirectoutput[i * textureWidth * textureHeight * 3];
int count = textureWidth * textureHeight * 3;
for (int j = 0; j < count; j++)
{
tex[j] = floatToHalf(halfToFloat(tex[j]) + halfToFloat(indirect[j]));
}
printf("------------- Tracing indirect -------------\n");
tracedTexels = 0;
processed = 0;
Worker::RunJob(mesh->surfaces.size(), [=](int id) {
LightIndirect(id);
});
}
printf("Texels traced: %i \n\n", tracedTexels);
for (auto &surf : mesh->surfaces)
{
FinishSurface(surf.get());
}
}
void LightmapBuilder::SetupLightCellGrid()
@ -1012,9 +1015,10 @@ void LightmapBuilder::AddLightmapLump(FWadWriter &wadFile)
for (size_t i = 0; i < textures.size(); i++)
{
unsigned int count = (textureWidth * textureHeight) * 3;
uint16_t *pixels = textures[i]->Pixels();
for (unsigned int j = 0; j < count; j++)
{
lumpFile.Write16(textures[i][j]);
lumpFile.Write16(pixels[j]);
}
}

View file

@ -31,8 +31,6 @@
#include "framework/tarray.h"
#include <mutex>
#define LIGHTMAP_MAX_SIZE 1024
#define LIGHTCELL_SIZE 64
#define LIGHTCELL_BLOCK_SIZE 16
@ -55,6 +53,20 @@ public:
std::vector<LightCellBlock> blocks;
};
class LightmapTexture
{
public:
LightmapTexture(int width, int height)
{
mPixels.resize(width * height * 3);
}
uint16_t *Pixels() { return mPixels.data(); }
private:
std::vector<uint16_t> mPixels;
};
class LightmapBuilder
{
public:
@ -74,6 +86,7 @@ private:
void BuildSurfaceParams(Surface *surface);
void TraceSurface(Surface *surface);
void TraceIndirectLight(Surface *surface);
void FinishSurface(Surface *surface);
void SetupLightCellGrid();
void LightBlock(int blockid);
void LightSurface(const int surfid);
@ -88,8 +101,7 @@ private:
std::unique_ptr<LevelMesh> mesh;
std::vector<std::unique_ptr<SurfaceLight>> surfaceLights;
std::vector<std::vector<uint16_t>> textures;
std::vector<uint16_t> indirectoutput;
std::vector<std::unique_ptr<LightmapTexture>> textures;
std::vector<std::vector<int>> allocBlocks;
int numTextures = 0;
int extraSamples = 2;

View file

@ -61,6 +61,8 @@ struct Surface
int numVerts;
std::vector<Vec3> verts;
std::vector<float> lightmapCoords;
std::vector<Vec3> samples;
std::vector<Vec3> indirect;
SurfaceType type;
int typeIndex;
IntSector *controlSector;

View file

@ -8,8 +8,6 @@
extern int NumThreads;
thread_local Vec3 *colorSamples;
void Worker::RunJob(int count, std::function<void(int)> callback)
{
int numThreads = NumThreads;
@ -25,9 +23,6 @@ void Worker::RunJob(int count, std::function<void(int)> callback)
{
threads.push_back(std::thread([=]() {
std::vector<Vec3> samples(LIGHTMAP_MAX_SIZE * LIGHTMAP_MAX_SIZE);
colorSamples = samples.data();
int start = threadIndex * count / numThreads;
int end = std::min((threadIndex + 1) * count / numThreads, count);
for (int i = start; i < end; i++)

View file

@ -114,9 +114,10 @@ bool V5GLNodes = false;
bool HaveSSE1, HaveSSE2;
int SSELevel;
int NumThreads = 0;
int LMDims = LIGHTMAP_MAX_SIZE;
int LMDims = 1024;
int Samples = 8;
int Multisample = 1;
int LightBounce = 0;
// PRIVATE DATA DEFINITIONS ------------------------------------------------
@ -155,6 +156,7 @@ static option long_opts[] =
{"samples", required_argument, 0, 'Q'},
{"size", required_argument, 0, 'S'},
{"multisample", required_argument, 0, 'M'},
{"bounce", required_argument, 0, 'B'},
{0,0,0,0}
};
@ -442,7 +444,7 @@ static void ParseArgs(int argc, char **argv)
case 'S':
LMDims = atoi(optarg);
if (LMDims <= 0) LMDims = 1;
if (LMDims > LIGHTMAP_MAX_SIZE) LMDims = LIGHTMAP_MAX_SIZE;
if (LMDims > 1024) LMDims = 1024;
LMDims = Math::RoundPowerOfTwo(LMDims);
break;
case 'M':
@ -450,6 +452,11 @@ static void ParseArgs(int argc, char **argv)
if (Multisample <= 0) Multisample = 1;
if (Multisample > 64) Multisample = 64;
break;
case 'B':
LightBounce = atoi(optarg);
if (LightBounce < 0) LightBounce = 0;
if (LightBounce > 1) LightBounce = 1;
break;
case 1000:
ShowUsage();
exit(0);
@ -497,6 +504,7 @@ static void ShowUsage()
" -S, --size=NNN lightmap texture dimensions for width and height\n"
" must be in powers of two (1, 2, 4, 8, 16, etc)\n"
" -M, --multisample=NNN Number of samples to use per texel (default %d)\n"
" -B, --bounce=NNN Number of indirect light bounces (default %d, max 1)\n"
" -w, --warn Show warning messages\n"
#if HAVE_TIMING
" -t, --no-timing Suppress timing information\n"
@ -511,6 +519,7 @@ static void ShowUsage()
, AAPreference
, (int)std::thread::hardware_concurrency()
, Multisample
, LightBounce
);
}