- add indirect light pass

This commit is contained in:
Magnus Norddahl 2018-11-08 21:15:29 +01:00
parent ef3257e062
commit 63715b855d
7 changed files with 216 additions and 69 deletions

View file

@ -309,8 +309,11 @@ struct LevelTraceHit
{
kexVec3 start;
kexVec3 end;
surface_t *hitSurface;
float fraction;
surface_t *hitSurface;
int indices[3];
float b, c;
};
struct FLevel
@ -357,6 +360,7 @@ struct FLevel
// Dlight helpers
TArray<kexVec3> MeshVertices;
TArray<int> MeshUVIndex;
TArray<unsigned int> MeshElements;
TArray<int> MeshSurfaces;
std::unique_ptr<TriangleMeshShape> CollisionMesh;

View file

@ -358,6 +358,24 @@ LevelTraceHit FLevel::Trace(const kexVec3 &startVec, const kexVec3 &endVec)
trace.start = startVec;
trace.end = endVec;
trace.fraction = hit.fraction;
trace.hitSurface = (trace.fraction < 1.0f) ? surfaces[hit.surface].get() : nullptr;
if (trace.fraction < 1.0f)
{
int elementIdx = hit.triangle * 3;
trace.hitSurface = surfaces[MeshSurfaces[hit.triangle]].get();
trace.indices[0] = MeshUVIndex[MeshElements[elementIdx]];
trace.indices[1] = MeshUVIndex[MeshElements[elementIdx + 1]];
trace.indices[2] = MeshUVIndex[MeshElements[elementIdx + 2]];
trace.b = hit.b;
trace.c = hit.c;
}
else
{
trace.hitSurface = nullptr;
trace.indices[0] = 0;
trace.indices[1] = 0;
trace.indices[2] = 0;
trace.b = 0.0f;
trace.c = 0.0f;
}
return trace;
}

View file

@ -27,8 +27,8 @@
#include <immintrin.h>
#endif
TriangleMeshShape::TriangleMeshShape(const kexVec3 *vertices, int num_vertices, const unsigned int *elements, int num_elements, const int *surfaces)
: vertices(vertices), num_vertices(num_vertices), elements(elements), num_elements(num_elements), surfaces(surfaces)
TriangleMeshShape::TriangleMeshShape(const kexVec3 *vertices, int num_vertices, const unsigned int *elements, int num_elements)
: vertices(vertices), num_vertices(num_vertices), elements(elements), num_elements(num_elements)
{
int num_triangles = num_elements / 3;
if (num_triangles <= 0)
@ -94,8 +94,6 @@ TraceHit TriangleMeshShape::find_first_hit(TriangleMeshShape *shape, const kexVe
}
}
if (hit.surface != -1)
hit.surface = shape->surfaces[hit.surface / 3];
return hit;
}
@ -193,7 +191,8 @@ bool TriangleMeshShape::find_any_hit(TriangleMeshShape *shape, const RayBBox &ra
{
if (shape->is_leaf(a))
{
return intersect_triangle_ray(shape, ray, a) < 1.0f;
float baryB, baryC;
return intersect_triangle_ray(shape, ray, a, baryB, baryC) < 1.0f;
}
else
{
@ -212,11 +211,14 @@ void TriangleMeshShape::find_first_hit(TriangleMeshShape *shape, const RayBBox &
{
if (shape->is_leaf(a))
{
float t = intersect_triangle_ray(shape, ray, a);
float baryB, baryC;
float t = intersect_triangle_ray(shape, ray, a, baryB, baryC);
if (t < hit->fraction)
{
hit->fraction = t;
hit->surface = shape->nodes[a].element_index;
hit->triangle = shape->nodes[a].element_index / 3;
hit->b = baryB;
hit->c = baryC;
}
}
else
@ -232,7 +234,7 @@ bool TriangleMeshShape::overlap_bv_ray(TriangleMeshShape *shape, const RayBBox &
return IntersectionTest::ray_aabb(ray, shape->nodes[a].aabb) == IntersectionTest::overlap;
}
float TriangleMeshShape::intersect_triangle_ray(TriangleMeshShape *shape, const RayBBox &ray, int a)
float TriangleMeshShape::intersect_triangle_ray(TriangleMeshShape *shape, const RayBBox &ray, int a, float &barycentricB, float &barycentricC)
{
const int start_element = shape->nodes[a].element_index;
@ -243,49 +245,6 @@ float TriangleMeshShape::intersect_triangle_ray(TriangleMeshShape *shape, const
shape->vertices[shape->elements[start_element + 2]]
};
#if 1
kexVec3 e1 = p[1] - p[0];
kexVec3 e2 = p[2] - p[0];
kexVec3 triangleNormal = kexVec3::Cross(e1, e2);
float dist = kexVec3::Dot(p[0], triangleNormal);
float distA = kexVec3::Dot(ray.start, triangleNormal) - dist;
float distB = kexVec3::Dot(ray.end, triangleNormal) - dist;
if (distA * distB >= 0.0f)
return 1.0f;
// Backface check
//if (distA < 0.0f)
// return 1.0f;
float projLength = distA - distB;
float distance = (distA) / (projLength);
if (distance > 1.0f)
return 1.0f;
float edgeTolerance = triangleNormal.LengthSq() * -0.0001f;
kexVec3 point = ray.start * (1.0f - distance) + ray.end * distance;
kexVec3 v0p = p[0] - point;
kexVec3 v1p = p[1] - point;
kexVec3 cp0 = kexVec3::Cross(v0p, v1p);
if (kexVec3::Dot(cp0, triangleNormal) < edgeTolerance)
return 1.0f;
kexVec3 v2p = p[2] - point;
kexVec3 cp1 = kexVec3::Cross(v1p, v2p);
if (kexVec3::Dot(cp1, triangleNormal) < edgeTolerance)
return 1.0f;
kexVec3 cp2 = kexVec3::Cross(v2p, v0p);
if (kexVec3::Dot(cp2, triangleNormal) < edgeTolerance)
return 1.0f;
return distance;
#else
// MoellerTrumbore ray-triangle intersection algorithm:
kexVec3 D = ray.end - ray.start;
@ -329,12 +288,14 @@ float TriangleMeshShape::intersect_triangle_ray(TriangleMeshShape *shape, const
return 1.0f;
float t = kexVec3::Dot(e2, Q) * inv_det;
if (t > FLT_EPSILON)
return t;
// No hit, no win
if (t <= FLT_EPSILON)
return 1.0f;
#endif
// Return hit location on triangle in barycentric coordinates
barycentricB = u;
barycentricC = v;
return t;
}
bool TriangleMeshShape::sweep_overlap_bv_sphere(TriangleMeshShape *shape1, SphereShape *shape2, int a, const kexVec3 &target)

View file

@ -39,7 +39,9 @@ public:
struct TraceHit
{
float fraction = 1.0f;
int surface = -1;
int triangle = -1;
float b = 0.0f;
float c = 0.0f;
};
class CollisionBBox : public kexBBox
@ -80,7 +82,7 @@ public:
class TriangleMeshShape
{
public:
TriangleMeshShape(const kexVec3 *vertices, int num_vertices, const unsigned int *elements, int num_elements, const int *surfaces);
TriangleMeshShape(const kexVec3 *vertices, int num_vertices, const unsigned int *elements, int num_elements);
int get_min_depth() const;
int get_max_depth() const;
@ -113,7 +115,6 @@ public:
const int num_vertices = 0;
const unsigned int *elements = nullptr;
int num_elements = 0;
const int *surfaces = nullptr;
std::vector<Node> nodes;
int root = -1;
@ -128,7 +129,7 @@ private:
static void find_first_hit(TriangleMeshShape *shape1, const RayBBox &ray, int a, TraceHit *hit);
inline static bool overlap_bv_ray(TriangleMeshShape *shape, const RayBBox &ray, int a);
inline static float intersect_triangle_ray(TriangleMeshShape *shape, const RayBBox &ray, int a);
inline static float intersect_triangle_ray(TriangleMeshShape *shape, const RayBBox &ray, int a, float &barycentricB, float &barycentricC);
inline static bool sweep_overlap_bv_sphere(TriangleMeshShape *shape1, SphereShape *shape2, int a, const kexVec3 &target);
inline static float sweep_intersect_triangle_sphere(TriangleMeshShape *shape1, SphereShape *shape2, int a, const kexVec3 &target);

View file

@ -464,19 +464,18 @@ void kexLightmapBuilder::TraceSurface(surface_t *surface)
bShouldLookupTexture = true;
}
kexMath::Clamp(c, 0, 1);
colorSamples[i * LIGHTMAP_MAX_SIZE + j] = c;
}
}
// SVE redraws the scene for lightmaps, so for optimizations,
// tell the engine to ignore this surface if completely black
if (bShouldLookupTexture == false)
/*if (bShouldLookupTexture == false)
{
surface->lightmapNum = -1;
return;
}
else
else*/
{
int x = 0, y = 0;
int width = surface->lightmapDims[0];
@ -534,6 +533,126 @@ void kexLightmapBuilder::TraceSurface(surface_t *surface)
}
}
static float RadicalInverse_VdC(uint32_t bits)
{
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return float(bits) * 2.3283064365386963e-10f; // / 0x100000000
}
static kexVec2 Hammersley(uint32_t i, uint32_t N)
{
return kexVec2(float(i) / float(N), RadicalInverse_VdC(i));
}
static kexVec3 ImportanceSampleGGX(kexVec2 Xi, kexVec3 N, float roughness)
{
float a = roughness * roughness;
float phi = 2.0f * M_PI * Xi.x;
float cosTheta = sqrt((1.0f - Xi.y) / (1.0f + (a*a - 1.0f) * Xi.y));
float sinTheta = sqrt(1.0f - cosTheta * cosTheta);
// from spherical coordinates to cartesian coordinates
kexVec3 H(std::cos(phi) * sinTheta, std::sin(phi) * sinTheta, cosTheta);
// from tangent-space vector to world-space sample vector
kexVec3 up = std::abs(N.z) < 0.999f ? kexVec3(0.0f, 0.0f, 1.0f) : kexVec3(1.0f, 0.0f, 0.0f);
kexVec3 tangent = kexVec3::Normalize(kexVec3::Cross(up, N));
kexVec3 bitangent = kexVec3::Cross(N, tangent);
kexVec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z;
return kexVec3::Normalize(sampleVec);
}
void kexLightmapBuilder::TraceIndirectLight(surface_t *surface)
{
if (surface->lightmapNum == -1)
return;
int sampleWidth = surface->lightmapDims[0];
int sampleHeight = surface->lightmapDims[1];
kexVec3 normal = surface->plane.Normal();
uint16_t *currentTexture = &indirectoutput[surface->lightmapNum * textureWidth * textureHeight * 3];
for (int i = 0; i < sampleHeight; i++)
{
for (int j = 0; j < sampleWidth; j++)
{
kexVec3 pos = surface->lightmapOrigin + normal +
(surface->lightmapSteps[0] * (float)j) +
(surface->lightmapSteps[1] * (float)i);
const int SAMPLE_COUNT = 128;// 1024;
float totalWeight = 0.0f;
kexVec3 c(0.0f, 0.0f, 0.0f);
for (int i = 0; i < SAMPLE_COUNT; i++)
{
kexVec2 Xi = Hammersley(i, SAMPLE_COUNT);
kexVec3 H = ImportanceSampleGGX(Xi, normal, 1.0f);
kexVec3 L = kexVec3::Normalize(H * (2.0f * kexVec3::Dot(normal, H)) - normal);
float NdotL = std::max(kexVec3::Dot(normal, L), 0.0f);
if (NdotL > 0.0f)
{
tracedTexels++;
LevelTraceHit hit = map->Trace(pos, pos + L * 1000.0f);
if (hit.fraction < 1.0f)
{
kexVec3 surfaceLight;
if (hit.hitSurface->bSky)
{
surfaceLight = { 0.5f, 0.5f, 0.5f };
}
else
{
float u =
hit.hitSurface->lightmapCoords[hit.indices[0] * 2] * (1.0f - hit.b - hit.c) +
hit.hitSurface->lightmapCoords[hit.indices[1] * 2] * hit.b +
hit.hitSurface->lightmapCoords[hit.indices[2] * 2] * hit.c;
float v =
hit.hitSurface->lightmapCoords[hit.indices[0] * 2 + 1] * (1.0f - hit.b - hit.c) +
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);
uint16_t *hitTexture = textures[hit.hitSurface->lightmapNum].data();
uint16_t *hitPixel = hitTexture + (hitTexelX + hitTexelY * textureWidth) * 3;
float attenuation = (1.0f - hit.fraction);
surfaceLight.x = halfToFloat(hitPixel[0]) * attenuation;
surfaceLight.y = halfToFloat(hitPixel[1]) * attenuation;
surfaceLight.z = halfToFloat(hitPixel[2]) * attenuation;
}
c += surfaceLight * NdotL;
}
totalWeight += NdotL;
}
}
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);
}
}
}
void kexLightmapBuilder::LightSurface(const int surfid)
{
BuildSurfaceParams(surfaces[surfid].get());
@ -552,6 +671,21 @@ void kexLightmapBuilder::LightSurface(const int surfid)
}
}
void kexLightmapBuilder::LightIndirect(const int surfid)
{
TraceIndirectLight(surfaces[surfid].get());
int numsurfs = surfaces.size();
int lastproc = processed * 100 / numsurfs;
processed++;
int curproc = processed * 100 / numsurfs;
if (lastproc != curproc || processed == 1)
{
float remaining = (float)processed / (float)numsurfs;
printf("%i%c surfaces done\r", (int)(remaining * 100.0f), '%');
}
}
void kexLightmapBuilder::CreateLightmaps(FLevel &doomMap)
{
map = &doomMap;
@ -577,6 +711,29 @@ void kexLightmapBuilder::CreateLightmaps(FLevel &doomMap)
});
printf("Texels traced: %i \n\n", tracedTexels);
printf("------------- Tracing indirect -------------\n");
indirectoutput.resize(textures.size() * textureWidth * textureHeight * 3);
tracedTexels = 0;
processed = 0;
kexWorker::RunJob(surfaces.size(), [=](int id) {
LightIndirect(id);
});
for (size_t i = 0; i < textures.size(); i++)
{
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("Texels traced: %i \n\n", tracedTexels);
}
void kexLightmapBuilder::SetupLightCellGrid()
@ -585,8 +742,8 @@ void kexLightmapBuilder::SetupLightCellGrid()
float blockWorldSize = LIGHTCELL_BLOCK_SIZE * LIGHTCELL_SIZE;
grid.x = static_cast<int>(std::floor(worldBBox.min.x / blockWorldSize));
grid.y = static_cast<int>(std::floor(worldBBox.min.y / blockWorldSize));
grid.width = static_cast<int>(std::ceil(worldBBox.max.x / blockWorldSize + 1.0f) - 1.0f) - grid.x;
grid.height = static_cast<int>(std::ceil(worldBBox.max.y / blockWorldSize + 1.0f) - 1.0f) - grid.y;
grid.width = static_cast<int>(std::ceil(worldBBox.max.x / blockWorldSize)) - grid.x;
grid.height = static_cast<int>(std::ceil(worldBBox.max.y / blockWorldSize)) - grid.y;
grid.blocks.resize(grid.width * grid.height);
}
@ -641,7 +798,7 @@ void kexLightmapBuilder::LightBlock(int id)
{
// Allocate space for the cells
block.z = static_cast<int>(std::floor(minFloor / LIGHTCELL_SIZE));
block.layers = static_cast<int>(std::ceil(maxCeiling / LIGHTCELL_SIZE + 1.0f) - 1.0f) - block.z;
block.layers = static_cast<int>(std::ceil(maxCeiling / LIGHTCELL_SIZE)) - block.z;
block.cells.Resize(LIGHTCELL_BLOCK_SIZE * LIGHTCELL_BLOCK_SIZE * block.layers);
// Ray trace the cells

View file

@ -79,12 +79,15 @@ private:
void BuildSurfaceParams(surface_t *surface);
void TraceSurface(surface_t *surface);
void TraceIndirectLight(surface_t *surface);
void SetupLightCellGrid();
void LightBlock(int blockid);
void LightSurface(const int surfid);
void LightIndirect(const int surfid);
FLevel *map;
std::vector<std::vector<uint16_t>> textures;
std::vector<uint16_t> indirectoutput;
std::vector<std::vector<int>> allocBlocks;
int numTextures = 0;
int extraSamples = 2;

View file

@ -397,7 +397,10 @@ void CreateSurfaces(FLevel &doomMap)
unsigned int pos = doomMap.MeshVertices.Size();
for (int j = 0; j < numVerts; j++)
{
doomMap.MeshVertices.Push(s->verts[j]);
doomMap.MeshUVIndex.Push(j);
}
if (s->type == ST_FLOOR || s->type == ST_CEILING)
{
@ -431,5 +434,5 @@ void CreateSurfaces(FLevel &doomMap)
}
}
doomMap.CollisionMesh = std::make_unique<TriangleMeshShape>(&doomMap.MeshVertices[0], doomMap.MeshVertices.Size(), &doomMap.MeshElements[0], doomMap.MeshElements.Size(), &doomMap.MeshSurfaces[0]);
doomMap.CollisionMesh = std::make_unique<TriangleMeshShape>(&doomMap.MeshVertices[0], doomMap.MeshVertices.Size(), &doomMap.MeshElements[0], doomMap.MeshElements.Size());
}