mirror of
https://github.com/ZDoom/ZDRay.git
synced 2024-11-10 06:41:37 +00:00
Rewrite the CPU ray tracer
This commit is contained in:
parent
96b31d8a69
commit
cd3563759c
10 changed files with 593 additions and 394 deletions
|
@ -155,7 +155,6 @@ set( SOURCES
|
||||||
src/nodebuilder/nodebuild_utility.cpp
|
src/nodebuilder/nodebuild_utility.cpp
|
||||||
src/nodebuilder/nodebuild_classify_nosse2.cpp
|
src/nodebuilder/nodebuild_classify_nosse2.cpp
|
||||||
src/lightmap/pngwriter.cpp
|
src/lightmap/pngwriter.cpp
|
||||||
src/lightmap/raytracer.cpp
|
|
||||||
src/lightmap/surfaces.cpp
|
src/lightmap/surfaces.cpp
|
||||||
src/lightmap/worker.cpp
|
src/lightmap/worker.cpp
|
||||||
src/lightmap/collision.cpp
|
src/lightmap/collision.cpp
|
||||||
|
@ -178,6 +177,8 @@ set( SOURCES
|
||||||
src/lightmap/glsl_rmiss_bounce.h
|
src/lightmap/glsl_rmiss_bounce.h
|
||||||
src/lightmap/glsl_rmiss_light.h
|
src/lightmap/glsl_rmiss_light.h
|
||||||
src/lightmap/glsl_rmiss_sun.h
|
src/lightmap/glsl_rmiss_sun.h
|
||||||
|
src/lightmap/cpuraytracer.cpp
|
||||||
|
src/lightmap/cpuraytracer.h
|
||||||
src/math/angle.cpp
|
src/math/angle.cpp
|
||||||
src/math/bounds.cpp
|
src/math/bounds.cpp
|
||||||
src/math/mathlib.cpp
|
src/math/mathlib.cpp
|
||||||
|
@ -216,7 +217,6 @@ set( HEADERS
|
||||||
src/framework/halffloat.h
|
src/framework/halffloat.h
|
||||||
src/framework/binfile.h
|
src/framework/binfile.h
|
||||||
src/lightmap/pngwriter.h
|
src/lightmap/pngwriter.h
|
||||||
src/lightmap/raytracer.h
|
|
||||||
src/lightmap/surfaces.h
|
src/lightmap/surfaces.h
|
||||||
src/lightmap/worker.h
|
src/lightmap/worker.h
|
||||||
src/lightmap/collision.h
|
src/lightmap/collision.h
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "level/level.h"
|
#include "level/level.h"
|
||||||
#include "lightmap/raytracer.h"
|
#include "lightmap/cpuraytracer.h"
|
||||||
#include "lightmap/gpuraytracer.h"
|
#include "lightmap/gpuraytracer.h"
|
||||||
//#include "rejectbuilder.h"
|
//#include "rejectbuilder.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -697,7 +697,7 @@ void FProcessor::BuildLightmaps()
|
||||||
|
|
||||||
if (CPURaytrace)
|
if (CPURaytrace)
|
||||||
{
|
{
|
||||||
Raytracer raytracer;
|
CPURaytracer raytracer;
|
||||||
raytracer.Raytrace(LightmapMesh.get());
|
raytracer.Raytrace(LightmapMesh.get());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
483
src/lightmap/cpuraytracer.cpp
Normal file
483
src/lightmap/cpuraytracer.cpp
Normal file
|
@ -0,0 +1,483 @@
|
||||||
|
|
||||||
|
#include "math/mathlib.h"
|
||||||
|
#include "surfaces.h"
|
||||||
|
#include "level/level.h"
|
||||||
|
#include "cpuraytracer.h"
|
||||||
|
#include "worker.h"
|
||||||
|
#include "framework/binfile.h"
|
||||||
|
#include "framework/templates.h"
|
||||||
|
#include "framework/halffloat.h"
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
|
extern int LightBounce;
|
||||||
|
extern bool VKDebug;
|
||||||
|
|
||||||
|
CPURaytracer::CPURaytracer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CPURaytracer::~CPURaytracer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPURaytracer::Raytrace(LevelMesh* level)
|
||||||
|
{
|
||||||
|
mesh = level;
|
||||||
|
|
||||||
|
std::vector<CPUTraceTask> tasks;
|
||||||
|
for (size_t i = 0; i < mesh->lightProbes.size(); i++)
|
||||||
|
{
|
||||||
|
CPUTraceTask task;
|
||||||
|
task.id = -(int)(i + 2);
|
||||||
|
task.x = 0;
|
||||||
|
task.y = 0;
|
||||||
|
tasks.push_back(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < mesh->surfaces.size(); i++)
|
||||||
|
{
|
||||||
|
Surface* surface = mesh->surfaces[i].get();
|
||||||
|
int sampleWidth = surface->lightmapDims[0];
|
||||||
|
int sampleHeight = surface->lightmapDims[1];
|
||||||
|
for (int y = 0; y < sampleHeight; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < sampleWidth; x++)
|
||||||
|
{
|
||||||
|
CPUTraceTask task;
|
||||||
|
task.id = (int)i;
|
||||||
|
task.x = x;
|
||||||
|
task.y = y;
|
||||||
|
tasks.push_back(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CollisionMesh = std::make_unique<TriangleMeshShape>(mesh->MeshVertices.Data(), mesh->MeshVertices.Size(), mesh->MeshElements.Data(), mesh->MeshElements.Size());
|
||||||
|
CreateHemisphereVectors();
|
||||||
|
CreateLights();
|
||||||
|
|
||||||
|
printf("Ray tracing with %d bounce(s)\n", LightBounce);
|
||||||
|
|
||||||
|
Worker::RunJob((int)tasks.size(), [=](int id) { RaytraceTask(tasks[id]); });
|
||||||
|
|
||||||
|
printf("\nRaytrace complete\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPURaytracer::RaytraceTask(const CPUTraceTask& task)
|
||||||
|
{
|
||||||
|
CPUTraceState state;
|
||||||
|
state.EndTrace = false;
|
||||||
|
|
||||||
|
if (task.id >= 0)
|
||||||
|
{
|
||||||
|
Surface* surface = mesh->surfaces[task.id].get();
|
||||||
|
Vec3 pos = surface->lightmapOrigin + surface->lightmapSteps[0] * (float)task.x + surface->lightmapSteps[1] * (float)task.y;
|
||||||
|
state.StartPosition = pos;
|
||||||
|
state.StartSurface = surface;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LightProbeSample& probe = mesh->lightProbes[(size_t)(-task.id) - 2];
|
||||||
|
state.StartPosition = probe.Position;
|
||||||
|
state.StartSurface = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.SampleDistance = (float)mesh->samples;
|
||||||
|
state.LightCount = mesh->map->ThingLights.Size();
|
||||||
|
state.SunDir = mesh->map->GetSunDirection();
|
||||||
|
state.SunColor = mesh->map->GetSunColor();
|
||||||
|
state.SunIntensity = 1.0f;
|
||||||
|
|
||||||
|
state.PassType = 0;
|
||||||
|
state.SampleIndex = 0;
|
||||||
|
state.SampleCount = bounceSampleCount;
|
||||||
|
RunBounceTrace(state);
|
||||||
|
|
||||||
|
state.SampleCount = coverageSampleCount;
|
||||||
|
RunLightTrace(state);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < (uint32_t)bounceSampleCount && !state.EndTrace; i++)
|
||||||
|
{
|
||||||
|
state.PassType = 1;
|
||||||
|
state.SampleIndex = i;
|
||||||
|
state.SampleCount = bounceSampleCount;
|
||||||
|
state.HemisphereVec = HemisphereVectors[state.SampleIndex];
|
||||||
|
RunBounceTrace(state);
|
||||||
|
|
||||||
|
for (int bounce = 0; bounce < LightBounce && !state.EndTrace; bounce++)
|
||||||
|
{
|
||||||
|
state.SampleCount = coverageSampleCount;
|
||||||
|
RunLightTrace(state);
|
||||||
|
|
||||||
|
state.PassType = 2;
|
||||||
|
state.SampleIndex = (i + bounce) % state.SampleCount;
|
||||||
|
state.SampleCount = bounceSampleCount;
|
||||||
|
state.HemisphereVec = HemisphereVectors[state.SampleIndex];
|
||||||
|
RunBounceTrace(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task.id >= 0)
|
||||||
|
{
|
||||||
|
Surface* surface = mesh->surfaces[task.id].get();
|
||||||
|
size_t sampleWidth = surface->lightmapDims[0];
|
||||||
|
surface->samples[task.x + task.y * sampleWidth] = state.Output;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LightProbeSample& probe = mesh->lightProbes[(size_t)(-task.id) - 2];
|
||||||
|
probe.Color = state.Output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPURaytracer::RunBounceTrace(CPUTraceState& state)
|
||||||
|
{
|
||||||
|
Vec3 origin;
|
||||||
|
Surface* surface;
|
||||||
|
if (state.PassType == 2)
|
||||||
|
{
|
||||||
|
origin = state.Position;
|
||||||
|
surface = state.Surface;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
origin = state.StartPosition;
|
||||||
|
surface = state.StartSurface;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3 incoming(0.0f, 0.0f, 0.0f);
|
||||||
|
float incomingAttenuation = 1.0f;
|
||||||
|
|
||||||
|
if (state.PassType == 0)
|
||||||
|
{
|
||||||
|
if (surface)
|
||||||
|
{
|
||||||
|
CPUEmissiveSurface emissive = GetEmissive(surface);
|
||||||
|
incoming = emissive.Color * emissive.Intensity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
incoming = state.Output;
|
||||||
|
incomingAttenuation = state.OutputAttenuation;
|
||||||
|
|
||||||
|
if (state.PassType == 1)
|
||||||
|
incomingAttenuation = 1.0f / float(state.SampleCount);
|
||||||
|
|
||||||
|
Vec3 normal;
|
||||||
|
if (surface)
|
||||||
|
{
|
||||||
|
normal = surface->plane.Normal();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (state.SampleIndex % 6)
|
||||||
|
{
|
||||||
|
case 0: normal = Vec3( 1.0f, 0.0f, 0.0f); break;
|
||||||
|
case 1: normal = Vec3(-1.0f, 0.0f, 0.0f); break;
|
||||||
|
case 2: normal = Vec3( 0.0f, 1.0f, 0.0f); break;
|
||||||
|
case 3: normal = Vec3( 0.0f, -1.0f, 0.0f); break;
|
||||||
|
case 4: normal = Vec3( 0.0f, 0.0f, 1.0f); break;
|
||||||
|
case 5: normal = Vec3( 0.0f, 0.0f, -1.0f); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3 H = ImportanceSample(state.HemisphereVec, normal);
|
||||||
|
Vec3 L = Vec3::Normalize(H * (2.0f * Vec3::Dot(normal, H)) - normal);
|
||||||
|
|
||||||
|
float NdotL = std::max(Vec3::Dot(normal, L), 0.0f);
|
||||||
|
|
||||||
|
const float p = (float)(1 / (2 * 3.14159265359));
|
||||||
|
incomingAttenuation *= NdotL / p;
|
||||||
|
|
||||||
|
state.EndTrace = true;
|
||||||
|
if (NdotL > 0.0f)
|
||||||
|
{
|
||||||
|
Vec3 start = origin + normal * 0.1f;
|
||||||
|
Vec3 end = start + L * 32768.0f;
|
||||||
|
LevelTraceHit hit = Trace(start, end);
|
||||||
|
if (hit.fraction < 1.0f)
|
||||||
|
{
|
||||||
|
state.EndTrace = false;
|
||||||
|
surface = hit.hitSurface;
|
||||||
|
Vec3 hitPosition = start * (1.0f - hit.fraction) + end * hit.fraction;
|
||||||
|
|
||||||
|
CPUEmissiveSurface emissive = GetEmissive(surface);
|
||||||
|
if (emissive.Distance > 0.0f)
|
||||||
|
{
|
||||||
|
float hitDistance = (hitPosition - origin).Length();
|
||||||
|
float attenuation = std::max(1.0f - (hitDistance / emissive.Distance), 0.0f);
|
||||||
|
incoming += emissive.Color * (emissive.Intensity * attenuation * incomingAttenuation);
|
||||||
|
}
|
||||||
|
|
||||||
|
origin = hitPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
incomingAttenuation *= 0.25; // the amount of incoming light the surfaces emit
|
||||||
|
}
|
||||||
|
|
||||||
|
state.Position = origin;
|
||||||
|
state.Surface = surface;
|
||||||
|
state.Output = incoming;
|
||||||
|
state.OutputAttenuation = incomingAttenuation;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPURaytracer::RunLightTrace(CPUTraceState& state)
|
||||||
|
{
|
||||||
|
Vec3 incoming = state.Output;
|
||||||
|
float incomingAttenuation = state.OutputAttenuation;
|
||||||
|
if (incomingAttenuation <= 0.0f)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Surface* surface = state.Surface;
|
||||||
|
|
||||||
|
Vec3 origin = state.Position;
|
||||||
|
Vec3 normal;
|
||||||
|
if (surface)
|
||||||
|
{
|
||||||
|
normal = surface->plane.Normal();
|
||||||
|
origin += normal * 0.1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float minDistance = 0.01f;
|
||||||
|
|
||||||
|
// Sun light
|
||||||
|
{
|
||||||
|
const float dist = 32768.0f;
|
||||||
|
|
||||||
|
float attenuation = 0.0f;
|
||||||
|
if (state.PassType == 0 && surface)
|
||||||
|
{
|
||||||
|
Vec3 e0 = Vec3::Cross(normal, std::abs(normal.x) < std::abs(normal.y) ? Vec3(1.0f, 0.0f, 0.0f) : Vec3(0.0f, 1.0f, 0.0f));
|
||||||
|
Vec3 e1 = Vec3::Cross(normal, e0);
|
||||||
|
e0 = Vec3::Cross(normal, e1);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < state.SampleCount; i++)
|
||||||
|
{
|
||||||
|
Vec2 offset = (Hammersley(i, state.SampleCount) - 0.5) * state.SampleDistance;
|
||||||
|
Vec3 origin2 = origin + e0 * offset.x + e1 * offset.y;
|
||||||
|
|
||||||
|
Vec3 start = origin2;
|
||||||
|
Vec3 end = start + state.SunDir * dist;
|
||||||
|
LevelTraceHit hit = Trace(start, end);
|
||||||
|
if (hit.fraction < 1.0f && hit.hitSurface->bSky)
|
||||||
|
attenuation += 1.0f;
|
||||||
|
}
|
||||||
|
attenuation *= 1.0f / float(state.SampleCount);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Vec3 start = origin;
|
||||||
|
Vec3 end = start + state.SunDir * dist;
|
||||||
|
LevelTraceHit hit = Trace(start, end);
|
||||||
|
attenuation = (hit.fraction < 1.0f && hit.hitSurface->bSky) ? 1.0f : 0.0f;
|
||||||
|
}
|
||||||
|
incoming += state.SunColor * (attenuation * state.SunIntensity * incomingAttenuation);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t j = 0; j < state.LightCount; j++)
|
||||||
|
{
|
||||||
|
const CPULightInfo& light = Lights.data()[j]; // MSVC vector operator[] is very slow
|
||||||
|
|
||||||
|
float dist = (light.Origin - origin).Length();
|
||||||
|
if (dist > minDistance && dist < light.Radius)
|
||||||
|
{
|
||||||
|
Vec3 dir = Vec3::Normalize(light.Origin - origin);
|
||||||
|
|
||||||
|
float distAttenuation = std::max(1.0f - (dist / light.Radius), 0.0f);
|
||||||
|
float angleAttenuation = 1.0f;
|
||||||
|
if (surface)
|
||||||
|
{
|
||||||
|
angleAttenuation = std::max(Vec3::Dot(normal, dir), 0.0f);
|
||||||
|
}
|
||||||
|
float spotAttenuation = 1.0f;
|
||||||
|
if (light.OuterAngleCos > -1.0f)
|
||||||
|
{
|
||||||
|
float cosDir = Vec3::Dot(dir, light.SpotDir);
|
||||||
|
spotAttenuation = smoothstep(light.OuterAngleCos, light.InnerAngleCos, cosDir);
|
||||||
|
spotAttenuation = std::max(spotAttenuation, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
float attenuation = distAttenuation * angleAttenuation * spotAttenuation;
|
||||||
|
if (attenuation > 0.0f)
|
||||||
|
{
|
||||||
|
float shadowAttenuation = 0.0f;
|
||||||
|
|
||||||
|
if (state.PassType == 0 && surface)
|
||||||
|
{
|
||||||
|
Vec3 e0 = Vec3::Cross(normal, std::abs(normal.x) < std::abs(normal.y) ? Vec3(1.0f, 0.0f, 0.0f) : Vec3(0.0f, 1.0f, 0.0f));
|
||||||
|
Vec3 e1 = Vec3::Cross(normal, e0);
|
||||||
|
e0 = Vec3::Cross(normal, e1);
|
||||||
|
for (uint32_t i = 0; i < state.SampleCount; i++)
|
||||||
|
{
|
||||||
|
Vec2 offset = (Hammersley(i, state.SampleCount) - 0.5) * state.SampleDistance;
|
||||||
|
Vec3 origin2 = origin + e0 * offset.x + e1 * offset.y;
|
||||||
|
|
||||||
|
LevelTraceHit hit = Trace(origin2, light.Origin);
|
||||||
|
if (hit.fraction == 1.0f)
|
||||||
|
shadowAttenuation += 1.0f;
|
||||||
|
}
|
||||||
|
shadowAttenuation *= 1.0f / float(state.SampleCount);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LevelTraceHit hit = Trace(origin, light.Origin);
|
||||||
|
shadowAttenuation = (hit.fraction == 1.0f) ? 1.0f : 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
attenuation *= shadowAttenuation;
|
||||||
|
|
||||||
|
incoming += light.Color * (attenuation * light.Intensity * incomingAttenuation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state.Output = incoming;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3 CPURaytracer::ImportanceSample(const Vec3& HemisphereVec, Vec3 N)
|
||||||
|
{
|
||||||
|
// from tangent-space vector to world-space sample vector
|
||||||
|
Vec3 up = std::abs(N.x) < std::abs(N.y) ? Vec3(1.0f, 0.0f, 0.0f) : Vec3(0.0f, 1.0f, 0.0f);
|
||||||
|
Vec3 tangent = Vec3::Normalize(Vec3::Cross(up, N));
|
||||||
|
Vec3 bitangent = Vec3::Cross(N, tangent);
|
||||||
|
|
||||||
|
Vec3 sampleVec = tangent * HemisphereVec.x + bitangent * HemisphereVec.y + N * HemisphereVec.z;
|
||||||
|
return Vec3::Normalize(sampleVec);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2 CPURaytracer::Hammersley(uint32_t i, uint32_t N)
|
||||||
|
{
|
||||||
|
return Vec2(float(i) / float(N), RadicalInverse_VdC(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
float CPURaytracer::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
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPURaytracer::CreateHemisphereVectors()
|
||||||
|
{
|
||||||
|
if (HemisphereVectors.empty())
|
||||||
|
{
|
||||||
|
HemisphereVectors.reserve(bounceSampleCount);
|
||||||
|
for (int i = 0; i < bounceSampleCount; i++)
|
||||||
|
{
|
||||||
|
Vec2 Xi = Hammersley(i, bounceSampleCount);
|
||||||
|
Vec3 H;
|
||||||
|
H.x = Xi.x * 2.0f - 1.0f;
|
||||||
|
H.y = Xi.y * 2.0f - 1.0f;
|
||||||
|
H.z = RadicalInverse_VdC(i) + 0.01f;
|
||||||
|
H.Normalize();
|
||||||
|
HemisphereVectors.push_back(H);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPURaytracer::CreateLights()
|
||||||
|
{
|
||||||
|
Lights.clear();
|
||||||
|
for (ThingLight& light : mesh->map->ThingLights)
|
||||||
|
{
|
||||||
|
CPULightInfo info;
|
||||||
|
info.Origin = light.LightOrigin();
|
||||||
|
info.Radius = light.LightRadius();
|
||||||
|
info.Intensity = light.intensity;
|
||||||
|
info.InnerAngleCos = light.innerAngleCos;
|
||||||
|
info.OuterAngleCos = light.outerAngleCos;
|
||||||
|
info.SpotDir = light.SpotDir();
|
||||||
|
info.Color = light.rgb;
|
||||||
|
Lights.push_back(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CPUEmissiveSurface CPURaytracer::GetEmissive(Surface* surface)
|
||||||
|
{
|
||||||
|
CPUEmissiveSurface info;
|
||||||
|
|
||||||
|
SurfaceLightDef* def = nullptr;
|
||||||
|
if (surface->type >= ST_MIDDLESIDE && surface->type <= ST_LOWERSIDE)
|
||||||
|
{
|
||||||
|
int lightdefidx = mesh->map->Sides[surface->typeIndex].lightdef;
|
||||||
|
if (lightdefidx != -1)
|
||||||
|
{
|
||||||
|
def = &mesh->map->SurfaceLights[lightdefidx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (surface->type == ST_FLOOR || surface->type == ST_CEILING)
|
||||||
|
{
|
||||||
|
MapSubsectorEx* sub = &mesh->map->GLSubsectors[surface->typeIndex];
|
||||||
|
IntSector* sector = mesh->map->GetSectorFromSubSector(sub);
|
||||||
|
|
||||||
|
if (sector && surface->numVerts > 0)
|
||||||
|
{
|
||||||
|
if (sector->floorlightdef != -1 && surface->type == ST_FLOOR)
|
||||||
|
{
|
||||||
|
def = &mesh->map->SurfaceLights[sector->floorlightdef];
|
||||||
|
}
|
||||||
|
else if (sector->ceilinglightdef != -1 && surface->type == ST_CEILING)
|
||||||
|
{
|
||||||
|
def = &mesh->map->SurfaceLights[sector->ceilinglightdef];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (def)
|
||||||
|
{
|
||||||
|
info.Distance = def->distance + def->distance;
|
||||||
|
info.Intensity = def->intensity;
|
||||||
|
info.Color = def->rgb;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
info.Distance = 0.0f;
|
||||||
|
info.Intensity = 0.0f;
|
||||||
|
info.Color = Vec3(0.0f, 0.0f, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
LevelTraceHit CPURaytracer::Trace(const Vec3& startVec, const Vec3& endVec)
|
||||||
|
{
|
||||||
|
TraceHit hit = TriangleMeshShape::find_first_hit(CollisionMesh.get(), startVec, endVec);
|
||||||
|
|
||||||
|
LevelTraceHit trace;
|
||||||
|
trace.start = startVec;
|
||||||
|
trace.end = endVec;
|
||||||
|
trace.fraction = hit.fraction;
|
||||||
|
if (trace.fraction < 1.0f)
|
||||||
|
{
|
||||||
|
int elementIdx = hit.triangle * 3;
|
||||||
|
trace.hitSurface = mesh->surfaces[mesh->MeshSurfaces[hit.triangle]].get();
|
||||||
|
trace.indices[0] = mesh->MeshUVIndex[mesh->MeshElements[elementIdx]];
|
||||||
|
trace.indices[1] = mesh->MeshUVIndex[mesh->MeshElements[elementIdx + 1]];
|
||||||
|
trace.indices[2] = mesh->MeshUVIndex[mesh->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CPURaytracer::TraceAnyHit(const Vec3& startVec, const Vec3& endVec)
|
||||||
|
{
|
||||||
|
return TriangleMeshShape::find_any_hit(CollisionMesh.get(), startVec, endVec);
|
||||||
|
}
|
98
src/lightmap/cpuraytracer.h
Normal file
98
src/lightmap/cpuraytracer.h
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class LevelMesh;
|
||||||
|
|
||||||
|
struct CPUTraceTask
|
||||||
|
{
|
||||||
|
int id, x, y;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CPULightInfo
|
||||||
|
{
|
||||||
|
Vec3 Origin;
|
||||||
|
float Radius;
|
||||||
|
float Intensity;
|
||||||
|
float InnerAngleCos;
|
||||||
|
float OuterAngleCos;
|
||||||
|
Vec3 SpotDir;
|
||||||
|
Vec3 Color;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CPUTraceState
|
||||||
|
{
|
||||||
|
uint32_t SampleIndex;
|
||||||
|
uint32_t SampleCount;
|
||||||
|
uint32_t PassType;
|
||||||
|
uint32_t LightCount;
|
||||||
|
Vec3 SunDir;
|
||||||
|
float SampleDistance;
|
||||||
|
Vec3 SunColor;
|
||||||
|
float SunIntensity;
|
||||||
|
Vec3 HemisphereVec;
|
||||||
|
|
||||||
|
Vec3 StartPosition;
|
||||||
|
Surface* StartSurface;
|
||||||
|
|
||||||
|
Vec3 Position;
|
||||||
|
Surface* Surface;
|
||||||
|
|
||||||
|
Vec3 Output;
|
||||||
|
float OutputAttenuation;
|
||||||
|
|
||||||
|
bool EndTrace;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CPUEmissiveSurface
|
||||||
|
{
|
||||||
|
float Distance;
|
||||||
|
float Intensity;
|
||||||
|
Vec3 Color;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LevelTraceHit
|
||||||
|
{
|
||||||
|
Vec3 start;
|
||||||
|
Vec3 end;
|
||||||
|
float fraction;
|
||||||
|
|
||||||
|
Surface* hitSurface;
|
||||||
|
int indices[3];
|
||||||
|
float b, c;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CPURaytracer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CPURaytracer();
|
||||||
|
~CPURaytracer();
|
||||||
|
|
||||||
|
void Raytrace(LevelMesh* level);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void RaytraceTask(const CPUTraceTask& task);
|
||||||
|
void RunBounceTrace(CPUTraceState& state);
|
||||||
|
void RunLightTrace(CPUTraceState& state);
|
||||||
|
|
||||||
|
CPUEmissiveSurface GetEmissive(Surface* surface);
|
||||||
|
|
||||||
|
void CreateHemisphereVectors();
|
||||||
|
void CreateLights();
|
||||||
|
|
||||||
|
LevelTraceHit Trace(const Vec3& startVec, const Vec3& endVec);
|
||||||
|
bool TraceAnyHit(const Vec3& startVec, const Vec3& endVec);
|
||||||
|
|
||||||
|
static Vec3 ImportanceSample(const Vec3& HemisphereVec, Vec3 N);
|
||||||
|
|
||||||
|
static float RadicalInverse_VdC(uint32_t bits);
|
||||||
|
static Vec2 Hammersley(uint32_t i, uint32_t N);
|
||||||
|
|
||||||
|
const int coverageSampleCount = 256;
|
||||||
|
const int bounceSampleCount = 2048;
|
||||||
|
|
||||||
|
LevelMesh* mesh = nullptr;
|
||||||
|
std::vector<Vec3> HemisphereVectors;
|
||||||
|
std::vector<CPULightInfo> Lights;
|
||||||
|
|
||||||
|
std::unique_ptr<TriangleMeshShape> CollisionMesh;
|
||||||
|
};
|
|
@ -110,16 +110,15 @@ void main()
|
||||||
traceRayEXT(acc, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, origin + normal * 0.1, minDistance, L, 32768, 0);
|
traceRayEXT(acc, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, origin + normal * 0.1, minDistance, L, 32768, 0);
|
||||||
if (payload.hitAttenuation == 1.0)
|
if (payload.hitAttenuation == 1.0)
|
||||||
{
|
{
|
||||||
float hitDistance = distance(origin, payload.hitPosition);
|
|
||||||
surfaceIndex = payload.hitSurfaceIndex;
|
surfaceIndex = payload.hitSurfaceIndex;
|
||||||
SurfaceInfo surface = surfaces[surfaceIndex];
|
SurfaceInfo surface = surfaces[surfaceIndex];
|
||||||
origin = payload.hitPosition;
|
|
||||||
|
|
||||||
if (surface.EmissiveDistance > 0.0)
|
if (surface.EmissiveDistance > 0.0)
|
||||||
{
|
{
|
||||||
|
float hitDistance = distance(origin, payload.hitPosition);
|
||||||
float attenuation = max(1.0 - (hitDistance / surface.EmissiveDistance), 0.0f);
|
float attenuation = max(1.0 - (hitDistance / surface.EmissiveDistance), 0.0f);
|
||||||
incoming.rgb += surface.EmissiveColor * (surface.EmissiveIntensity * attenuation * incoming.w);
|
incoming.rgb += surface.EmissiveColor * (surface.EmissiveIntensity * attenuation * incoming.w);
|
||||||
}
|
}
|
||||||
|
origin = payload.hitPosition;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,11 +70,13 @@ void main()
|
||||||
if (surfaceIndex == -1 || incoming.w <= 0.0)
|
if (surfaceIndex == -1 || incoming.w <= 0.0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
vec3 origin = data0.xyz;
|
||||||
vec3 normal;
|
vec3 normal;
|
||||||
if (surfaceIndex >= 0)
|
if (surfaceIndex >= 0)
|
||||||
|
{
|
||||||
normal = surfaces[surfaceIndex].Normal;
|
normal = surfaces[surfaceIndex].Normal;
|
||||||
|
origin += normal * 0.1;
|
||||||
vec3 origin = data0.xyz + normal * 0.1;
|
}
|
||||||
|
|
||||||
const float minDistance = 0.01;
|
const float minDistance = 0.01;
|
||||||
|
|
||||||
|
@ -104,7 +106,7 @@ void main()
|
||||||
traceRayEXT(acc, gl_RayFlagsOpaqueEXT, 0xff, 2, 0, 2, origin, minDistance, SunDir, dist, 0);
|
traceRayEXT(acc, gl_RayFlagsOpaqueEXT, 0xff, 2, 0, 2, origin, minDistance, SunDir, dist, 0);
|
||||||
attenuation = payload.hitAttenuation;
|
attenuation = payload.hitAttenuation;
|
||||||
}
|
}
|
||||||
incoming.rgb += SunColor * (attenuation * SunIntensity) * incoming.w;
|
incoming.rgb += SunColor * (attenuation * SunIntensity * incoming.w);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint j = 0; j < LightCount; j++)
|
for (uint j = 0; j < LightCount; j++)
|
||||||
|
|
|
@ -1,302 +0,0 @@
|
||||||
|
|
||||||
#include "math/mathlib.h"
|
|
||||||
#include "surfaces.h"
|
|
||||||
#include "level/level.h"
|
|
||||||
#include "raytracer.h"
|
|
||||||
#include "worker.h"
|
|
||||||
#include "framework/binfile.h"
|
|
||||||
#include "framework/templates.h"
|
|
||||||
#include "framework/halffloat.h"
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <zlib.h>
|
|
||||||
|
|
||||||
extern int LightBounce;
|
|
||||||
|
|
||||||
Raytracer::Raytracer()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Raytracer::~Raytracer()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Raytracer::Raytrace(LevelMesh* level)
|
|
||||||
{
|
|
||||||
mesh = level;
|
|
||||||
|
|
||||||
printf("Tracing light probes\n");
|
|
||||||
|
|
||||||
Worker::RunJob((int)mesh->lightProbes.size(), [=](int id) {
|
|
||||||
RaytraceProbeSample(&mesh->lightProbes[id]);
|
|
||||||
});
|
|
||||||
|
|
||||||
printf("Tracing surfaces (%d bounces)\n", LightBounce);
|
|
||||||
|
|
||||||
struct SurfaceTask
|
|
||||||
{
|
|
||||||
int surf, x, y;
|
|
||||||
};
|
|
||||||
std::vector<SurfaceTask> tasks;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < mesh->surfaces.size(); i++)
|
|
||||||
{
|
|
||||||
Surface* surface = mesh->surfaces[i].get();
|
|
||||||
int sampleWidth = surface->lightmapDims[0];
|
|
||||||
int sampleHeight = surface->lightmapDims[1];
|
|
||||||
for (int y = 0; y < sampleHeight; y++)
|
|
||||||
{
|
|
||||||
for (int x = 0; x < sampleWidth; x++)
|
|
||||||
{
|
|
||||||
SurfaceTask task;
|
|
||||||
task.surf = (int)i;
|
|
||||||
task.x = x;
|
|
||||||
task.y = y;
|
|
||||||
tasks.push_back(task);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Worker::RunJob((int)tasks.size(), [=](int id) {
|
|
||||||
const SurfaceTask& task = tasks[id];
|
|
||||||
RaytraceSurfaceSample(mesh->surfaces[task.surf].get(), task.x, task.y);
|
|
||||||
});
|
|
||||||
|
|
||||||
printf("Raytrace complete\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Raytracer::RaytraceProbeSample(LightProbeSample* probe)
|
|
||||||
{
|
|
||||||
Vec3 incoming(0.0f, 0.0f, 0.0f);
|
|
||||||
|
|
||||||
if (LightBounce > 0)
|
|
||||||
{
|
|
||||||
Vec3 directions[6] =
|
|
||||||
{
|
|
||||||
{ 1.0f, 0.0f, 0.0f },
|
|
||||||
{ -1.0f, 0.0f, 0.0f },
|
|
||||||
{ 0.0f, 1.0f, 0.0f },
|
|
||||||
{ 0.0f, -1.0f, 0.0f },
|
|
||||||
{ 0.0f, 0.0f, 1.0f, },
|
|
||||||
{ 0.0f, 0.0f, -1.0f, }
|
|
||||||
};
|
|
||||||
for (int i = 0; i < SAMPLE_COUNT; i++)
|
|
||||||
{
|
|
||||||
const Vec3& normal = directions[i % 6];
|
|
||||||
Vec2 Xi = Hammersley(i, SAMPLE_COUNT);
|
|
||||||
Vec3 H = ImportanceSampleGGX(Xi, normal, 1.0f);
|
|
||||||
Vec3 L = Vec3::Normalize(H * (2.0f * Vec3::Dot(normal, H)) - normal);
|
|
||||||
incoming += TracePath(probe->Position, L, i);
|
|
||||||
}
|
|
||||||
incoming = incoming / (float)SAMPLE_COUNT / (float)LightBounce;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ThingLight& light : mesh->map->ThingLights)
|
|
||||||
{
|
|
||||||
Vec3 lightOrigin = light.LightOrigin();
|
|
||||||
float lightRadius = light.LightRadius();
|
|
||||||
|
|
||||||
if (probe->Position.DistanceSq(lightOrigin) > (lightRadius * lightRadius))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (mesh->TraceAnyHit(lightOrigin, probe->Position))
|
|
||||||
continue; // this light is occluded by something
|
|
||||||
|
|
||||||
Vec3 dir = (lightOrigin - probe->Position);
|
|
||||||
float dist = dir.Unit();
|
|
||||||
dir.Normalize();
|
|
||||||
|
|
||||||
incoming += light.rgb * (light.SpotAttenuation(dir) * light.DistAttenuation(dist) * light.intensity);
|
|
||||||
}
|
|
||||||
|
|
||||||
const Vec3& sundir = mesh->map->GetSunDirection();
|
|
||||||
LevelTraceHit trace = mesh->Trace(probe->Position, probe->Position + sundir * 32768.0f);
|
|
||||||
if (trace.fraction != 1.0f && trace.hitSurface->bSky)
|
|
||||||
incoming += mesh->map->GetSunColor();
|
|
||||||
|
|
||||||
probe->Color = incoming;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Raytracer::RaytraceSurfaceSample(Surface* surface, int x, int y)
|
|
||||||
{
|
|
||||||
Vec3 normal = surface->plane.Normal();
|
|
||||||
Vec3 pos = surface->lightmapOrigin + normal + surface->lightmapSteps[0] * (float)x + surface->lightmapSteps[1] * (float)y;
|
|
||||||
|
|
||||||
Vec3 incoming(0.0f, 0.0f, 0.0f);
|
|
||||||
if (LightBounce > 0)
|
|
||||||
{
|
|
||||||
float totalWeight = 0.0f;
|
|
||||||
for (int i = 0; i < SAMPLE_COUNT; i++)
|
|
||||||
{
|
|
||||||
Vec2 Xi = Hammersley(i, SAMPLE_COUNT);
|
|
||||||
Vec3 H = ImportanceSampleGGX(Xi, normal, 1.0f);
|
|
||||||
Vec3 L = Vec3::Normalize(H * (2.0f * Vec3::Dot(normal, H)) - normal);
|
|
||||||
float NdotL = std::max(Vec3::Dot(normal, L), 0.0f);
|
|
||||||
if (NdotL > 0.0f)
|
|
||||||
{
|
|
||||||
incoming += TracePath(pos, L, i) * NdotL;
|
|
||||||
totalWeight += NdotL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
incoming = incoming / totalWeight / (float)LightBounce;
|
|
||||||
}
|
|
||||||
|
|
||||||
incoming = incoming + GetSurfaceEmittance(surface, 0.0f) + GetLightEmittance(surface, pos);
|
|
||||||
|
|
||||||
const Vec3& sundir = mesh->map->GetSunDirection();
|
|
||||||
float attenuation = normal.Dot(sundir);
|
|
||||||
if (attenuation > 0.0f)
|
|
||||||
{
|
|
||||||
LevelTraceHit trace = mesh->Trace(pos, pos + sundir * 32768.0f);
|
|
||||||
if (trace.fraction != 1.0f && trace.hitSurface->bSky)
|
|
||||||
incoming += mesh->map->GetSunColor() * attenuation;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sampleWidth = surface->lightmapDims[0];
|
|
||||||
surface->samples[x + y * sampleWidth] = incoming;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 Raytracer::GetLightEmittance(Surface* surface, const Vec3& pos)
|
|
||||||
{
|
|
||||||
Vec3 emittance = Vec3(0.0f);
|
|
||||||
for (ThingLight& light : mesh->map->ThingLights)
|
|
||||||
{
|
|
||||||
Vec3 lightOrigin = light.LightOrigin();
|
|
||||||
float lightRadius = light.LightRadius();
|
|
||||||
|
|
||||||
if (surface->plane.Distance(lightOrigin) - surface->plane.d < 0)
|
|
||||||
continue; // completely behind the plane
|
|
||||||
|
|
||||||
if (pos.DistanceSq(lightOrigin) > (lightRadius * lightRadius))
|
|
||||||
continue; // light too far away
|
|
||||||
|
|
||||||
Vec3 dir = (lightOrigin - pos);
|
|
||||||
float dist = dir.Unit();
|
|
||||||
dir.Normalize();
|
|
||||||
|
|
||||||
float attenuation = light.SpotAttenuation(dir) * light.DistAttenuation(dist) * surface->plane.Normal().Dot(dir);
|
|
||||||
if (attenuation <= 0.0f)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (mesh->TraceAnyHit(lightOrigin, pos))
|
|
||||||
continue; // this light is occluded by something
|
|
||||||
|
|
||||||
emittance += light.rgb * (attenuation * light.intensity);
|
|
||||||
}
|
|
||||||
return emittance;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 Raytracer::TracePath(const Vec3& pos, const Vec3& dir, int sampleIndex, int depth)
|
|
||||||
{
|
|
||||||
if (depth >= LightBounce)
|
|
||||||
return Vec3(0.0f);
|
|
||||||
|
|
||||||
LevelTraceHit hit = mesh->Trace(pos + dir * 0.1f, pos + dir * 2000.0f);
|
|
||||||
if (hit.fraction == 1.0f)
|
|
||||||
return Vec3(0.0f);
|
|
||||||
|
|
||||||
Vec3 normal = hit.hitSurface->plane.Normal();
|
|
||||||
Vec3 hitpos = hit.start * (1.0f - hit.fraction) + hit.end * hit.fraction;
|
|
||||||
|
|
||||||
Vec3 emittance = GetSurfaceEmittance(hit.hitSurface, pos.Distance(hitpos)) + GetLightEmittance(hit.hitSurface, hitpos) * 0.5f;
|
|
||||||
|
|
||||||
const Vec3& sundir = mesh->map->GetSunDirection();
|
|
||||||
float attenuation = normal.Dot(sundir);
|
|
||||||
if (attenuation > 0.0f)
|
|
||||||
{
|
|
||||||
Vec3 start = hitpos + normal * 0.1f;
|
|
||||||
LevelTraceHit trace = mesh->Trace(start, start + sundir * 32768.0f);
|
|
||||||
if (trace.fraction != 1.0f && trace.hitSurface->bSky)
|
|
||||||
emittance += mesh->map->GetSunColor() * (attenuation * 0.5f);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec2 Xi = Hammersley(sampleIndex, SAMPLE_COUNT);
|
|
||||||
Vec3 H = ImportanceSampleGGX(Xi, normal, 1.0f);
|
|
||||||
Vec3 L = Vec3::Normalize(H * (2.0f * Vec3::Dot(normal, H)) - normal);
|
|
||||||
|
|
||||||
float NdotL = Vec3::Dot(normal, L);
|
|
||||||
if (NdotL <= 0.0f)
|
|
||||||
return emittance;
|
|
||||||
|
|
||||||
const float p = 1 / (2 * M_PI);
|
|
||||||
Vec3 incoming = TracePath(hitpos, L, (sampleIndex + depth + 1) % SAMPLE_COUNT, depth + 1);
|
|
||||||
|
|
||||||
return emittance + incoming * NdotL / p;
|
|
||||||
}
|
|
||||||
|
|
||||||
float Raytracer::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
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec2 Raytracer::Hammersley(uint32_t i, uint32_t N)
|
|
||||||
{
|
|
||||||
return Vec2(float(i) / float(N), RadicalInverse_VdC(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 Raytracer::ImportanceSampleGGX(Vec2 Xi, Vec3 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
|
|
||||||
Vec3 H(std::cos(phi) * sinTheta, std::sin(phi) * sinTheta, cosTheta);
|
|
||||||
|
|
||||||
// from tangent-space vector to world-space sample vector
|
|
||||||
Vec3 up = std::abs(N.z) < 0.999f ? Vec3(0.0f, 0.0f, 1.0f) : Vec3(1.0f, 0.0f, 0.0f);
|
|
||||||
Vec3 tangent = Vec3::Normalize(Vec3::Cross(up, N));
|
|
||||||
Vec3 bitangent = Vec3::Cross(N, tangent);
|
|
||||||
|
|
||||||
Vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z;
|
|
||||||
return Vec3::Normalize(sampleVec);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 Raytracer::GetSurfaceEmittance(Surface* surface, float distance)
|
|
||||||
{
|
|
||||||
SurfaceLightDef* def = nullptr;
|
|
||||||
if (surface->type >= ST_MIDDLESIDE && surface->type <= ST_LOWERSIDE)
|
|
||||||
{
|
|
||||||
int lightdefidx = mesh->map->Sides[surface->typeIndex].lightdef;
|
|
||||||
if (lightdefidx != -1)
|
|
||||||
{
|
|
||||||
def = &mesh->map->SurfaceLights[lightdefidx];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (surface->type == ST_FLOOR || surface->type == ST_CEILING)
|
|
||||||
{
|
|
||||||
MapSubsectorEx* sub = &mesh->map->GLSubsectors[surface->typeIndex];
|
|
||||||
IntSector* sector = mesh->map->GetSectorFromSubSector(sub);
|
|
||||||
|
|
||||||
if (sector && surface->numVerts > 0)
|
|
||||||
{
|
|
||||||
if (sector->floorlightdef != -1 && surface->type == ST_FLOOR)
|
|
||||||
{
|
|
||||||
def = &mesh->map->SurfaceLights[sector->floorlightdef];
|
|
||||||
}
|
|
||||||
else if (sector->ceilinglightdef != -1 && surface->type == ST_CEILING)
|
|
||||||
{
|
|
||||||
def = &mesh->map->SurfaceLights[sector->ceilinglightdef];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (def && distance < def->distance + def->distance)
|
|
||||||
{
|
|
||||||
float radius = def->distance + def->distance;
|
|
||||||
float attenuation = std::max(1.0f - (distance / radius), 0.0f);
|
|
||||||
return def->rgb * (attenuation * def->intensity);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return Vec3(0.0f);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
class LevelMesh;
|
|
||||||
|
|
||||||
class Raytracer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Raytracer();
|
|
||||||
~Raytracer();
|
|
||||||
|
|
||||||
void Raytrace(LevelMesh* level);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void RaytraceProbeSample(LightProbeSample* probe);
|
|
||||||
void RaytraceSurfaceSample(Surface* surface, int x, int y);
|
|
||||||
Vec3 TracePath(const Vec3& pos, const Vec3& dir, int sampleIndex, int depth = 0);
|
|
||||||
|
|
||||||
Vec3 GetLightEmittance(Surface* surface, const Vec3& pos);
|
|
||||||
Vec3 GetSurfaceEmittance(Surface* surface, float distance);
|
|
||||||
|
|
||||||
static float RadicalInverse_VdC(uint32_t bits);
|
|
||||||
static Vec2 Hammersley(uint32_t i, uint32_t N);
|
|
||||||
static Vec3 ImportanceSampleGGX(Vec2 Xi, Vec3 N, float roughness);
|
|
||||||
|
|
||||||
int SAMPLE_COUNT = 1024;
|
|
||||||
|
|
||||||
LevelMesh* mesh = nullptr;
|
|
||||||
};
|
|
|
@ -62,7 +62,7 @@ LevelMesh::LevelMesh(FLevel &doomMap, int sampleDistance, int textureSize)
|
||||||
|
|
||||||
printf("Surfaces total: %i\n\n", (int)surfaces.size());
|
printf("Surfaces total: %i\n\n", (int)surfaces.size());
|
||||||
|
|
||||||
printf("Building collision mesh..\n\n");
|
printf("Building level mesh..\n\n");
|
||||||
|
|
||||||
for (size_t i = 0; i < surfaces.size(); i++)
|
for (size_t i = 0; i < surfaces.size(); i++)
|
||||||
{
|
{
|
||||||
|
@ -108,8 +108,6 @@ LevelMesh::LevelMesh(FLevel &doomMap, int sampleDistance, int textureSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CollisionMesh = std::make_unique<TriangleMeshShape>(&MeshVertices[0], MeshVertices.Size(), &MeshElements[0], MeshElements.Size());
|
|
||||||
|
|
||||||
CreateLightProbes(doomMap);
|
CreateLightProbes(doomMap);
|
||||||
|
|
||||||
for (size_t i = 0; i < surfaces.size(); i++)
|
for (size_t i = 0; i < surfaces.size(); i++)
|
||||||
|
@ -789,41 +787,6 @@ void LevelMesh::CreateSubsectorSurfaces(FLevel &doomMap)
|
||||||
printf("\nLeaf surfaces: %i\n", (int)surfaces.size() - doomMap.NumGLSubsectors);
|
printf("\nLeaf surfaces: %i\n", (int)surfaces.size() - doomMap.NumGLSubsectors);
|
||||||
}
|
}
|
||||||
|
|
||||||
LevelTraceHit LevelMesh::Trace(const Vec3 &startVec, const Vec3 &endVec)
|
|
||||||
{
|
|
||||||
TraceHit hit = TriangleMeshShape::find_first_hit(CollisionMesh.get(), startVec, endVec);
|
|
||||||
|
|
||||||
LevelTraceHit trace;
|
|
||||||
trace.start = startVec;
|
|
||||||
trace.end = endVec;
|
|
||||||
trace.fraction = hit.fraction;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LevelMesh::TraceAnyHit(const Vec3 &startVec, const Vec3 &endVec)
|
|
||||||
{
|
|
||||||
return TriangleMeshShape::find_any_hit(CollisionMesh.get(), startVec, endVec);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LevelMesh::IsDegenerate(const Vec3 &v0, const Vec3 &v1, const Vec3 &v2)
|
bool LevelMesh::IsDegenerate(const Vec3 &v0, const Vec3 &v1, const Vec3 &v2)
|
||||||
{
|
{
|
||||||
// A degenerate triangle has a zero cross product for two of its sides.
|
// A degenerate triangle has a zero cross product for two of its sides.
|
||||||
|
|
|
@ -74,17 +74,6 @@ struct Surface
|
||||||
std::string material;
|
std::string material;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LevelTraceHit
|
|
||||||
{
|
|
||||||
Vec3 start;
|
|
||||||
Vec3 end;
|
|
||||||
float fraction;
|
|
||||||
|
|
||||||
Surface *hitSurface;
|
|
||||||
int indices[3];
|
|
||||||
float b, c;
|
|
||||||
};
|
|
||||||
|
|
||||||
class LightmapTexture
|
class LightmapTexture
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -166,9 +155,6 @@ public:
|
||||||
void AddLightmapLump(FWadWriter& wadFile);
|
void AddLightmapLump(FWadWriter& wadFile);
|
||||||
void Export(std::string filename);
|
void Export(std::string filename);
|
||||||
|
|
||||||
LevelTraceHit Trace(const Vec3 &startVec, const Vec3 &endVec);
|
|
||||||
bool TraceAnyHit(const Vec3 &startVec, const Vec3 &endVec);
|
|
||||||
|
|
||||||
FLevel* map = nullptr;
|
FLevel* map = nullptr;
|
||||||
|
|
||||||
std::vector<std::unique_ptr<Surface>> surfaces;
|
std::vector<std::unique_ptr<Surface>> surfaces;
|
||||||
|
@ -184,7 +170,6 @@ public:
|
||||||
TArray<int> MeshUVIndex;
|
TArray<int> MeshUVIndex;
|
||||||
TArray<unsigned int> MeshElements;
|
TArray<unsigned int> MeshElements;
|
||||||
TArray<int> MeshSurfaces;
|
TArray<int> MeshSurfaces;
|
||||||
std::unique_ptr<TriangleMeshShape> CollisionMesh;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void CreateSubsectorSurfaces(FLevel &doomMap);
|
void CreateSubsectorSurfaces(FLevel &doomMap);
|
||||||
|
|
Loading…
Reference in a new issue