mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-19 04:04:54 +02:00
Added lit particle emissive support.
This commit is contained in:
@@ -3899,45 +3899,55 @@ float3 LoadEmissiveRadianceClamped(int2 p)
|
||||
{
|
||||
int2 maxPixel = int2((int)gScreenSize.x - 1, (int)gScreenSize.y - 1);
|
||||
p = clamp(p, int2(0, 0), maxPixel);
|
||||
return CompressEmissiveRadiance(gEmissiveTex.Load(int3(p, 0)).rgb, 7.50);
|
||||
float3 radiance = CompressEmissiveRadiance(gEmissiveTex.Load(int3(p, 0)).rgb, 18.0);
|
||||
return min(radiance + sqrt(max(radiance, 0.0)) * 0.35, 22.0);
|
||||
}
|
||||
|
||||
float3 EstimateEmissiveBloomAtPixel(uint2 pixel)
|
||||
{
|
||||
int2 p = int2(pixel);
|
||||
|
||||
// Strong threshold-free bloom. This stays direct/bloom-only: no extra
|
||||
// TraceRay calls, and no emissive lighting contribution. The goal is to make
|
||||
// visible glow maps read as hot emissive surfaces in the DXR output.
|
||||
// Strong threshold-free bloom in the ray output. Particle emissive lighting
|
||||
// is injected separately below; this is the visible camera halo.
|
||||
float3 bloom = 0.0;
|
||||
bloom += LoadEmissiveRadianceClamped(p) * 0.180;
|
||||
bloom += LoadEmissiveRadianceClamped(p) * 0.320;
|
||||
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 1, 0)) * 0.220;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2(-1, 0)) * 0.220;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 0, 1)) * 0.220;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 0, -1)) * 0.220;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 1, 0)) * 0.420;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2(-1, 0)) * 0.420;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 0, 1)) * 0.420;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 0, -1)) * 0.420;
|
||||
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 2, 2)) * 0.135;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2(-2, 2)) * 0.135;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 2, -2)) * 0.135;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2(-2, -2)) * 0.135;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 2, 2)) * 0.260;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2(-2, 2)) * 0.260;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 2, -2)) * 0.260;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2(-2, -2)) * 0.260;
|
||||
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 4, 0)) * 0.090;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2(-4, 0)) * 0.090;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 0, 4)) * 0.090;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 0, -4)) * 0.090;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 4, 0)) * 0.180;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2(-4, 0)) * 0.180;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 0, 4)) * 0.180;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 0, -4)) * 0.180;
|
||||
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 8, 0)) * 0.060;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2(-8, 0)) * 0.060;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 0, 8)) * 0.060;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 0, -8)) * 0.060;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 8, 0)) * 0.120;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2(-8, 0)) * 0.120;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 0, 8)) * 0.120;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 0, -8)) * 0.120;
|
||||
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 14, 0)) * 0.035;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2(-14, 0)) * 0.035;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 0, 14)) * 0.035;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 0, -14)) * 0.035;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 14, 0)) * 0.075;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2(-14, 0)) * 0.075;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 0, 14)) * 0.075;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 0, -14)) * 0.075;
|
||||
|
||||
return bloom * 0.78;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 24, 0)) * 0.045;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2(-24, 0)) * 0.045;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 0, 24)) * 0.045;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 0, -24)) * 0.045;
|
||||
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 42, 0)) * 0.025;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2(-42, 0)) * 0.025;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 0, 42)) * 0.025;
|
||||
bloom += LoadEmissiveRadianceClamped(p + int2( 0, -42)) * 0.025;
|
||||
|
||||
return min(bloom * 1.45, 28.0);
|
||||
}
|
||||
|
||||
float3 SafeNormalizeOr(float3 v, float3 fallback)
|
||||
@@ -3948,6 +3958,103 @@ float3 SafeNormalizeOr(float3 v, float3 fallback)
|
||||
return v * rsqrt(lenSq);
|
||||
}
|
||||
|
||||
bool TryScreenParticleWorldCandidate(float4 clip, float3 receiverPos, inout float bestDistSq, inout float3 bestWorld)
|
||||
{
|
||||
float4 worldA = mul(clip, gInvViewProj);
|
||||
if (abs(worldA.w) > 1e-6)
|
||||
{
|
||||
float3 candidate = worldA.xyz / worldA.w;
|
||||
float distSq = dot(candidate - receiverPos, candidate - receiverPos);
|
||||
if (distSq < bestDistSq && distSq > 1e-4 && distSq < 147456.0)
|
||||
{
|
||||
bestDistSq = distSq;
|
||||
bestWorld = candidate;
|
||||
}
|
||||
}
|
||||
|
||||
float4 worldB = mul(gInvViewProj, clip);
|
||||
if (abs(worldB.w) > 1e-6)
|
||||
{
|
||||
float3 candidate = worldB.xyz / worldB.w;
|
||||
float distSq = dot(candidate - receiverPos, candidate - receiverPos);
|
||||
if (distSq < bestDistSq && distSq > 1e-4 && distSq < 147456.0)
|
||||
{
|
||||
bestDistSq = distSq;
|
||||
bestWorld = candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return bestDistSq < 147456.0;
|
||||
}
|
||||
|
||||
bool TryReconstructScreenParticleWorld(uint2 particlePixel, float particleDepth, float3 receiverPos, out float3 particleWorld)
|
||||
{
|
||||
float2 uv = ((float2)particlePixel + 0.5) / max(gScreenSize.xy, float2(1.0, 1.0));
|
||||
float x = uv.x * 2.0 - 1.0;
|
||||
float yUp = 1.0 - uv.y * 2.0;
|
||||
float yDown = uv.y * 2.0 - 1.0;
|
||||
|
||||
float bestDistSq = 1.0e30;
|
||||
particleWorld = receiverPos;
|
||||
TryScreenParticleWorldCandidate(float4(x, yUp, particleDepth, 1.0), receiverPos, bestDistSq, particleWorld);
|
||||
TryScreenParticleWorldCandidate(float4(x, yDown, particleDepth, 1.0), receiverPos, bestDistSq, particleWorld);
|
||||
return bestDistSq < 147456.0;
|
||||
}
|
||||
|
||||
float3 EstimateScreenSpaceEmissiveParticleLighting(uint2 pixel, float3 worldPos, float3 N)
|
||||
{
|
||||
static const int2 kParticleLightTaps[17] =
|
||||
{
|
||||
int2( 0, 0),
|
||||
int2( 7, 0), int2( -7, 0), int2( 0, 7), int2( 0, -7),
|
||||
int2( 10, 10), int2(-10, 10), int2( 10, -10), int2(-10, -10),
|
||||
int2( 18, 4), int2(-18, -4), int2( 4, 18), int2( -4, -18),
|
||||
int2( 30, 0), int2(-30, 0), int2( 0, 30), int2( 0, -30)
|
||||
};
|
||||
|
||||
int2 maxPixel = int2((int)gScreenSize.x - 1, (int)gScreenSize.y - 1);
|
||||
float pixelScale = max(1.0, round(min(gScreenSize.x, gScreenSize.y) / 720.0));
|
||||
float3 lighting = 0.0;
|
||||
|
||||
[unroll]
|
||||
for (uint i = 0u; i < 17u; ++i)
|
||||
{
|
||||
int2 sp = int2(pixel) + int2(round((float2)kParticleLightTaps[i] * pixelScale));
|
||||
sp = clamp(sp, int2(0, 0), maxPixel);
|
||||
|
||||
float4 particleEmission = gEmissiveTex.Load(int3(sp, 0));
|
||||
if (particleEmission.a <= 1.5)
|
||||
continue;
|
||||
|
||||
float particleDepth = saturate(particleEmission.a - 2.0);
|
||||
float3 particleWorld;
|
||||
if (!TryReconstructScreenParticleWorld(uint2(sp), particleDepth, worldPos, particleWorld))
|
||||
continue;
|
||||
|
||||
float3 toParticle = particleWorld - worldPos;
|
||||
float distSq = dot(toParticle, toParticle);
|
||||
if (distSq <= 1e-4 || distSq >= 147456.0)
|
||||
continue;
|
||||
|
||||
float dist = sqrt(distSq);
|
||||
float3 L = toParticle / dist;
|
||||
float NoL = saturate(dot(N, L));
|
||||
if (NoL <= 0.01)
|
||||
continue;
|
||||
|
||||
float visibility = TraceVisibilityBiased(worldPos, N, L, dist);
|
||||
if (visibility <= 0.0)
|
||||
continue;
|
||||
|
||||
float distanceFade = saturate(1.0 - dist / 640.0);
|
||||
distanceFade *= distanceFade;
|
||||
float attenuation = distanceFade / (1.0 + distSq * 0.000075);
|
||||
lighting += CompressEmissiveRadiance(particleEmission.rgb, 24.0) * (NoL * visibility * attenuation);
|
||||
}
|
||||
|
||||
return lighting * 9.0;
|
||||
}
|
||||
|
||||
bool ProjectClipToGBufferCandidate(
|
||||
float4 clipPos,
|
||||
bool flipY,
|
||||
@@ -4151,7 +4258,8 @@ float3 PathTraceDirectPointLight(uint2 pixel, float3 worldPos, float3 N, float3
|
||||
specularOut = specAccum * invSamples;
|
||||
return diffuseAccum * invSamples;
|
||||
}
|
||||
|
||||
)"
|
||||
R"(
|
||||
float3 PathTraceDirectSpotLight(float3 worldPos, float3 N, float3 V, float3 baseAlbedo, float3 specularAlbedo, Light Lgt, inout uint rng, out float3 specularOut)
|
||||
{
|
||||
specularOut = 0.0;
|
||||
@@ -4175,7 +4283,8 @@ float3 PathTraceDirectSpotLight(float3 worldPos, float3 N, float3 V, float3 base
|
||||
|
||||
return Lgt.color * (Lgt.intensity * atten * diffuseNoL * shadow);
|
||||
}
|
||||
|
||||
)"
|
||||
R"(
|
||||
float3 PathTraceDirectRectLight(uint2 pixel, float3 worldPos, float3 N, float3 V, float3 baseAlbedo, float3 specularAlbedo, Light Lgt, inout uint rng, out float3 specularOut)
|
||||
{
|
||||
specularOut = 0.0;
|
||||
@@ -5304,6 +5413,7 @@ void RayGen()
|
||||
skyVis,
|
||||
ambientSkyVis,
|
||||
specularAccum);
|
||||
lightingAccum += EstimateScreenSpaceEmissiveParticleLighting(pixel, worldPos, N);
|
||||
|
||||
// Only the indirect bounce path uses per-SPP randomness now. Direct lights,
|
||||
// AO, sky visibility, cavity, and final gather are deterministic and should
|
||||
|
||||
Reference in New Issue
Block a user