POM Relief Mapping

This commit is contained in:
Justin Marshall
2026-05-24 18:27:11 -07:00
parent 26dd7e649e
commit 754f5b4aa0
2 changed files with 508 additions and 152 deletions
+258 -48
View File
@@ -2643,7 +2643,7 @@ struct Light
// The scalar radius above is still kept as a max/fallback range for point lights,
// as the influence range for rect lights, and as the far clip distance for spot lights.
float3 pointRadius;
float pointRadiusPad; // non-zero disables specular for this light
float pointRadiusPad;
uint iesTextureIndex; // 0 = none, 1..8 = gIesTextures index + 1
float iesStrength;
@@ -3745,7 +3745,7 @@ float EstimateSpecularRoughness(float3 specularAlbedo)
// 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.70, 0.42, peak), 0.38, 0.74);
return clamp(lerp(0.66, 0.40, peak), 0.36, 0.72);
}
float3 LimitSpecularPeak(float3 c, float peakLimit)
@@ -3757,7 +3757,7 @@ float3 LimitSpecularPeak(float3 c, float peakLimit)
return c;
}
float3 ComputeSpecular(
float3 ComputeSpecularShaped(
float3 N,
float3 V,
float3 L,
@@ -3765,7 +3765,13 @@ float3 ComputeSpecular(
float lightIntensity,
float atten,
float shadow,
float3 specularAlbedo)
float3 specularAlbedo,
float roughnessBias,
float specularWrap,
float f0Blend,
float fresnelToWhiteScale,
float energyScale,
float peakLimit)
{
if (gEnableSpecular == 0)
return 0.0;
@@ -3777,7 +3783,10 @@ float3 ComputeSpecular(
V = normalize(V);
L = normalize(L);
float NoL = saturate(dot(N, L));
float rawNoL = dot(N, L);
float NoL = (specularWrap > 0.0)
? saturate((rawNoL + specularWrap) / (1.0 + specularWrap))
: saturate(rawNoL);
float NoV = saturate(dot(N, V));
if (NoL <= 1.0e-4 || NoV <= 1.0e-4)
@@ -3795,7 +3804,7 @@ float3 ComputeSpecular(
if (specPeak <= 0.001)
return 0.0;
float roughness = EstimateSpecularRoughness(specMask);
float roughness = clamp(EstimateSpecularRoughness(specMask) + roughnessBias, 0.38, 0.90);
float a = roughness * roughness;
float a2 = max(a * a, 1.0e-4);
@@ -3813,11 +3822,11 @@ float3 ComputeSpecular(
float Gl = NoL / max(NoL * (1.0 - k) + k, 1.0e-4);
float G = Gv * Gl;
float3 F0 = saturate(lerp(float3(0.025, 0.025, 0.025), specMask, 0.58));
float3 F = F0 + (1.0 - F0) * pow(1.0 - VoH, 5.0);
float3 F0 = saturate(lerp(float3(0.025, 0.025, 0.025), specMask, saturate(f0Blend)));
float3 F = F0 + (1.0 - F0) * (pow(1.0 - VoH, 5.0) * saturate(fresnelToWhiteScale));
float specTerm = (D * G) / max(4.0 * NoL * NoV, 1.0e-4);
specTerm = min(specTerm, 3.0);
specTerm = min(specTerm, 3.6);
const float SPECULAR_ENERGY_SCALE = 2.35;
@@ -3829,9 +3838,155 @@ float3 ComputeSpecular(
NoL *
F *
specTerm *
SPECULAR_ENERGY_SCALE;
(SPECULAR_ENERGY_SCALE * max(energyScale, 0.0));
return LimitSpecularPeak(specular, 4.0);
return LimitSpecularPeak(specular, peakLimit);
}
float3 ComputeSpecular(
float3 N,
float3 V,
float3 L,
float3 lightColor,
float lightIntensity,
float atten,
float shadow,
float3 specularAlbedo)
{
return ComputeSpecularShaped(
N,
V,
L,
lightColor,
lightIntensity,
atten,
shadow,
specularAlbedo,
-0.04,
0.12,
0.82,
1.0,
1.42,
4.8);
}
float EstimateSkeletalSubsurfaceMask(float3 baseAlbedo, float3 specularAlbedo)
{
// Doom 3 characters have no authored SSS/thickness maps. Use a conservative
// skin/flesh proxy from the visible albedo and damp it on shiny/metal-like
// pieces so armor and gear do not glow like wax.
baseAlbedo = saturate(baseAlbedo);
float luma = dot(baseAlbedo, float3(0.299, 0.587, 0.114));
float warm = saturate((baseAlbedo.r - baseAlbedo.b * 0.55) * 2.35);
float redBias = saturate((baseAlbedo.r - max(baseAlbedo.g, baseAlbedo.b)) * 3.25 + 0.18);
float fleshRange = smoothstep(0.035, 0.18, luma) * (1.0 - smoothstep(0.82, 1.0, luma));
float matte = 1.0 - saturate(SpecularPeak3(specularAlbedo) * 0.85);
return saturate((0.08 + warm * 0.42 + redBias * 0.38) * fleshRange * lerp(0.55, 1.0, matte));
}
float3 ComputeSkeletalSpecularAlbedo(float3 baseAlbedo, float3 specularAlbedo)
{
float subsurfaceMask = EstimateSkeletalSubsurfaceMask(baseAlbedo, specularAlbedo);
// Old character spec maps were authored for Doom 3's stylized interaction
// lights. In the path tracer they behave like wet plastic unless we make the
// BRDF much softer and cap the F0 on skin/flesh-like areas.
float3 spec = saturate(specularAlbedo);
float3 softSpec = spec * lerp(0.48, 0.24, subsurfaceMask);
float cap = lerp(0.16, 0.075, subsurfaceMask);
return min(softSpec, float3(cap, cap, cap));
}
float3 ComputeSkeletalSpecular(
float3 N,
float3 V,
float3 L,
float3 baseAlbedo,
float3 lightColor,
float lightIntensity,
float atten,
float shadow,
float3 specularAlbedo)
{
float subsurfaceMask = EstimateSkeletalSubsurfaceMask(baseAlbedo, specularAlbedo);
float3 characterSpec = ComputeSkeletalSpecularAlbedo(baseAlbedo, specularAlbedo);
// Character MD5 specular maps often encode art-directed shine, not material
// F0. Shape the whole BRDF, including grazing Fresnel, so faces and bodies
// keep a soft oily skin response instead of reflecting like sealed plastic.
return ComputeSpecularShaped(
N,
V,
L,
lightColor,
lightIntensity,
atten,
shadow,
characterSpec,
lerp(0.10, 0.20, subsurfaceMask),
0.0,
0.58,
lerp(0.32, 0.14, subsurfaceMask),
lerp(0.40, 0.24, subsurfaceMask),
lerp(0.55, 0.18, subsurfaceMask));
}
float ComputeSubsurfaceShape(float3 N, float3 V, float3 L)
{
N = normalize(N);
V = normalize(V);
L = normalize(L);
float NoL = dot(N, L);
float wrap = saturate((NoL + 0.58) / 1.58);
wrap = wrap * wrap;
// Transmission becomes visible around the terminator and when the light is
// behind the viewed surface, which is the bit missing most painfully on MD5s.
float back = saturate((-NoL + 0.22) / 1.22);
float forward = pow(saturate(dot(-L, V)), 2.25);
float rim = pow(saturate(1.0 - abs(dot(N, V))), 1.60);
return saturate(wrap * 0.42 + back * (0.55 + forward * 0.85) + rim * wrap * 0.18);
}
float3 ComputeSkeletalSubsurfaceRadiance(
float3 N,
float3 V,
float3 L,
float3 baseAlbedo,
float3 lightColor,
float lightIntensity,
float atten,
float shadow,
float subsurfaceMask)
{
if (subsurfaceMask <= 0.001 || atten <= 0.0 || lightIntensity <= 0.0)
return 0.0;
float shape = ComputeSubsurfaceShape(N, V, L);
if (shape <= 0.001)
return 0.0;
float softShadow = lerp(shadow, 1.0, 0.18 * subsurfaceMask);
float3 scatterTint = saturate(lerp(
float3(1.0, 0.30, 0.18),
saturate(baseAlbedo) * float3(1.22, 0.68, 0.52) + float3(0.10, 0.03, 0.02),
0.58));
const float SUBSURFACE_DIRECT_SCALE = 0.34;
return clamp(
lightColor *
lightIntensity *
atten *
softShadow *
scatterTint *
shape *
(subsurfaceMask * SUBSURFACE_DIRECT_SCALE),
0.0,
2.25);
}
)"
R"(
@@ -4255,10 +4410,12 @@ float3 EstimatePathTracedSky(float3 worldPos, float3 N, inout uint rng)
accum /= (float)bounceSamples;
return accum * 0.55;
}
float3 PathTraceDirectPointLight(uint2 pixel, float3 worldPos, float3 N, float3 V, float3 baseAlbedo, float3 specularAlbedo, Light Lgt, inout uint rng, out float3 specularOut)
)"
R"(
float3 PathTraceDirectPointLight(uint2 pixel, float3 worldPos, float3 N, float3 V, float3 baseAlbedo, float3 specularAlbedo, bool isSkeletal, float subsurfaceMask, Light Lgt, inout uint rng, out float3 specularOut, out float3 subsurfaceOut)
{
specularOut = 0.0;
subsurfaceOut = 0.0;
float3 toCenter = Lgt.position - worldPos;
float centerDist = length(toCenter);
@@ -4284,6 +4441,7 @@ float3 PathTraceDirectPointLight(uint2 pixel, float3 worldPos, float3 N, float3
float3 diffuseAccum = 0.0;
float3 specAccum = 0.0;
float3 sssAccum = 0.0;
[loop]
for (uint s = 0u; s < sampleCount; ++s)
@@ -4303,24 +4461,28 @@ float3 PathTraceDirectPointLight(uint2 pixel, float3 worldPos, float3 N, float3
float diffuseNoL = ComputeDiffuseLightingTerm(N, L);
float shadow = 1.0;
if (Lgt.samples != 0u && diffuseNoL > 0.0001)
if (Lgt.samples != 0u && (diffuseNoL > 0.0001 || subsurfaceMask > 0.001))
shadow = TraceVisibilityBiased(worldPos, N, L, dist);
if (Lgt.pointRadiusPad <= 0.5)
specAccum += ComputeSpecular(N, V, L, Lgt.color, Lgt.intensity, atten, shadow, specularAlbedo);
specAccum += isSkeletal
? ComputeSkeletalSpecular(N, V, L, baseAlbedo, Lgt.color, Lgt.intensity, atten, shadow, specularAlbedo)
: ComputeSpecular(N, V, L, Lgt.color, Lgt.intensity, atten, shadow, specularAlbedo);
sssAccum += ComputeSkeletalSubsurfaceRadiance(N, V, L, baseAlbedo, Lgt.color, Lgt.intensity, atten, shadow, subsurfaceMask);
diffuseAccum += Lgt.color * (Lgt.intensity * atten * diffuseNoL * shadow);
}
float invSamples = 1.0 / (float)sampleCount;
specularOut = specAccum * invSamples;
subsurfaceOut = sssAccum * 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)
float3 PathTraceDirectSpotLight(float3 worldPos, float3 N, float3 V, float3 baseAlbedo, float3 specularAlbedo, bool isSkeletal, float subsurfaceMask, Light Lgt, inout uint rng, out float3 specularOut, out float3 subsurfaceOut)
{
specularOut = 0.0;
subsurfaceOut = 0.0;
float3 toLight = Lgt.position - worldPos;
float dist = length(toLight);
@@ -4333,19 +4495,22 @@ float3 PathTraceDirectSpotLight(float3 worldPos, float3 N, float3 V, float3 base
float diffuseNoL = ComputeDiffuseLightingTerm(N, L);
float shadow = 1.0;
if (Lgt.samples != 0u && diffuseNoL > 0.0001 && atten > 0.0)
if (Lgt.samples != 0u && (diffuseNoL > 0.0001 || subsurfaceMask > 0.001) && atten > 0.0)
shadow = TraceVisibilityBiased(worldPos, N, L, dist);
if (Lgt.pointRadiusPad <= 0.5)
specularOut = ComputeSpecular(N, V, L, Lgt.color, Lgt.intensity, atten, shadow, specularAlbedo);
specularOut = isSkeletal
? ComputeSkeletalSpecular(N, V, L, baseAlbedo, Lgt.color, Lgt.intensity, atten, shadow, specularAlbedo)
: ComputeSpecular(N, V, L, Lgt.color, Lgt.intensity, atten, shadow, specularAlbedo);
subsurfaceOut = ComputeSkeletalSubsurfaceRadiance(N, V, L, baseAlbedo, Lgt.color, Lgt.intensity, atten, shadow, subsurfaceMask);
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)
float3 PathTraceDirectRectLight(uint2 pixel, float3 worldPos, float3 N, float3 V, float3 baseAlbedo, float3 specularAlbedo, bool isSkeletal, float subsurfaceMask, Light Lgt, inout uint rng, out float3 specularOut, out float3 subsurfaceOut)
{
specularOut = 0.0;
subsurfaceOut = 0.0;
float3 toCenter = Lgt.position - worldPos;
float centerDist = length(toCenter);
@@ -4365,6 +4530,7 @@ float3 PathTraceDirectRectLight(uint2 pixel, float3 worldPos, float3 N, float3 V
float3 diffuseAccum = 0.0;
float3 specAccum = 0.0;
float3 sssAccum = 0.0;
[loop]
for (uint s = 0u; s < sampleCount; ++s)
@@ -4385,7 +4551,8 @@ float3 PathTraceDirectRectLight(uint2 pixel, float3 worldPos, float3 N, float3 V
float3 L = sampleVec / sampleDist;
float NdotL = ComputeDiffuseLightingTerm(N, L);
if (NdotL <= 0.0)
float sssShape = (subsurfaceMask > 0.001) ? ComputeSubsurfaceShape(N, V, L) : 0.0;
if (NdotL <= 0.0 && sssShape <= 0.001)
continue;
float faceTerm = (Lgt.twoSided != 0)
@@ -4399,9 +4566,18 @@ float3 PathTraceDirectRectLight(uint2 pixel, float3 worldPos, float3 N, float3 V
if (Lgt.samples != 0u)
shadow = TraceVisibilityBiased(worldPos, N, L, sampleDist);
if (Lgt.pointRadiusPad <= 0.5)
{
specAccum += ComputeSpecular(
specAccum += (isSkeletal
? ComputeSkeletalSpecular(
N,
V,
L,
baseAlbedo,
Lgt.color,
Lgt.intensity * faceTerm,
1.0,
shadow,
specularAlbedo)
: ComputeSpecular(
N,
V,
L,
@@ -4409,14 +4585,25 @@ float3 PathTraceDirectRectLight(uint2 pixel, float3 worldPos, float3 N, float3 V
Lgt.intensity * faceTerm,
1.0,
shadow,
specularAlbedo) * atten;
}
specularAlbedo)) * atten;
sssAccum += ComputeSkeletalSubsurfaceRadiance(
N,
V,
L,
baseAlbedo,
Lgt.color,
Lgt.intensity * faceTerm,
atten,
shadow,
subsurfaceMask);
diffuseAccum += clamp(Lgt.color * (Lgt.intensity * NdotL * faceTerm * atten * shadow), 0.0, 4.0);
}
float invSamples = 1.0 / (float)sampleCount;
specularOut = specAccum * invSamples;
subsurfaceOut = sssAccum * invSamples;
return diffuseAccum * invSamples;
}
@@ -4618,29 +4805,31 @@ float3 EstimateUnresolvedBounceRadiance(float3 hitPos, float3 hitN, float3 albed
return clamp(albedo * max(lighting, 0.0), 0.0, 6.0);
}
float3 EstimateShadowedBounceLight(uint2 hitPixel, float3 hitPos, float3 hitN, float3 hitV, float3 hitAlbedo, Light Lgt, inout uint rng)
float3 EstimateShadowedBounceLight(uint2 hitPixel, float3 hitPos, float3 hitN, float3 hitV, float3 hitAlbedo, bool hitIsSkeletal, Light Lgt, inout uint rng)
{
float3 spec = 0.0;
float3 sss = 0.0;
float3 diffuse = 0.0;
float3 hitSpecularAlbedo = LoadSceneSpecularAlbedo(hitPixel, hitAlbedo);
float subsurfaceMask = hitIsSkeletal ? EstimateSkeletalSubsurfaceMask(hitAlbedo, hitSpecularAlbedo) * 0.45 : 0.0;
// Use the same visibility-capable direct-light samplers as the primary hit.
// This supplies proper next-event estimation at secondary hits instead of the
// old unoccluded light-list approximation.
if (Lgt.type == GL_RAYTRACING_LIGHT_TYPE_POINT)
{
diffuse = PathTraceDirectPointLight(hitPixel, hitPos, hitN, hitV, hitAlbedo, hitSpecularAlbedo, Lgt, rng, spec);
diffuse = PathTraceDirectPointLight(hitPixel, hitPos, hitN, hitV, hitAlbedo, hitSpecularAlbedo, hitIsSkeletal, subsurfaceMask, Lgt, rng, spec, sss);
}
else if (Lgt.type == GL_RAYTRACING_LIGHT_TYPE_SPOT)
{
diffuse = PathTraceDirectSpotLight(hitPos, hitN, hitV, hitAlbedo, hitSpecularAlbedo, Lgt, rng, spec);
diffuse = PathTraceDirectSpotLight(hitPos, hitN, hitV, hitAlbedo, hitSpecularAlbedo, hitIsSkeletal, subsurfaceMask, Lgt, rng, spec, sss);
}
else if (Lgt.type == GL_RAYTRACING_LIGHT_TYPE_RECT)
{
diffuse = PathTraceDirectRectLight(hitPixel, hitPos, hitN, hitV, hitAlbedo, hitSpecularAlbedo, Lgt, rng, spec);
diffuse = PathTraceDirectRectLight(hitPixel, hitPos, hitN, hitV, hitAlbedo, hitSpecularAlbedo, hitIsSkeletal, subsurfaceMask, Lgt, rng, spec, sss);
}
return clamp(diffuse, 0.0, 12.0);
return clamp(diffuse + sss * 0.35, 0.0, 12.0);
}
float3 EstimateBounceSkyLighting(float3 hitPos, float3 hitN, inout uint rng)
@@ -4731,7 +4920,7 @@ float3 EstimateDirectLightingForBounceHit(uint2 hitPixel, float3 hitPos, float3
uint lightIndex = (start + c * stride) % gLightCount;
Light Lgt = gLights[lightIndex];
float3 fast = EstimateFastBounceLight(hitPos, hitN, Lgt);
float3 shadowed = EstimateShadowedBounceLight(hitPixel, hitPos, hitN, hitV, hitAlbedo, Lgt, lightingRng);
float3 shadowed = EstimateShadowedBounceLight(hitPixel, hitPos, hitN, hitV, hitAlbedo, hitIsSkeletal, Lgt, lightingRng);
lighting += shadowed - fast;
}
}
@@ -5056,7 +5245,10 @@ float3 ApplyPrimarySpecularPost(float3 specularAccum, float ao, bool isSkeletal)
// surfaces. Keep a mild occlusion tint, but let direct-light specular read.
float specAo = lerp(0.58, 1.0, saturate(ao));
if (isSkeletal)
{
specAo = lerp(specAo, 1.0, 0.50);
specularAccum *= 0.42;
}
specularAccum *= specAo;
//if (isSkeletal)
@@ -5077,9 +5269,11 @@ float3 PathTraceDeterministicLighting(
float ao,
float skyVis,
float ambientSkyVis,
out float3 specularAccum)
out float3 specularAccum,
out float3 subsurfaceAccum)
{
specularAccum = 0.0;
subsurfaceAccum = 0.0;
float microShadow = lerp(0.75, 1.0, cavity);
@@ -5104,32 +5298,37 @@ float3 PathTraceDeterministicLighting(
// argument only because the functions share the same signature as stochastic
// helpers; it is not consumed by PathTraceDirect* in the current shader.
uint directRng = InitRng(pixel, 0u, 0xD17EC7u);
float subsurfaceMask = isSkeletal ? EstimateSkeletalSubsurfaceMask(baseAlbedo, specularAlbedo) : 0.0;
[loop]
for (uint i = 0; i < gLightCount; ++i)
{
Light Lgt = gLights[i];
float3 spec = 0.0;
float3 sss = 0.0;
float3 diffuse = 0.0;
if (Lgt.type == GL_RAYTRACING_LIGHT_TYPE_POINT)
{
diffuse = PathTraceDirectPointLight(pixel, worldPos, N, V, baseAlbedo, specularAlbedo, Lgt, directRng, spec);
diffuse = PathTraceDirectPointLight(pixel, worldPos, N, V, baseAlbedo, specularAlbedo, isSkeletal, subsurfaceMask, Lgt, directRng, spec, sss);
}
else if (Lgt.type == GL_RAYTRACING_LIGHT_TYPE_SPOT)
{
diffuse = PathTraceDirectSpotLight(worldPos, N, V, baseAlbedo, specularAlbedo, Lgt, directRng, spec);
diffuse = PathTraceDirectSpotLight(worldPos, N, V, baseAlbedo, specularAlbedo, isSkeletal, subsurfaceMask, Lgt, directRng, spec, sss);
}
else if (Lgt.type == GL_RAYTRACING_LIGHT_TYPE_RECT)
{
diffuse = PathTraceDirectRectLight(pixel, worldPos, N, V, baseAlbedo, specularAlbedo, Lgt, directRng, spec);
diffuse = PathTraceDirectRectLight(pixel, worldPos, N, V, baseAlbedo, specularAlbedo, isSkeletal, subsurfaceMask, Lgt, directRng, spec, sss);
}
lightingAccum += diffuse;
specularAccum += spec;
subsurfaceAccum += sss;
}
specularAccum = ApplyPrimarySpecularPost(specularAccum, ao, isSkeletal);
if (isSkeletal)
subsurfaceAccum *= lerp(0.72, 1.0, saturate(ao));
return ApplyPrimaryDiffusePost(lightingAccum, ao, microShadow, isSkeletal);
}
@@ -5193,7 +5392,7 @@ float ComputeSpecularReflectionLocalEnergy(float3 worldPos, float3 N)
continue;
float3 toLight = SafeNormalizeOr(Lgt.position - worldPos, N);
float facing = saturate(dot(N, toLight));
float facing = saturate((dot(N, toLight) + 0.18) / 1.18);
float lightPeak = max(max(Lgt.color.r, Lgt.color.g), Lgt.color.b);
energy += atten * facing * Lgt.intensity * lightPeak;
@@ -5201,7 +5400,7 @@ float ComputeSpecularReflectionLocalEnergy(float3 worldPos, float3 N)
// Keep reflections tied to nearby light contribution instead of becoming
// a global mirror pass.
return saturate(energy * 0.08);
return saturate(energy * 0.12);
}
float ComputeSpecularReflectionMaxDistance(float3 worldPos)
@@ -5264,11 +5463,18 @@ float3 EstimateRayTracedSpecularReflection(
float3 baseAlbedo,
float3 specularAlbedo,
float cavity,
bool isSkeletal,
inout uint rng)
{
if (gEnableSpecular == 0u || gMaxBounces <= 1u)
return 0.0;
// Character materials should get soft direct specular, not mirror-like local
// reflections. The G-buffer material model has no skeletal roughness map, so
// reflected TLAS hits make skin/armor read much wetter than intended.
if (isSkeletal)
return 0.0;
N = SafeNormalizeOr(N, float3(0.0, 0.0, 1.0));
V = SafeNormalizeOr(V, -N);
@@ -5280,7 +5486,7 @@ float3 EstimateRayTracedSpecularReflection(
bool hasSpecularMap = LooksLikeAuthoredSpecularSample(rawSpecular, baseAlbedo);
float specPeak = SpecularPeak3(specularAlbedo);
if (specPeak <= 0.015)
if (specPeak <= 0.006)
return 0.0;
float NoV = saturate(dot(N, V));
@@ -5319,8 +5525,8 @@ float3 EstimateRayTracedSpecularReflection(
{
// Do not reflect sky forever. A missed finite reflection ray contributes
// only a tiny local glossy sheen.
reflectedRadiance = GetSkyRadiance(R) * 0.035;
distanceFade = 0.20;
reflectedRadiance = GetSkyRadiance(R) * 0.055;
distanceFade = 0.28;
}
else
{
@@ -5373,13 +5579,13 @@ float3 EstimateRayTracedSpecularReflection(
}
reflectedRadiance *= reflectedSurfaceAverageColor;
reflectedRadiance = CompressEmissiveRadiance(reflectedRadiance, hasSpecularMap ? 5.0 : 3.5);
reflectedRadiance = CompressEmissiveRadiance(reflectedRadiance, hasSpecularMap ? 3.5 : 2.5);
float cavityFade = lerp(0.42, 1.0, saturate(cavity));
float reflectionStrength = hasSpecularMap
? saturate(specPeak * 1.35)
: saturate((specPeak - 0.10) * 0.65);
float reflectionScale = hasSpecularMap ? 0.90 : 0.55;
? saturate(specPeak * 1.30)
: saturate((specPeak - 0.08) * 0.70);
float reflectionScale = hasSpecularMap ? 0.85 : 0.52;
if (reflectionStrength <= 0.001)
return 0.0;
@@ -5393,7 +5599,7 @@ float3 EstimateRayTracedSpecularReflection(
localReflectionEnergy *
distanceFade,
0.0,
3.5);
2.6);
}
)"
R"(
@@ -5488,6 +5694,7 @@ void RayGen()
}
float3 specularAccum = 0.0;
float3 subsurfaceAccum = 0.0;
float3 lightingAccum = PathTraceDeterministicLighting(
pixel,
worldPos,
@@ -5500,7 +5707,8 @@ void RayGen()
ao,
skyVis,
ambientSkyVis,
specularAccum);
specularAccum,
subsurfaceAccum);
lightingAccum += EstimateScreenSpaceEmissiveParticleLighting(pixel, worldPos, N);
// Only the indirect bounce path uses per-SPP randomness now. Direct lights,
@@ -5540,6 +5748,7 @@ void RayGen()
baseAlbedo,
specularAlbedo,
cavity,
isSkeletal,
reflectionRng);
float3 albedo = baseAlbedo * cavity;
@@ -5554,6 +5763,7 @@ void RayGen()
float outputAo = isSkeletal ? lerp(ao, 1.0, 0.55) : ao;
finalColor *= clamp(outputAo + 0.3, 0.0, 1.0);
finalColor += subsurfaceAccum;
// Volumetric light scattering is radiance in the camera ray, not surface
// reflectance, so add it after surface albedo/specular composition. Because