diff --git a/RayTracing/src/BVH.h b/RayTracing/src/BVH.h index d7f4346..f1590fe 100644 --- a/RayTracing/src/BVH.h +++ b/RayTracing/src/BVH.h @@ -2,6 +2,7 @@ #include "Scene.h" #include "Ray.h" +#include "Constants.h" #include @@ -104,7 +105,7 @@ class BVH int resultSphere = -1; // Stack-based traversal (max 64 levels for a ~2^64 sphere scene) - int stack[64]; + int stack[kBVHMaxStackDepth]; int stackPtr = 0; stack[stackPtr++] = 0; // root node diff --git a/RayTracing/src/CPUBackend.cpp b/RayTracing/src/CPUBackend.cpp index ab5002b..c152f2e 100644 --- a/RayTracing/src/CPUBackend.cpp +++ b/RayTracing/src/CPUBackend.cpp @@ -294,7 +294,7 @@ glm::vec4 CPUBackend::PerPixel(uint32_t x, uint32_t y) const if (i > 2) { // Use luminance (BT.709) for survival probability: fairer than max(channel) - const float p = 0.2126f * contribution.r + 0.7152f * contribution.g + 0.0722f * contribution.b; + const float p = kLuminanceR * contribution.r + kLuminanceG * contribution.g + kLuminanceB * contribution.b; if (p < kRussianRouletteThreshold || (p < 1.0f && PathTracerCore::RandomFloat(seed) > p)) break; contribution /= p; diff --git a/RayTracing/src/CUDARenderer.cu b/RayTracing/src/CUDARenderer.cu index 2afe0ed..1cf980c 100644 --- a/RayTracing/src/CUDARenderer.cu +++ b/RayTracing/src/CUDARenderer.cu @@ -159,7 +159,7 @@ int CUDARenderer_Init(CUDARenderState* state) } std::printf("[CUDA] Using device: %s (Compute %d.%d, %zu MB)\n", prop.name, prop.major, prop.minor, - prop.totalGlobalMem / (1024 * 1024)); + prop.totalGlobalMem / (static_cast(kBytesPerMB))); state->initialized = true; err = cudaStreamCreate(&state->uploadStream); @@ -226,7 +226,7 @@ void CUDARenderer_OnResize(CUDARenderState* state, uint32_t width, uint32_t heig std::printf("[CUDA] Resized to %ux%u (%.2f MB device memory)\n", width, height, - static_cast(state->pixelCount * (2 * sizeof(float4) + sizeof(uint32_t) + sizeof(float3))) / (1024.0f * 1024.0f)); + static_cast(state->pixelCount * (2 * sizeof(float4) + sizeof(uint32_t) + sizeof(float3))) / static_cast(kBytesPerMB)); } void CUDARenderer_UploadScene( diff --git a/RayTracing/src/CUDARenderer.cuh b/RayTracing/src/CUDARenderer.cuh index 5f61044..ee255b0 100644 --- a/RayTracing/src/CUDARenderer.cuh +++ b/RayTracing/src/CUDARenderer.cuh @@ -94,7 +94,7 @@ __device__ inline float3 SampleGGX_VNDF(float3 V, float a, float r1, float r2) float3 up = make_float3(0.0f, 0.0f, 1.0f); float3 T1; - if (Vh.z < 0.9999f) { + if (Vh.z < kONBThreshold) { T1 = make_float3(-Vh.y, Vh.x, 0.0f); float t1Len = sqrtf(T1.x*T1.x + T1.y*T1.y); T1 = make_float3(T1.x/t1Len, T1.y/t1Len, 0.0f); @@ -205,7 +205,7 @@ __device__ inline GPUHitPayload TraceRay( ); // Stack-based BVH traversal (max 64 levels) - int stack[64]; + int stack[kBVHMaxStackDepth]; int stackPtr = 0; stack[stackPtr++] = 0; // root node @@ -280,7 +280,7 @@ __device__ inline GPUHitPayload TraceRay( int rightChild = node.Count; // Push far child first, then near child (near will be popped first) - if (stackPtr + 2 <= 64) + if (stackPtr + 2 <= kBVHMaxStackDepth) { stack[stackPtr++] = rightChild; stack[stackPtr++] = leftChild; @@ -345,7 +345,7 @@ __device__ inline float3 PerPixel( if (i > 2) { // Use luminance (BT.709) for survival probability: fairer than max(channel) - float p = 0.2126f * contribution.x + 0.7152f * contribution.y + 0.0722f * contribution.z; + float p = kLuminanceR * contribution.x + kLuminanceG * contribution.y + kLuminanceB * contribution.z; if (p < kRussianRouletteThreshold || (p < 1.0f && RandomFloat(seed) > p)) break; float invP = 1.0f / p; diff --git a/RayTracing/src/Constants.h b/RayTracing/src/Constants.h index 3df4e6e..a08706b 100644 --- a/RayTracing/src/Constants.h +++ b/RayTracing/src/Constants.h @@ -11,3 +11,15 @@ constexpr float kNdotMin = 0.001f; // minimum dot(N,*) for mic constexpr float kRussianRouletteThreshold = 0.001f; // path termination probability threshold constexpr float kSpecDenominatorEps = 0.001f; // specular BRDF denominator guard constexpr float kInSphereEpsilon = 0.000001f; // rejection threshold for RandomInUnitSphere (1e-6) + +// ─── Luminance Constants (BT.709) ─── +constexpr float kLuminanceR = 0.2126f; +constexpr float kLuminanceG = 0.7152f; +constexpr float kLuminanceB = 0.0722f; + +// ─── Algorithmic Constants ─── +constexpr float kONBThreshold = 0.9999f; // BuildONB degeneracy check +constexpr int kBVHMaxStackDepth = 64; // BVH traversal stack (supports ~2^64 spheres) + +// ─── Unit Conversion ─── +constexpr int kBytesPerMB = 1024 * 1024; diff --git a/RayTracing/src/OptiXDenoiser.cpp b/RayTracing/src/OptiXDenoiser.cpp index cbea0ef..bdcb7ed 100644 --- a/RayTracing/src/OptiXDenoiser.cpp +++ b/RayTracing/src/OptiXDenoiser.cpp @@ -4,6 +4,8 @@ #include // Driver API for context bridging +#include "Constants.h" + // Define the global OptiX function table (required by optix_stubs.h) // Must be defined in exactly one translation unit. OptixFunctionTable g_optixFunctionTable_118 = {}; @@ -156,7 +158,7 @@ bool OptiXDenoiser::Initialize(uint32_t width, uint32_t height, cudaStream_t str width, height, static_cast(m_sizes.withoutOverlapScratchSizeInBytes + m_sizes.stateSizeInBytes - + sizeof(float)) / (1024.0f * 1024.0f)); + + sizeof(float)) / static_cast(kBytesPerMB)); return true; } diff --git a/RayTracing/src/PathTracer.ispc b/RayTracing/src/PathTracer.ispc index e6f8475..74d045f 100644 --- a/RayTracing/src/PathTracer.ispc +++ b/RayTracing/src/PathTracer.ispc @@ -14,6 +14,15 @@ static const float kRussianRouletteThreshold = 0.001f; static const float kSpecDenominatorEps = 0.001f; static const float kInSphereEpsilon = 1e-6f; +// ── Luminance Constants (BT.709) ── +static const float kLuminanceR = 0.2126f; +static const float kLuminanceG = 0.7152f; +static const float kLuminanceB = 0.0722f; + +// ── Algorithmic Constants ── +static const float kONBThreshold = 0.9999f; +#define kBVHMaxStackDepth 64 + // ── RNG: PCG Hash (matches Renderer.cpp Utils::PCG_Hash exactly) ── // Use unsigned int32: PCG hash relies on unsigned overflow behavior (well-defined). // ISPC's unsigned int32→float conversion is efficient on modern targets (AVX2/AVX512). @@ -100,7 +109,7 @@ static inline void SampleGGX_VNDF( // T1 = normalize(cross(up, Vh)) varying float T1x, T1y, T1z; - if (Vhz < 0.9999f) { + if (Vhz < kONBThreshold) { T1x = -Vhy; T1y = Vhx; T1z = 0.0f; varying float t1Len = sqrt(T1x*T1x + T1y*T1y); T1x /= t1Len; T1y /= t1Len; @@ -163,7 +172,7 @@ static inline float TraceRayBVH( varying bool negZ = (invZ < 0.0f); // Per-lane varying stack (ISPC handles varying arrays efficiently) - varying int stack[64]; + varying int stack[kBVHMaxStackDepth]; varying int stackPtr = 0; stack[stackPtr++] = 0; // root node @@ -374,7 +383,7 @@ export void ISPCRenderPixels( // ── Russian roulette (after 3 guaranteed bounces) ── // Use luminance (BT.709) for survival probability: fairer than max(channel) if (bounce > 2) { - varying float p = 0.2126f * contribR + 0.7152f * contribG + 0.0722f * contribB; + varying float p = kLuminanceR * contribR + kLuminanceG * contribG + kLuminanceB * contribB; if (p < kRussianRouletteThreshold) break; diff --git a/RayTracing/src/PathTracerCore.h b/RayTracing/src/PathTracerCore.h index 3da108c..f56279c 100644 --- a/RayTracing/src/PathTracerCore.h +++ b/RayTracing/src/PathTracerCore.h @@ -99,7 +99,7 @@ namespace PathTracerCore { glm::vec3 Vh = glm::normalize(glm::vec3(a * V.x, a * V.y, V.z)); glm::vec3 up(0.0f, 0.0f, 1.0f); - glm::vec3 T1 = (Vh.z < 0.9999f) ? glm::normalize(glm::cross(up, Vh)) : glm::vec3(1.0f, 0.0f, 0.0f); + glm::vec3 T1 = (Vh.z < kONBThreshold) ? glm::normalize(glm::cross(up, Vh)) : glm::vec3(1.0f, 0.0f, 0.0f); glm::vec3 T2 = glm::cross(Vh, T1); float r = glm::sqrt(r1);