Fix DDGI iradiance to use debanding by applying quantization error to reduce yellowish artifacts due to R11G11B10 format
This commit is contained in:
@@ -620,6 +620,40 @@ void RenderTools::ComputeSphereModelDrawMatrix(const RenderView& view, const Flo
|
||||
resultIsViewInside = Float3::DistanceSquared(view.Position, position) < Math::Square(radius * 1.1f); // Manually tweaked bias
|
||||
}
|
||||
|
||||
Float3 RenderTools::GetColorQuantizationError(PixelFormat format)
|
||||
{
|
||||
Float3 mantissaBits;
|
||||
switch (format)
|
||||
{
|
||||
case PixelFormat::R11G11B10_Float:
|
||||
mantissaBits = Float3(6, 6, 5);
|
||||
break;
|
||||
case PixelFormat::R10G10B10A2_UNorm:
|
||||
mantissaBits = Float3(10, 10, 10);
|
||||
break;
|
||||
case PixelFormat::R16G16B16A16_Float:
|
||||
mantissaBits = Float3(16, 16, 16);
|
||||
break;
|
||||
case PixelFormat::R32G32B32A32_Float:
|
||||
mantissaBits = Float3(23, 23, 23);
|
||||
break;
|
||||
case PixelFormat::R9G9B9E5_SharedExp:
|
||||
mantissaBits = Float3(5, 6, 5);
|
||||
break;
|
||||
case PixelFormat::R8G8B8A8_UNorm:
|
||||
case PixelFormat::B8G8R8A8_UNorm:
|
||||
mantissaBits = Float3(8, 8, 8);
|
||||
break;
|
||||
default:
|
||||
return Float3::Zero;
|
||||
}
|
||||
return {
|
||||
Math::Pow(0.5f, mantissaBits.X),
|
||||
Math::Pow(0.5f, mantissaBits.Y),
|
||||
Math::Pow(0.5f, mantissaBits.Z)
|
||||
};
|
||||
}
|
||||
|
||||
int32 MipLevelsCount(int32 width)
|
||||
{
|
||||
int32 result = 1;
|
||||
|
||||
@@ -140,6 +140,9 @@ public:
|
||||
static void CalculateTangentFrame(Float3& resultNormal, Float4& resultTangent, const Float3& normal, const Float3& tangent);
|
||||
|
||||
static void ComputeSphereModelDrawMatrix(const RenderView& view, const Float3& position, float radius, Matrix& resultWorld, bool& resultIsViewInside);
|
||||
|
||||
// Calculates error for a given render target format to reduce floating-point precision artifacts via QuantizeColor (from Noise.hlsl).
|
||||
static Float3 GetColorQuantizationError(PixelFormat format);
|
||||
};
|
||||
|
||||
// Calculates mip levels count for a texture 1D.
|
||||
|
||||
@@ -68,6 +68,8 @@ GPU_CB_STRUCT(Data0 {
|
||||
Int4 ProbeScrollClears[4];
|
||||
Float3 ViewDir;
|
||||
float Padding1;
|
||||
Float3 QuantizationError;
|
||||
int32 FrameIndexMod8;
|
||||
});
|
||||
|
||||
GPU_CB_STRUCT(Data1 {
|
||||
@@ -535,6 +537,8 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont
|
||||
data.TemporalTime = renderContext.List->Setup.UseTemporalAAJitter ? RenderTools::ComputeTemporalTime() : 0.0f;
|
||||
data.ViewDir = renderContext.View.Direction;
|
||||
data.SkyboxIntensity = renderContext.List->Sky ? renderContext.List->Sky->GetIndirectLightingIntensity() : 1.0f;
|
||||
data.QuantizationError = RenderTools::GetColorQuantizationError(ddgiData.ProbesIrradiance->Format());
|
||||
data.FrameIndexMod8 = (int32)(Engine::FrameCount % 8);
|
||||
GBufferPass::SetInputs(renderContext.View, data.GBuffer);
|
||||
context->UpdateCB(_cb0, &data);
|
||||
context->BindCB(0, _cb0);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "./Flax/Math.hlsl"
|
||||
#include "./Flax/Noise.hlsl"
|
||||
#include "./Flax/Quaternion.hlsl"
|
||||
#include "./Flax/MonteCarlo.hlsl"
|
||||
#include "./Flax/GlobalSignDistanceField.hlsl"
|
||||
#include "./Flax/GI/GlobalSurfaceAtlas.hlsl"
|
||||
#include "./Flax/GI/DDGI.hlsl"
|
||||
@@ -42,6 +43,8 @@ float TemporalTime;
|
||||
int4 ProbeScrollClears[4];
|
||||
float3 ViewDir;
|
||||
float Padding1;
|
||||
float3 QuantizationError;
|
||||
uint FrameIndexMod8;
|
||||
META_CB_END
|
||||
|
||||
META_CB_BEGIN(1, Data1)
|
||||
@@ -704,6 +707,9 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_
|
||||
result = float4(lerp(result.rg, previous.rg, historyWeight), 0.0f, 1.0f);
|
||||
#endif
|
||||
|
||||
// Write output irradiance (apply quantization error to reduce yellowish artifacts due to R11G11B10 format)
|
||||
float noise = InterleavedGradientNoise(octahedralCoords, FrameIndexMod8);
|
||||
result.rgb = QuantizeColor(result.rgb, noise, QuantizationError);
|
||||
RWOutput[outputCoords] = result;
|
||||
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
|
||||
@@ -54,6 +54,26 @@ float2 PerlinNoiseFade(float2 t)
|
||||
return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
|
||||
}
|
||||
|
||||
// "Next Generation Post Processing in Call of Duty: Advanced Warfare"
|
||||
// http://advances.realtimerendering.com/s2014/index.html
|
||||
float InterleavedGradientNoise(float2 uv, uint frameCount)
|
||||
{
|
||||
const float2 magicFrameScale = float2(47, 17) * 0.695;
|
||||
uv += frameCount * magicFrameScale;
|
||||
const float3 magic = float3(0.06711056, 0.00583715, 52.9829189);
|
||||
return frac(magic.z * frac(dot(uv, magic.xy)));
|
||||
}
|
||||
|
||||
// Removes error from the color to properly store it in lower precision formats (error = 2^(-mantissaBits))
|
||||
float3 QuantizeColor(float3 color, float noise, float3 error)
|
||||
{
|
||||
float3 delta = color * error;
|
||||
delta.x = asfloat(asuint(delta.x) & ~0x007fffff);
|
||||
delta.y = asfloat(asuint(delta.y) & ~0x007fffff);
|
||||
delta.z = asfloat(asuint(delta.z) & ~0x007fffff);
|
||||
return color + delta * noise;
|
||||
}
|
||||
|
||||
float rand2dTo1d(float2 value, float2 dotDir = float2(12.9898, 78.233))
|
||||
{
|
||||
// https://www.ronja-tutorials.com/post/024-white-noise/
|
||||
|
||||
Reference in New Issue
Block a user