diff --git a/neo/IceTech.sln b/neo/IceTech.sln index d2d38fd0..10f3bfd3 100644 --- a/neo/IceTech.sln +++ b/neo/IceTech.sln @@ -135,7 +135,6 @@ Global {6EA6406F-3E65-47D9-8246-D6660A81606F}.Dedicated Release|x64.ActiveCfg = Dedicated Release|Win32 {6EA6406F-3E65-47D9-8246-D6660A81606F}.Dedicated Release|x64.Build.0 = Dedicated Release|Win32 {6EA6406F-3E65-47D9-8246-D6660A81606F}.Prey Debug|x64.ActiveCfg = Debug with inlines|Win32 - {6EA6406F-3E65-47D9-8246-D6660A81606F}.Prey Debug|x64.Build.0 = Debug with inlines|Win32 {6EA6406F-3E65-47D9-8246-D6660A81606F}.Q4 Debug|x64.ActiveCfg = Debug with inlines|Win32 {6EA6406F-3E65-47D9-8246-D6660A81606F}.Q4 Debug|x64.Build.0 = Debug with inlines|Win32 {6EA6406F-3E65-47D9-8246-D6660A81606F}.Release|x64.ActiveCfg = Release|Win32 diff --git a/neo/engine/doomdll.vcxproj.user b/neo/engine/doomdll.vcxproj.user index a02f32d6..20aa55cb 100644 --- a/neo/engine/doomdll.vcxproj.user +++ b/neo/engine/doomdll.vcxproj.user @@ -31,7 +31,7 @@ WindowsLocalDebugger +set r_fullscreen 1 +set r_mode 9 D:\projects\Doom3 - +set r_fullscreen 0 +set r_mode 6|+set r_fullscreen 1 +set r_mode 6|+set r_fullscreen 1 +set r_mode 1|+set r_fullscreen 0 +set r_mode 1|+set r_fullscreen 0 +set r_mode 9| + +set r_fullscreen 1 +set r_mode 6|+set r_fullscreen 1 +set r_mode 1|+set r_fullscreen 0 +set r_mode 1|+set r_fullscreen 0 +set r_mode 9|+set r_fullscreen 1 +set r_mode 9| D:\projects\Doom3\Doom3.exe diff --git a/neo/engine/opengl/gl_d3d12raylight.cpp b/neo/engine/opengl/gl_d3d12raylight.cpp index 71e0e740..96f49eef 100644 --- a/neo/engine/opengl/gl_d3d12raylight.cpp +++ b/neo/engine/opengl/gl_d3d12raylight.cpp @@ -3398,9 +3398,19 @@ float EstimateSpecularRoughness(float3 specularAlbedo) { // No roughness map is available in this G-buffer path, so infer a stable // perceptual roughness from the specular map strength. Brighter spec maps - // get tighter highlights; dark/missing maps stay broad and subdued. + // get tighter highlights, but keep a floor high enough that POM/normal-map + // detail cannot collapse into one-pixel GGX fireflies. float peak = SpecularPeak3(saturate(specularAlbedo)); - return clamp(lerp(0.68, 0.34, peak), 0.28, 0.72); + return clamp(lerp(0.70, 0.42, peak), 0.38, 0.74); +} + +float3 LimitSpecularPeak(float3 c, float peakLimit) +{ + c = max(c, 0.0); + float peak = SpecularPeak3(c); + if (peak > peakLimit && peak > 1.0e-5) + c *= peakLimit / peak; + return c; } float3 ComputeSpecular( @@ -3463,7 +3473,7 @@ float3 ComputeSpecular( float3 F = F0 + (1.0 - F0) * pow(1.0 - VoH, 5.0); float specTerm = (D * G) / max(4.0 * NoL * NoV, 1.0e-4); - specTerm = min(specTerm, 5.0); + specTerm = min(specTerm, 3.0); const float SPECULAR_ENERGY_SCALE = 2.35; @@ -3477,7 +3487,7 @@ float3 ComputeSpecular( specTerm * SPECULAR_ENERGY_SCALE; - return clamp(specular, 0.0, 8.0); + return LimitSpecularPeak(specular, 4.0); } )" R"( @@ -3535,6 +3545,18 @@ float3 SampleCosineWorld(float3 N, inout uint rng) N * localDir.z); } +float3 SampleCosineWorldXi(float3 N, float2 xi) +{ + float3 tangent, bitangent; + BuildOrthonormalBasis(N, tangent, bitangent); + + float3 localDir = CosineSampleHemisphere(xi); + return normalize( + tangent * localDir.x + + bitangent * localDir.y + + N * localDir.z); +} + float3 SampleConeWorld(float3 centerDir, float coneRadius, inout uint rng) { float3 tangent, bitangent; @@ -4374,7 +4396,21 @@ float3 EstimateReactiveScreenSpaceFinalGather(uint2 pixel, float3 worldPos, floa } )" R"( -float3 TraceOneIndirectBouncePath(uint2 pixel, float3 worldPos, float3 N, float3 V, float3 baseAlbedo, inout uint rng) +float2 GetStratifiedBounceXi(uint2 pixel, float3 worldPos, float3 pathNormal, uint sampleIndex) +{ + uint spp = max(gSamplesPerPixel, 1u); + uint sequenceLength = max(spp * 64u, 64u); + uint sequenceIndex = (sampleIndex + (gFrameIndex & 63u) * spp) % sequenceLength; + + float rand = Hash12( + (float2)pixel + + worldPos.xy * 0.013 + + float2(pathNormal.x * 19.19, pathNormal.y * 37.37)); + + return Hammersley2D(sequenceIndex, sequenceLength, rand); +} + +float3 TraceOneIndirectBouncePath(uint2 pixel, float3 worldPos, float3 N, float3 V, float3 baseAlbedo, uint sampleIndex, inout uint rng) { const float BOUNCE_TMAX = 1000000.0; @@ -4393,7 +4429,10 @@ float3 TraceOneIndirectBouncePath(uint2 pixel, float3 worldPos, float3 N, float3 [loop] for (uint depth = 0; depth < maxIndirectDepth; ++depth) { - float3 bounceDir = SampleCosineWorld(pathNormal, rng); + float2 bounceXi = (depth == 0u) + ? GetStratifiedBounceXi(pixel, pathPos, pathNormal, sampleIndex) + : Rand2(rng); + float3 bounceDir = SampleCosineWorldXi(pathNormal, bounceXi); float NoD = saturate(dot(pathNormal, bounceDir)); float normalBias = lerp(gShadowBias * 3.0, gShadowBias * 0.75, NoD); @@ -4456,6 +4495,7 @@ float3 TraceOneIndirectBouncePath(uint2 pixel, float3 worldPos, float3 N, float3 bouncedRadiance += skyTint * 0.045; } + bouncedRadiance = CompressEmissiveRadiance(bouncedRadiance, depth == 0u ? 8.0 : 5.0); accum += throughput * bouncedRadiance; if ((hitGeoFlag & GEOMETRY_FLAG_UNLIT) != 0u) @@ -4487,14 +4527,14 @@ float3 TraceOneIndirectBouncePath(uint2 pixel, float3 worldPos, float3 N, float3 return accum; } -float3 EstimatePathTracedIndirectBounce(uint2 pixel, float3 worldPos, float3 N, float3 V, float3 baseAlbedo, inout uint rng) +float3 EstimatePathTracedIndirectBounce(uint2 pixel, float3 worldPos, float3 N, float3 V, float3 baseAlbedo, uint sampleIndex, inout uint rng) { if (gMaxBounces <= 1u) return 0.0; // One stochastic diffuse path per lighting sample. The path can contain // several diffuse depths, while the temporal pass below handles convergence. - float3 accum = TraceOneIndirectBouncePath(pixel, worldPos, N, V, baseAlbedo, rng); + float3 accum = TraceOneIndirectBouncePath(pixel, worldPos, N, V, baseAlbedo, sampleIndex, rng); const float INDIRECT_STRENGTH = 0.72; return accum * INDIRECT_STRENGTH; @@ -4714,7 +4754,8 @@ float ComputeSpecularReflectionDistanceFade(float hitT, float maxT) return fade * fade; } - +)" +R"( float3 EstimateRayTracedSpecularReflection( uint2 pixel, float3 worldPos, @@ -4804,6 +4845,7 @@ float3 EstimateRayTracedSpecularReflection( if (hasGBufferMaterial) { float3 hitV = SafeNormalizeOr(-R, V); + uint stableHitRng = InitRng(hitPixel, 0u, 0x5EECu); reflectedRadiance = EstimateDirectLightingForBounceHit( hitPixel, hitPos, @@ -4811,7 +4853,7 @@ float3 EstimateRayTracedSpecularReflection( hitV, hitAlbedo, hitGeoFlag, - rng); + stableHitRng); // Reflected glow maps should be visible in the reflection, but still // remain direct radiance; this does not turn emissive into GI. @@ -4827,6 +4869,8 @@ float3 EstimateRayTracedSpecularReflection( } } + reflectedRadiance = CompressEmissiveRadiance(reflectedRadiance, hasSpecularMap ? 5.0 : 3.5); + float cavityFade = lerp(0.42, 1.0, saturate(cavity)); float reflectionStrength = hasSpecularMap ? saturate(specPeak * 1.35) @@ -4968,6 +5012,7 @@ void RayGen() N, V, baseAlbedo, + s, rng); } @@ -4975,7 +5020,7 @@ void RayGen() lightingAccum += ApplyPrimaryDiffusePost(indirectLighting, ao, microShadow, isSkeletal); } - uint reflectionRng = InitRng(pixel, gFrameIndex, 0x5EECu); + uint reflectionRng = InitRng(pixel, 0u, 0x5EECu); float3 reflectedSpecular = EstimateRayTracedSpecularReflection( pixel, worldPos, @@ -5334,9 +5379,9 @@ float3 SafeNormalTemporal(float3 n) float3 ClampHistoryToCurrent(float3 history, float3 current) { - // Loose temporal clamp: it removes GI fireflies and old lighting while still - // allowing bright muzzle-flash/door-light changes to appear in a few frames. - float3 radius = 0.20 + abs(current) * 0.55; + // Clamp enough to kill rare GI/volume fireflies before they enter history, + // while still allowing real dynamic light changes to pull the accumulator. + float3 radius = 0.12 + abs(current) * 0.42; return clamp(history, current - radius, current + radius); } @@ -5359,7 +5404,7 @@ void TemporalAccumCS(uint3 dispatchThreadId : SV_DispatchThreadID) } float4 history = gHistoryTex.Load(int3(pixel, 0)); - float historyCount = (gFrameIndex == 0u) ? 0.0 : clamp(history.a, 0.0, 31.0); + float historyCount = (gFrameIndex == 0u) ? 0.0 : clamp(history.a, 0.0, 63.0); if (historyCount <= 0.0) { @@ -5374,14 +5419,14 @@ void TemporalAccumCS(uint3 dispatchThreadId : SV_DispatchThreadID) float histLum = LuminanceTemporal(historyColor); float relChange = abs(rawLum - histLum) / max(max(rawLum, histLum), 0.08); - // Base accumulation approaches 32 frames, but large lighting changes raise + // Base accumulation approaches 64 frames, but large lighting changes raise // current-frame weight so the accumulator does not leave obvious trails. - float currentWeight = max(1.0 / (historyCount + 1.0), 0.055); - currentWeight = max(currentWeight, saturate(relChange * 0.28)); + float currentWeight = max(1.0 / (historyCount + 1.0), 0.035); + currentWeight = max(currentWeight, saturate(relChange * 0.24)); currentWeight = saturate(currentWeight); float3 resolved = lerp(historyColor, max(raw.rgb, 0.0), currentWeight); - float nextCount = min(historyCount + 1.0, 31.0); + float nextCount = min(historyCount + 1.0, 63.0); gTemporalOutTex[pixel] = float4(resolved, raw.a); gHistoryOutTex[pixel] = float4(resolved, nextCount); @@ -7323,4 +7368,4 @@ void glRaytracingShowAllInstances(void) for (int i = 0; i < GL_RAYTRACING_MAX_RENDER_WORLDS; ++i) glRaytracingSetAllInstancesVisibleUnlocked(&g_glRaytracingScene.worlds[i], 1); -} \ No newline at end of file +} diff --git a/neo/engine/opengl/gl_d3d12shim.cpp b/neo/engine/opengl/gl_d3d12shim.cpp index 86b8a442..bc1ac044 100644 --- a/neo/engine/opengl/gl_d3d12shim.cpp +++ b/neo/engine/opengl/gl_d3d12shim.cpp @@ -1298,7 +1298,7 @@ struct GLState bool motionHistoryReset = true; QD3D12UpscalerBackend upscalerBackend = QD3D12_UPSCALER_DLSS; - QD3D12UpscalerQuality upscalerQuality = QD3D12_QUALITY_PERFORMANCE; + QD3D12UpscalerQuality upscalerQuality = QD3D12_QUALITY_DLAA; bool enableInternalTAA = true; bool enableRayAIDenoise = false; bool enableDLSSRayReconstruction = true; @@ -2513,7 +2513,7 @@ cbuffer DrawCB : register(b0) // Pixel-shader POM depth in UV space. Keep this conservative: legacy idTech // normal maps often do not have a true height channel, so the POM path below // gates fallback height by local detail before applying any offset. -#define gParallaxScale (clamp(gNormalMapStrength, 0.0, 4.0) * 0.026) +#define gParallaxScale (clamp(gNormalMapStrength, 0.0, 4.0) * 0.020) #define gCameraWorldPos gCameraPomPad.xyz #define gCameraPomValid gCameraPomPad.w #define gUseNeuralPOM gNeuralPomPad.x @@ -2777,6 +2777,14 @@ float3 QD3D12_DecodeTangentSpaceNormal(float3 encodedNormal) return QD3D12_SafeNormalize(n, float3(0.0, 0.0, 1.0)); } +float3 QD3D12_DecodeTangentSpaceNormalScaled(float3 encodedNormal, float strengthScale) +{ + float3 n = encodedNormal * 2.0 - 1.0; + n.y *= gNormalMapYSign; + n.xy *= max(gNormalMapStrength, 0.0) * saturate(strengthScale); + return QD3D12_SafeNormalize(n, float3(0.0, 0.0, 1.0)); +} + float QD3D12_HeightFromNormalSample(float4 nm) { // Many legacy normal maps are RGB/DXT uploads with an implicitly forced @@ -2883,7 +2891,8 @@ void QD3D12_BuildPixelTBN(VSOut i, out float3 n, out float3 t, out float3 b) t = derivedT; b = derivedB; } - +)HLSL" +R"HLSL( float QD3D12_AuthoredPomAlphaWeight(float alphaValue) { // Alpha == 1 is frequently forced by RGB/DXT uploads in this shim. Treat only @@ -2993,6 +3002,29 @@ float QD3D12_GetPomConfidence(float2 uv) return confidence * confidence * (3.0 - 2.0 * confidence); } +float QD3D12_GetRegularPomFade(VSOut i, float2 baseUv) +{ + if (gUseNormalMap < 0.5) + return 0.0; + + float confidence = QD3D12_GetPomConfidence(baseUv); + if (confidence <= 0.03) + return 0.0; + + float3 n, t, b; + QD3D12_BuildPixelTBN(i, n, t, b); + + float3 rawViewWS = (gCameraPomValid >= 0.5) ? (gCameraWorldPos - i.worldPos) : (-i.worldPos); + float3 viewWS = QD3D12_SafeNormalize(rawViewWS, n); + float NoV = saturate(dot(n, viewWS)); + + float viewDistance = (gCameraPomValid >= 0.5) ? max(length(gCameraWorldPos - i.worldPos), 1.0) : max(abs(i.currClip.w), 1.0); + float distanceFade = 1.0 - smoothstep(QD3D12_POM_DISTANCE_NEAR, QD3D12_POM_DISTANCE_FAR, viewDistance); + float grazingFade = smoothstep(0.10, 0.26, NoV); + + return saturate(confidence * distanceFade * grazingFade); +} + // Keep the runtime network bounded. Most payloads do not need the full 128-wide // MLP in a fixed-function material pass; clamping evaluation here prevents a @@ -3269,7 +3301,7 @@ float2 QD3D12_ComputeParallaxUVWithNeural(VSOut i, float2 baseUv, QD3D12NeuralPO return baseUv; float confidence = QD3D12_GetPomConfidence(baseUv); - if (confidence <= 0.03) + if (confidence <= 0.05) return baseUv; float parallaxScale = gParallaxScale * confidence; @@ -3320,7 +3352,7 @@ float2 QD3D12_ComputeParallaxUVWithNeural(VSOut i, float2 baseUv, QD3D12NeuralPO // authored alpha height. float2 parallaxVector = (viewTS.xy / vz) * scale; float parallaxLen = length(parallaxVector); - float maxParallaxShift = lerp(0.024, 0.058, confidence); + float maxParallaxShift = lerp(0.016, 0.040, confidence); if (parallaxLen > maxParallaxShift && parallaxLen > 1e-6) parallaxVector *= maxParallaxShift / parallaxLen; @@ -3358,9 +3390,10 @@ float2 QD3D12_ComputeParallaxUVWithNeural(VSOut i, float2 baseUv, QD3D12NeuralPO // Blend the final result in instead of applying a manual half-vector center // correction. The center correction was the source of several texture-offset // bugs on flat/low-confidence areas. - return lerp(baseUv, refinedUv, saturate(distanceFade * grazingFade)); + return lerp(baseUv, refinedUv, saturate(distanceFade * grazingFade * confidence)); } - +)HLSL" +R"HLSL( float2 QD3D12_ComputeParallaxUV(VSOut i, float2 baseUv) { QD3D12NeuralPOMResult nr = QD3D12_EvaluateNeuralPOM(i, baseUv); @@ -3515,7 +3548,11 @@ float4 BuildSpecularAlbedoCached(VSOut i, QD3D12MaterialEval m) { if (gUseSpecularMap > 0.0) { - float4 spec = gSpecularMap.Sample(gSamp4, m.uv0); + float regularPomFade = (gUseNeuralPOM <= 0.5 || m.neural.active <= 0.5) + ? QD3D12_GetRegularPomFade(i, i.uv0) + : 0.0; + float2 specUv = lerp(i.uv0, m.uv0, regularPomFade); + float4 spec = gSpecularMap.Sample(gSamp4, specUv); float strength = max(gSpecularMapStrength, 0.0); // RGB-only legacy spec maps are uploaded with forced opaque alpha. For real @@ -3526,6 +3563,8 @@ float4 BuildSpecularAlbedoCached(VSOut i, QD3D12MaterialEval m) bool alphaLooksForcedOpaque = (spec.a >= 0.999); float alphaMask = alphaLooksForcedOpaque ? 1.0 : saturate(spec.a); float3 specRgb = saturate(spec.rgb) * alphaMask * strength; + if (regularPomFade > 0.0) + specRgb = min(specRgb, float3(0.82, 0.82, 0.82)); return float4(specRgb, 1.0); } @@ -3573,7 +3612,12 @@ float3 BuildGBufferNormalCached(VSOut i, QD3D12MaterialEval m) float3 t, b; QD3D12_BuildPixelTBN(i, n, t, b); - float3 tangentNormal = QD3D12_DecodeTangentSpaceNormal(gNormalMap.Sample(gSamp2, m.uv0).xyz); + float regularPomFade = (gUseNeuralPOM <= 0.5 || m.neural.active <= 0.5) + ? QD3D12_GetRegularPomFade(i, i.uv0) + : 0.0; + float3 tangentBase = QD3D12_DecodeTangentSpaceNormalScaled(gNormalMap.Sample(gSamp2, i.uv0).xyz, 1.0); + float3 tangentPom = QD3D12_DecodeTangentSpaceNormalScaled(gNormalMap.Sample(gSamp2, m.uv0).xyz, lerp(0.70, 1.0, regularPomFade)); + float3 tangentNormal = QD3D12_SafeNormalize(lerp(tangentBase, tangentPom, regularPomFade), tangentBase); return QD3D12_SafeNormalize( tangentNormal.x * t + diff --git a/neo/prey/game/anim/Anim.h b/neo/prey/game/anim/Anim.h index 6f1374e9..524c3260 100644 --- a/neo/prey/game/anim/Anim.h +++ b/neo/prey/game/anim/Anim.h @@ -49,12 +49,6 @@ typedef struct { int firstComponent; } jointAnimInfo_t; -typedef struct { - jointHandle_t num; - jointHandle_t parentNum; - int channel; -} jointInfo_t; - // // joint modifier modes. make sure to change script/doom_defs.script if you add any, or change their order. //