- 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 #endif
extern int Multisample; extern int Multisample;
extern thread_local Vec3 *colorSamples; extern int LightBounce;
LightmapBuilder::LightmapBuilder() LightmapBuilder::LightmapBuilder()
{ {
@ -59,7 +59,7 @@ void LightmapBuilder::NewTexture()
numTextures++; numTextures++;
allocBlocks.push_back(std::vector<int>(textureWidth)); 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 // 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. // 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) void LightmapBuilder::TraceSurface(Surface *surface)
{ {
int sampleWidth; int sampleWidth = surface->lightmapDims[0];
int sampleHeight; int sampleHeight = surface->lightmapDims[1];
Vec3 normal;
Vec3 pos;
Vec3 tDelta;
int i;
int j;
uint16_t *currentTexture;
bool bShouldLookupTexture = false;
sampleWidth = surface->lightmapDims[0]; Vec3 normal = surface->plane.Normal();
sampleHeight = surface->lightmapDims[1];
normal = surface->plane.Normal();
int multisampleCount = Multisample; int multisampleCount = Multisample;
surface->samples.resize(sampleWidth * sampleHeight);
Vec3 *colorSamples = surface->samples.data();
// start walking through each texel // 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); Vec3 c(0.0f, 0.0f, 0.0f);
@ -440,35 +432,65 @@ void LightmapBuilder::TraceSurface(Surface *surface)
// convert the texel into world-space coordinates. // convert the texel into world-space coordinates.
// this will be the origin in which a line will be traced from // this will be the origin in which a line will be traced from
pos = surface->lightmapOrigin + normal + Vec3 pos = surface->lightmapOrigin + normal + (surface->lightmapSteps[0] * multisamplePos.x) + (surface->lightmapSteps[1] * multisamplePos.y);
(surface->lightmapSteps[0] * multisamplePos.x) +
(surface->lightmapSteps[1] * multisamplePos.y);
c += LightTexelSample(pos, surface); c += LightTexelSample(pos, surface);
} }
c /= multisampleCount; c /= multisampleCount;
// if nothing at all was traced and color is completely black colorSamples[i * sampleWidth + j] = c;
// 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 * 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, // SVE redraws the scene for lightmaps, so for optimizations,
// tell the engine to ignore this surface if completely black // 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; surface->lightmapNum = -1;
return;
} }
else*/ else
{ {
int x = 0, y = 0; int x = 0, y = 0;
int width = surface->lightmapDims[0]; int width = surface->lightmapDims[0];
@ -490,38 +512,34 @@ void LightmapBuilder::TraceSurface(Surface *surface)
} }
} }
uint16_t *currentTexture = textures[surface->lightmapNum]->Pixels();
lock.unlock(); lock.unlock();
// calculate texture coordinates // 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; Vec3 tDelta = surface->verts[i] - surface->bounds.min;
surface->lightmapCoords[i * 2 + 0] = surface->lightmapCoords[i * 2 + 0] = (tDelta.Dot(surface->textureCoords[0]) + x + 0.5f) / (float)textureWidth;
(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->lightmapCoords[i * 2 + 1] =
(tDelta.Dot(surface->textureCoords[1]) + y + 0.5f) / (float)textureHeight;
} }
surface->lightmapOffs[0] = x; surface->lightmapOffs[0] = x;
surface->lightmapOffs[1] = y; surface->lightmapOffs[1] = y;
}
std::unique_lock<std::mutex> lock(mutex); // store results to lightmap texture
currentTexture = textures[surface->lightmapNum].data(); for (int i = 0; i < sampleHeight; i++)
lock.unlock();
// store results to lightmap texture
for (i = 0; i < sampleHeight; i++)
{
for (j = 0; j < sampleWidth; j++)
{ {
// get texture offset for (int j = 0; j < sampleWidth; j++)
int offs = (((textureWidth * (i + surface->lightmapOffs[1])) + surface->lightmapOffs[0]) * 3); {
// get texture offset
int offs = (((textureWidth * (i + surface->lightmapOffs[1])) + surface->lightmapOffs[0]) * 3);
// convert RGB to bytes // convert RGB to bytes
currentTexture[offs + j * 3 + 0] = floatToHalf(colorSamples[i * LIGHTMAP_MAX_SIZE + j].x); currentTexture[offs + j * 3 + 0] = floatToHalf(colorSamples[i * sampleWidth + j].x);
currentTexture[offs + j * 3 + 1] = floatToHalf(colorSamples[i * LIGHTMAP_MAX_SIZE + j].y); currentTexture[offs + j * 3 + 1] = floatToHalf(colorSamples[i * sampleWidth + j].y);
currentTexture[offs + j * 3 + 2] = floatToHalf(colorSamples[i * LIGHTMAP_MAX_SIZE + j].z); 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) void LightmapBuilder::TraceIndirectLight(Surface *surface)
{ {
if (surface->lightmapNum == -1)
return;
int sampleWidth = surface->lightmapDims[0]; int sampleWidth = surface->lightmapDims[0];
int sampleHeight = surface->lightmapDims[1]; int sampleHeight = surface->lightmapDims[1];
Vec3 normal = surface->plane.Normal(); 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++) 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[1] * 2 + 1] * hit.b +
hit.hitSurface->lightmapCoords[hit.indices[2] * 2 + 1] * hit.c; hit.hitSurface->lightmapCoords[hit.indices[2] * 2 + 1] * hit.c;
int hitTexelX = clamp((int)(u * textureWidth + 0.5f), 0, textureWidth - 1); int hitTexelX = clamp((int)(u + 0.5f), 0, hit.hitSurface->lightmapDims[0] - 1);
int hitTexelY = clamp((int)(v * textureHeight + 0.5f), 0, textureHeight - 1); int hitTexelY = clamp((int)(v + 0.5f), 0, hit.hitSurface->lightmapDims[1] - 1);
uint16_t *hitTexture = textures[hit.hitSurface->lightmapNum].data(); Vec3 *hitTexture = hit.hitSurface->samples.data();
uint16_t *hitPixel = hitTexture + (hitTexelX + hitTexelY * textureWidth) * 3; const Vec3 &hitPixel = hitTexture[hitTexelX + hitTexelY * hit.hitSurface->lightmapDims[0]];
float attenuation = (1.0f - hit.fraction); float attenuation = (1.0f - hit.fraction);
surfaceLight.x = halfToFloat(hitPixel[0]) * attenuation; surfaceLight = hitPixel * attenuation;
surfaceLight.y = halfToFloat(hitPixel[1]) * attenuation;
surfaceLight.z = halfToFloat(hitPixel[2]) * attenuation;
} }
c += surfaceLight * NdotL; c += surfaceLight * NdotL;
} }
@ -635,13 +649,7 @@ void LightmapBuilder::TraceIndirectLight(Surface *surface)
c = c / totalWeight; c = c / totalWeight;
// convert RGB to bytes indirect[i * sampleWidth + j] = c;
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);
} }
} }
} }
@ -752,28 +760,23 @@ void LightmapBuilder::CreateLightmaps(FLevel &doomMap, int sampleDistance, int t
printf("Texels traced: %i \n\n", tracedTexels); printf("Texels traced: %i \n\n", tracedTexels);
printf("------------- Tracing indirect -------------\n"); if (LightBounce > 0)
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++)
{ {
uint16_t *tex = textures[i].data(); printf("------------- Tracing indirect -------------\n");
uint16_t *indirect = &indirectoutput[i * textureWidth * textureHeight * 3];
int count = textureWidth * textureHeight * 3; tracedTexels = 0;
for (int j = 0; j < count; j++) processed = 0;
{ Worker::RunJob(mesh->surfaces.size(), [=](int id) {
tex[j] = floatToHalf(halfToFloat(tex[j]) + halfToFloat(indirect[j])); LightIndirect(id);
} });
} }
printf("Texels traced: %i \n\n", tracedTexels); printf("Texels traced: %i \n\n", tracedTexels);
for (auto &surf : mesh->surfaces)
{
FinishSurface(surf.get());
}
} }
void LightmapBuilder::SetupLightCellGrid() void LightmapBuilder::SetupLightCellGrid()
@ -1012,9 +1015,10 @@ void LightmapBuilder::AddLightmapLump(FWadWriter &wadFile)
for (size_t i = 0; i < textures.size(); i++) for (size_t i = 0; i < textures.size(); i++)
{ {
unsigned int count = (textureWidth * textureHeight) * 3; unsigned int count = (textureWidth * textureHeight) * 3;
uint16_t *pixels = textures[i]->Pixels();
for (unsigned int j = 0; j < count; j++) 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 "framework/tarray.h"
#include <mutex> #include <mutex>
#define LIGHTMAP_MAX_SIZE 1024
#define LIGHTCELL_SIZE 64 #define LIGHTCELL_SIZE 64
#define LIGHTCELL_BLOCK_SIZE 16 #define LIGHTCELL_BLOCK_SIZE 16
@ -55,6 +53,20 @@ public:
std::vector<LightCellBlock> blocks; 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 class LightmapBuilder
{ {
public: public:
@ -74,6 +86,7 @@ private:
void BuildSurfaceParams(Surface *surface); void BuildSurfaceParams(Surface *surface);
void TraceSurface(Surface *surface); void TraceSurface(Surface *surface);
void TraceIndirectLight(Surface *surface); void TraceIndirectLight(Surface *surface);
void FinishSurface(Surface *surface);
void SetupLightCellGrid(); void SetupLightCellGrid();
void LightBlock(int blockid); void LightBlock(int blockid);
void LightSurface(const int surfid); void LightSurface(const int surfid);
@ -88,8 +101,7 @@ private:
std::unique_ptr<LevelMesh> mesh; std::unique_ptr<LevelMesh> mesh;
std::vector<std::unique_ptr<SurfaceLight>> surfaceLights; std::vector<std::unique_ptr<SurfaceLight>> surfaceLights;
std::vector<std::vector<uint16_t>> textures; std::vector<std::unique_ptr<LightmapTexture>> textures;
std::vector<uint16_t> indirectoutput;
std::vector<std::vector<int>> allocBlocks; std::vector<std::vector<int>> allocBlocks;
int numTextures = 0; int numTextures = 0;
int extraSamples = 2; int extraSamples = 2;

View file

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

View file

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

View file

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