Skip to content

Commit

Permalink
feat: HuePreservingReinhard
Browse files Browse the repository at this point in the history
  • Loading branch information
doodlum committed Nov 22, 2024
1 parent a06b6f2 commit bb3637c
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 1 deletion.
35 changes: 35 additions & 0 deletions package/Shaders/Common/DICETonemapper.hlsli
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,38 @@ float3 HuePreservingHejlBurgessDawson(float3 col)

return col;
}

float3 HuePreservingReinhard(float3 col)
{
float3 ictcp = RGBToICtCp(col);

// Hue-preserving range compression requires desaturation in order to achieve a natural look. We adaptively desaturate the input based on its luminance.
float saturationAmount = pow(smoothstep(1.0, 0.3, ictcp.x), 1.3);
col = ICtCpToRGB(ictcp * float3(1, saturationAmount.xx));

// Hue preserving mapping
float maxCol = Color::RGBToLuminance(col);
float mappedMax = GetTonemapFactorReinhard(maxCol);
float3 compressedHuePreserving = col * mappedMax / maxCol;

// Non-hue preserving mapping
float3 perChannelCompressed = GetTonemapFactorReinhard(col);
// Compensate for modified tonemapping desaturating colors
perChannelCompressed = lerp(Color::RGBToLuminance(perChannelCompressed), perChannelCompressed, 1.5);

col = lerp(perChannelCompressed, compressedHuePreserving, 0.6);

float3 ictcpMapped = RGBToICtCp(col);

// Smoothly ramp off saturation as brightness increases, but keep some even for very bright input
float postCompressionSaturationBoost = 0.3 * smoothstep(1.0, 0.5, ictcp.x);

// Re-introduce some hue from the pre-compression color. Something similar could be accomplished by delaying the luma-dependent desaturation before range compression.
// Doing it here however does a better job of preserving perceptual luminance of highly saturated colors. Because in the hue-preserving path we only range-compress the max channel,
// saturated colors lose luminance. By desaturating them more aggressively first, compressing, and then re-adding some saturation, we can preserve their brightness to a greater extent.
ictcpMapped.yz = lerp(ictcpMapped.yz, ictcp.yz * ictcpMapped.x / max(1e-3, ictcp.x), postCompressionSaturationBoost);

col = ICtCpToRGB(ictcpMapped);

return col;
}
2 changes: 1 addition & 1 deletion package/Shaders/ISHDR.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ PS_OUTPUT main(PS_INPUT input)
if (Param.z > 0.5) {
blendedColor = HuePreservingHejlBurgessDawson(inputColor);
} else {
blendedColor = GetTonemapFactorReinhard(inputColor);
blendedColor = HuePreservingReinhard(inputColor);
}

blendedColor += saturate(Param.x - blendedColor) * bloomColor;
Expand Down

0 comments on commit bb3637c

Please sign in to comment.