mirror of
https://github.com/ZDoom/ZDRay.git
synced 2025-02-03 13:11:04 +00:00
Remove the CPU raytracer
This commit is contained in:
parent
8ed96484c2
commit
8dcd37691c
5 changed files with 3 additions and 724 deletions
|
@ -189,8 +189,6 @@ set( SOURCES
|
|||
src/lightmap/glsl_frag.h
|
||||
src/lightmap/glsl_vert.h
|
||||
src/lightmap/glsl_frag_resolve.h
|
||||
src/lightmap/cpuraytracer.cpp
|
||||
src/lightmap/cpuraytracer.h
|
||||
src/math/mat.cpp
|
||||
src/math/plane.cpp
|
||||
src/math/angle.cpp
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
*/
|
||||
|
||||
#include "level/level.h"
|
||||
#include "lightmap/cpuraytracer.h"
|
||||
#include "lightmap/gpuraytracer.h"
|
||||
#include "math/vec.h"
|
||||
//#include "rejectbuilder.h"
|
||||
|
@ -31,7 +30,6 @@
|
|||
#endif
|
||||
|
||||
extern int LMDims;
|
||||
extern bool CPURaytrace;
|
||||
|
||||
extern void ShowView (FLevel *level);
|
||||
|
||||
|
@ -752,8 +750,6 @@ void FProcessor::BuildNodes()
|
|||
}
|
||||
}
|
||||
|
||||
//#define USE_GPU_RAYTRACER
|
||||
|
||||
void FProcessor::BuildLightmaps()
|
||||
{
|
||||
Level.PostLoadInitialization();
|
||||
|
@ -767,29 +763,8 @@ void FProcessor::BuildLightmaps()
|
|||
|
||||
LightmapMesh = std::make_unique<LevelMesh>(Level, Level.DefaultSamples, LMDims);
|
||||
|
||||
std::unique_ptr<GPURaytracer> gpuraytracer;
|
||||
if (!CPURaytrace)
|
||||
{
|
||||
try
|
||||
{
|
||||
gpuraytracer = std::make_unique<GPURaytracer>();
|
||||
}
|
||||
catch (std::exception msg)
|
||||
{
|
||||
printf("%s\n", msg.what());
|
||||
printf("Falling back to CPU ray tracing\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (gpuraytracer)
|
||||
{
|
||||
gpuraytracer->Raytrace(LightmapMesh.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
CPURaytracer raytracer;
|
||||
raytracer.Raytrace(LightmapMesh.get());
|
||||
}
|
||||
std::unique_ptr<GPURaytracer> gpuraytracer = std::make_unique<GPURaytracer>();
|
||||
gpuraytracer->Raytrace(LightmapMesh.get());
|
||||
|
||||
LightmapMesh->CreateTextures();
|
||||
}
|
||||
|
|
|
@ -1,584 +0,0 @@
|
|||
|
||||
#include "math/mathlib.h"
|
||||
#include "levelmesh.h"
|
||||
#include "level/level.h"
|
||||
#include "cpuraytracer.h"
|
||||
#include "framework/binfile.h"
|
||||
#include "framework/templates.h"
|
||||
#include "framework/halffloat.h"
|
||||
#include "surfaceclip.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
#include <condition_variable>
|
||||
#include <atomic>
|
||||
|
||||
extern bool VKDebug;
|
||||
extern int NumThreads;
|
||||
|
||||
extern int coverageSampleCount;
|
||||
extern int bounceSampleCount;
|
||||
|
||||
CPURaytracer::CPURaytracer()
|
||||
{
|
||||
}
|
||||
|
||||
CPURaytracer::~CPURaytracer()
|
||||
{
|
||||
}
|
||||
|
||||
void CPURaytracer::Raytrace(LevelMesh* level)
|
||||
{
|
||||
mesh = level;
|
||||
|
||||
std::vector<CPUTraceTask> tasks;
|
||||
|
||||
CreateTasks(tasks);
|
||||
|
||||
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", mesh->map->LightBounce);
|
||||
printf("Ray tracing in progress...\n");
|
||||
|
||||
RunJob((int)tasks.size(), [=](int id) { RaytraceTask(tasks[id]); });
|
||||
|
||||
printf("\nRay tracing 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->worldOrigin + surface->worldStepX * ((float)task.x + 0.5f) + surface->worldStepY * ((float)task.y + 0.5f);
|
||||
state.StartPosition = pos;
|
||||
state.StartSurface = surface;
|
||||
}
|
||||
else
|
||||
{
|
||||
LightProbeSample& probe = mesh->lightProbes[(size_t)(-task.id) - 2];
|
||||
state.StartPosition = probe.Position;
|
||||
state.StartSurface = nullptr;
|
||||
}
|
||||
|
||||
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 < mesh->map->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->texWidth;
|
||||
surface->texPixels[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.Surf;
|
||||
}
|
||||
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 = normalize(H * (2.0f * dot(normal, H)) - normal);
|
||||
|
||||
float NdotL = std::max(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 = length(hitPosition - origin);
|
||||
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.Surf = 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.Surf;
|
||||
|
||||
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)
|
||||
{
|
||||
if (dot(normal, state.SunDir) > 0.0f)
|
||||
{
|
||||
vec3 e0 = normalize(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 = cross(normal, e0);
|
||||
e0 = cross(normal, e1);
|
||||
|
||||
for (uint32_t i = 0; i < state.SampleCount; i++)
|
||||
{
|
||||
vec2 offset = (Hammersley(i, state.SampleCount) - 0.5f) * float(surface->sampleDimension);
|
||||
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);
|
||||
incoming += state.SunColor * (attenuation * state.SunIntensity * incomingAttenuation);
|
||||
}
|
||||
}
|
||||
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 = length(light.Origin - origin);
|
||||
if (dist > minDistance && dist < light.Radius)
|
||||
{
|
||||
vec3 dir = normalize(light.Origin - origin);
|
||||
|
||||
if (!surface || dot(normal, dir) > 0.0)
|
||||
{
|
||||
float distAttenuation = std::max(1.0f - (dist / light.Radius), 0.0f);
|
||||
float angleAttenuation = 1.0f;
|
||||
if (surface)
|
||||
{
|
||||
angleAttenuation = std::max(dot(normal, dir), 0.0f);
|
||||
}
|
||||
float spotAttenuation = 1.0f;
|
||||
if (light.OuterAngleCos > -1.0f)
|
||||
{
|
||||
float cosDir = 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 = normalize(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 = cross(normal, e0);
|
||||
e0 = cross(normal, e1);
|
||||
for (uint32_t i = 0; i < state.SampleCount; i++)
|
||||
{
|
||||
vec2 offset = (Hammersley(i, state.SampleCount) - 0.5f) * float(surface->sampleDimension);
|
||||
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 = normalize(cross(up, N));
|
||||
vec3 bitangent = cross(N, tangent);
|
||||
|
||||
vec3 sampleVec = tangent * HemisphereVec.x + bitangent * HemisphereVec.y + N * HemisphereVec.z;
|
||||
return 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::CreateTasks(std::vector<CPUTraceTask>& tasks)
|
||||
{
|
||||
tasks.reserve(mesh->lightProbes.size());
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
size_t fullTaskCount = mesh->lightProbes.size();
|
||||
|
||||
for (size_t i = 0; i < mesh->surfaces.size(); i++)
|
||||
{
|
||||
if (i % 4096 == 0)
|
||||
printf("\rGathering surface trace tasks: %llu / %llu", i, mesh->surfaces.size());
|
||||
|
||||
Surface* surface = mesh->surfaces[i].get();
|
||||
|
||||
if (!surface->bSky)
|
||||
{
|
||||
int sampleWidth = surface->texWidth;
|
||||
int sampleHeight = surface->texHeight;
|
||||
|
||||
fullTaskCount += size_t(sampleHeight) * size_t(sampleWidth);
|
||||
|
||||
SurfaceClip surfaceClip(surface);
|
||||
|
||||
for (int y = 0; y < sampleHeight; y++)
|
||||
{
|
||||
for (int x = 0; x < sampleWidth; x++)
|
||||
{
|
||||
if (surfaceClip.SampleIsInBounds(float(x), float(y)))
|
||||
{
|
||||
CPUTraceTask task;
|
||||
task.id = (int)i;
|
||||
task.x = x;
|
||||
task.y = y;
|
||||
tasks.push_back(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("\rGathering surface trace tasks: %llu / %llu\n", mesh->surfaces.size(), mesh->surfaces.size());
|
||||
printf("\tDiscarded %.3f%% of all tasks\n", (1.0 - double(tasks.size()) / fullTaskCount) * 100.0);
|
||||
}
|
||||
|
||||
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 = 1.5f - length(Xi);
|
||||
H = normalize(H);
|
||||
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->verts.size() > 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);
|
||||
}
|
||||
|
||||
void CPURaytracer::RunJob(int count, std::function<void(int)> callback)
|
||||
{
|
||||
int numThreads = NumThreads;
|
||||
if (numThreads <= 0)
|
||||
numThreads = std::thread::hardware_concurrency();
|
||||
if (numThreads <= 0)
|
||||
numThreads = 4;
|
||||
|
||||
numThreads = std::min(numThreads, count);
|
||||
|
||||
std::condition_variable condvar;
|
||||
std::mutex m;
|
||||
int threadsleft = 0;
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
threadsleft = numThreads;
|
||||
}
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
for (int threadIndex = 0; threadIndex < numThreads; threadIndex++)
|
||||
{
|
||||
threads.push_back(std::thread([&, threadIndex]() {
|
||||
|
||||
if (threadIndex == 0)
|
||||
{
|
||||
for (int i = 0; i < count; i += numThreads)
|
||||
{
|
||||
if((i / numThreads) % 8192 == 0)
|
||||
printf("\r%.1f%%\t%d/%d", double(i) / double(count) * 100, i, count);
|
||||
callback(i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = threadIndex; i < count; i += numThreads)
|
||||
{
|
||||
callback(i);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
threadsleft--;
|
||||
lock.unlock();
|
||||
condvar.notify_all();
|
||||
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
while (threadsleft != 0)
|
||||
{
|
||||
condvar.wait_for(lock, std::chrono::milliseconds(500), [&]() { return threadsleft == 0; });
|
||||
}
|
||||
printf("\r%.1f%%\t%d/%d\n", 100.0, count, count);
|
||||
}
|
||||
|
||||
for (int i = 0; i < numThreads; i++)
|
||||
threads[i].join();
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include "collision.h"
|
||||
|
||||
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;
|
||||
vec3 SunColor;
|
||||
float SunIntensity;
|
||||
vec3 HemisphereVec;
|
||||
|
||||
vec3 StartPosition;
|
||||
Surface* StartSurface;
|
||||
|
||||
vec3 Position;
|
||||
Surface* Surf;
|
||||
|
||||
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 CreateTasks(std::vector<CPUTraceTask>& tasks);
|
||||
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);
|
||||
|
||||
static void RunJob(int count, std::function<void(int i)> callback);
|
||||
|
||||
LevelMesh* mesh = nullptr;
|
||||
std::vector<vec3> HemisphereVectors;
|
||||
std::vector<CPULightInfo> Lights;
|
||||
|
||||
std::unique_ptr<TriangleMeshShape> CollisionMesh;
|
||||
};
|
12
src/main.cpp
12
src/main.cpp
|
@ -115,12 +115,9 @@ bool HaveSSE1, HaveSSE2;
|
|||
int SSELevel;
|
||||
int NumThreads = 0;
|
||||
int LMDims = 1024;
|
||||
bool CPURaytrace = false;
|
||||
bool VKDebug = false;
|
||||
bool DumpMesh = false;
|
||||
|
||||
int coverageSampleCount = 256;
|
||||
int bounceSampleCount = 2048;
|
||||
int ambientSampleCount = 2048;
|
||||
|
||||
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
||||
|
@ -158,14 +155,13 @@ static option long_opts[] =
|
|||
{"comments", no_argument, 0, 'c'},
|
||||
{"threads", required_argument, 0, 'j'},
|
||||
{"size", required_argument, 0, 'S'},
|
||||
{"cpu-raytrace", no_argument, 0, 'C'},
|
||||
{"vkdebug", no_argument, 0, 'D'},
|
||||
{"dump-mesh", no_argument, 0, 1004},
|
||||
{"preview", no_argument, 0, 1005},
|
||||
{0,0,0,0}
|
||||
};
|
||||
|
||||
static const char short_opts[] = "wVgGvbNrReEm:o:f:p:s:d:PqtzZXx5cj:S:CD::";
|
||||
static const char short_opts[] = "wVgGvbNrReEm:o:f:p:s:d:PqtzZXx5cj:S:D::";
|
||||
|
||||
// CODE --------------------------------------------------------------------
|
||||
|
||||
|
@ -453,9 +449,6 @@ static void ParseArgs(int argc, char **argv)
|
|||
if (LMDims > 1024) LMDims = 1024;
|
||||
LMDims = Math::RoundPowerOfTwo(LMDims);
|
||||
break;
|
||||
case 'C':
|
||||
CPURaytrace = true;
|
||||
break;
|
||||
case 'D':
|
||||
VKDebug = true;
|
||||
break;
|
||||
|
@ -463,8 +456,6 @@ static void ParseArgs(int argc, char **argv)
|
|||
DumpMesh = true;
|
||||
break;
|
||||
case 1005:
|
||||
coverageSampleCount = 4;
|
||||
bounceSampleCount = 16;
|
||||
ambientSampleCount = 16;
|
||||
break;
|
||||
case 1000:
|
||||
|
@ -510,7 +501,6 @@ static void ShowUsage()
|
|||
" -P, --no-polyobjs Do not check for polyobject subsector splits\n"
|
||||
" -j, --threads=NNN Number of threads used for raytracing (default %d)\n"
|
||||
" -S, --size=NNN lightmap texture dimensions for width and height must be in powers of two (1, 2, 4, 8, 16, etc)\n"
|
||||
" -C, --cpu-raytrace Use the CPU for ray tracing\n"
|
||||
" -D, --vkdebug Print messages from the Vulkan validation layer\n"
|
||||
" --dump-mesh Export level mesh and lightmaps for debugging\n"
|
||||
" -w, --warn Show warning messages\n"
|
||||
|
|
Loading…
Reference in a new issue