Cel shading jitter solution

In PostProcessTonemap.usf the Grain is never set to zero even though all grain settings are zero.
This is the patch (additions in bold). Would be nice if Epic fixes this. Normally you don’t see it but it will be very annoying if you are dealing with cel shaders or other type of stylized shaders that are pixel sensitive and don’t want any jitter (you also disable antialiasing and voila). If you disable tonemapper altogether you don’t have access to contrast and sharpening.

half GrainFromUV(float2 GrainUV)
{
#if USE_GRAIN_JITTER
half Grain = frac(sin(GrainUV.x + GrainUV.y * 543.31) * 493013.0);
#else
half Grain = 0.0f;
#endif

return Grain;
}

1 Like

Just curious, did you make a PR for this?

In HairStrandsDeepShadowMask.usf, to remove jitter from strands shading, do:

float3 GetShadowMaskRandom(uint2 PixelPosition)
{
float3 Random = 0.5f;
if (Voxel_RandomType == 0)
{
Random = GetHairVoxelJitter(PixelPosition.xy, View.StateFrameIndexMod8);
}
else if (Voxel_RandomType == 1)
{
Random = ComputeRandom4_0(PixelPosition, View.StateFrameIndexMod8).xyz;
}
else if (Voxel_RandomType == 2)
{
//Random = ComputeRandom4_1(PixelPosition, View.StateFrameIndexMod8).xyz;
Random = ComputeRandom4_1(PixelPosition, 0).xyz;
}
return Random;
}

May also be needed in HairStrandsEnvironmentAO.usf:

float TraceAO(float3 WorldPosition, float3 WorldNormal, float2 SvPosition, float PixelRadius)
{
//const float3 VoxelOffset = VirtualVoxel.VoxelWorldSize * GetHairVoxelJitter(SvPosition, View.StateFrameIndexMod8);
const float3 VoxelOffset = VirtualVoxel.VoxelWorldSize * GetHairVoxelJitter(SvPosition, 0);

//const float4 Random4 = ComputeRandom4(SvPosition, SampleIt, SampleCount, View.StateFrameIndexMod8);
const float4 Random4 = 0;


for (uint SampleIt = 0; SampleIt < SampleCount; ++SampleIt)
{
//const float4 Random4 = ComputeRandom4(SvPosition, SampleIt, SampleCount, View.StateFrameIndexMod8);
const float4 Random4 = ComputeRandom4(SvPosition, SampleIt, SampleCount, 0);

HairStrandsMaterialGBufferPS.usf

//const float Dither = InterleavedGradientNoise(PixelCoord.xy, View.StateFrameIndexMod8);
const float Dither = InterleavedGradientNoise(PixelCoord.xy, 0);

HairScatter.usf

//const float3 SampleRandom = GetHairVoxelJitter(PixelCoord, View.StateFrameIndexMod8);
const float3 SampleRandom = GetHairVoxelJitter(PixelCoord,1);

HairStrandsDeepTransmittanceMask.usf

//const float3 SampleRandom = GetHairVoxelJitter(PixelCoord, View.StateFrameIndexMod8);
const float3 SampleRandom = GetHairVoxelJitter(PixelCoord, 0);

//Settings.Random = GetHairVoxelJitter(PixelCoord.xy, View.StateFrameIndexMod8);
Settings.Random = GetHairVoxelJitter(PixelCoord.xy,0);

HairStrandsEnvironmentLighting.usf

//const float3 VoxelOffset = GetHairVoxelJitter(SvPosition, View.StateFrameIndexMod8 + PrimitiveIdMod8);
const float3 VoxelOffset = GetHairVoxelJitter(SvPosition, 0 + PrimitiveIdMod8);

//const float4 Random4 = ComputeRandom4(PixelPosition, SampleIt, SampleCount, View.StateFrameIndexMod8);
const float4 Random4 = 0;

//const float4 Random4 = ComputeRandom4(PixelPosition, SampleIt, SampleCount, View.StateFrameIndexMod8);
const float4 Random4 = 0;

HairStrandsVoxelPageRayMarching.usf

//const float3 SampleRandom = GetHairVoxelJitter(PixelCoord, View.StateFrameIndexMod8);
const float3 SampleRandom = GetHairVoxelJitter(PixelCoord, 0);

HairScatter.usf

//const float3 TransmittanceRandom = float3(InterleavedGradientNoise(PixelCoord.xy, 1), InterleavedGradientNoise(PixelCoord.xy, 2), InterleavedGradientNoise(PixelCoord.xy, 3));
const float3 TransmittanceRandom = 0;

HairStrandsDeepTransmittanceMask.usf

// const float3 TransmittanceRandom = float3(InterleavedGradientNoise(PixelCoord, 1), InterleavedGradientNoise(PixelCoord, 2), InterleavedGradientNoise(PixelCoord, 3));

const float3 TransmittanceRandom = 0;

HairStrandsVoxelPageTraversal.usf

float3 GetHairVoxelJitter(uint2 PixelCoord, uint Seed)
{
Seed = 1;
return float3(
InterleavedGradientNoise(PixelCoord.xy, Seed),
InterleavedGradientNoise(PixelCoord.xy, Seed * 117),
InterleavedGradientNoise(PixelCoord.xy, Seed * 7901));
}

const float3 RandomStepJitter = 0;//InSettings.Random.xyz * 2 - 1;
for (float StepIt = 0.0f; StepIt < MaxStep; StepIt += StepScale)

HairStrandsVoxelRasterCompute.usf

float3 GetHairVoxelJitter(uint2 PixelCoord, uint Seed)
{
Seed = 1;
return float3(
InterleavedGradientNoise(PixelCoord.xy, Seed),
InterleavedGradientNoise(PixelCoord.xy, Seed * 117),
InterleavedGradientNoise(PixelCoord.xy, Seed * 7901));
}

#if JITTER_ENABLE
const float3 Jitter = 0;

HairStrandsEnvironmentLightingCommon.usf

float4 ComputeRandom4(uint2 PixelPosition, uint SampleIt, uint SampleCount, uint FrameIdMod8)
{
FrameIdMod8 = 0;

This takes care of jitter in contact shadows:

DeferredLightingCommon.usf

void GetShadowTerms(FGBufferData GBuffer, FDeferredLightData LightData, float3 WorldPosition, float3 L, float4 LightAttenuation, float Dither, inout FShadowTerms Shadow)
{
float ContactShadowLength = 0.0f;
const float ContactShadowLengthScreenScale = View.ClipToView[1][1] * GBuffer.Depth;
Dither = 0;