I know i#m late but i manege to pull this of by uisng a custom script
setup like this heres the script
// ============================================================
// NATURAL LOW-BITRATE / BLOCKY COLOR COMPRESSION
// UE 5.6 CUSTOM NODE
//
// INPUT:
// SceneColor : Float3
//
// OUTPUT:
// Float3
//
// Kein CRT
// Keine Scanlines
// Kein Chroma-Bleeding
// Weniger harte 8x8-Kompression
// Blockige Farbquantisierung bleibt deutlich sichtbar
// ============================================================
float2 UV = GetDefaultSceneTextureUV(Parameters, 14);
float2 InvRes = View.ViewSizeAndInvSize.zw;
float2 Pixel = UV * View.ViewSizeAndInvSize.xy;
float3 Original = saturate(SceneColor);
// ============================================================
// 8x8 COMPRESSION BLOCK
// ============================================================
float2 Block =
floor(Pixel / 8.0);
float2 BlockUV =
(Block * 8.0 + 4.0) * InvRes;
// ============================================================
// BLOCK SAMPLES
// ============================================================
float3 B0 =
SceneTextureLookup(
BlockUV,
14,
false
).rgb;
float3 B1 =
SceneTextureLookup(
BlockUV + float2(2.0 * InvRes.x, 0.0),
14,
false
).rgb;
float3 B2 =
SceneTextureLookup(
BlockUV - float2(2.0 * InvRes.x, 0.0),
14,
false
).rgb;
float3 B3 =
SceneTextureLookup(
BlockUV + float2(0.0, 2.0 * InvRes.y),
14,
false
).rgb;
float3 B4 =
SceneTextureLookup(
BlockUV - float2(0.0, 2.0 * InvRes.y),
14,
false
).rgb;
// Block color
float3 BlockColor =
B0 * 0.40
- B1 * 0.15
- B2 * 0.15
- B3 * 0.15
- B4 * 0.15;
// ============================================================
// RGB → YCbCr
// ============================================================
float Y =
dot(
Original,
float3(
0.299,
0.587,
0.114
)
);
float Cb =
dot(
Original,
float3(
-0.168736,
-0.331264,
0.5
)
);
float Cr =
dot(
Original,
float3(
0.5,
-0.418688,
-0.081312
)
);
// ============================================================
// BLOCK YCbCr
// ============================================================
float BY =
dot(
BlockColor,
float3(
0.299,
0.587,
0.114
)
);
float BCb =
dot(
BlockColor,
float3(
-0.168736,
-0.331264,
0.5
)
);
float BCr =
dot(
BlockColor,
float3(
0.5,
-0.418688,
-0.081312
)
);
// ============================================================
// BLOCK RANDOMNESS
// ============================================================
float BlockHash =
frac(
sin(
dot(
Block,
float2(
127.1,
311.7
)
)
)
* 43758.5453
);
// unterschiedliche Kompression pro Block
float Damage =
lerp(
0.35,
1.0,
BlockHash
);
// ============================================================
// CHROMA QUANTIZATION
// ============================================================
float ChromaLevels =
lerp(
25.0,
14.0,
Damage
);
float QCb =
floor(
BCb * ChromaLevels + 0.5
)
/ ChromaLevels;
float QCr =
floor(
BCr * ChromaLevels + 0.5
)
/ ChromaLevels;
// ============================================================
// LUMA QUANTIZATION
// ============================================================
float LumaLevels =
lerp(
58.0,
34.0,
Damage
);
float QY =
floor(
BY * LumaLevels + 0.5
)
/ LumaLevels;
// ============================================================
// QUANTIZATION ERRORS
// ============================================================
float CbError =
QCb - Cb;
float CrError =
QCr - Cr;
float YError =
QY - Y;
// ============================================================
// EDGE DETECTION
// ============================================================
float3 Left =
SceneTextureLookup(
UV - float2(2.0 * InvRes.x, 0.0),
14,
false
).rgb;
float3 Right =
SceneTextureLookup(
UV + float2(2.0 * InvRes.x, 0.0),
14,
false
).rgb;
float3 Up =
SceneTextureLookup(
UV - float2(0.0, 2.0 * InvRes.y),
14,
false
).rgb;
float3 Down =
SceneTextureLookup(
UV + float2(0.0, 2.0 * InvRes.y),
14,
false
).rgb;
float Edge =
length(Right - Left)
Edge =
saturate(
Edge * 2.0
);
// ============================================================
// LOCAL BLOCK VARIATION
// ============================================================
float LocalVariation =
length(B0 - B1)
- length(B0 - B2)
- length(B0 - B3)
- length(B0 - B4);
LocalVariation =
saturate(
LocalVariation * 2.0
);
// ============================================================
// COMPRESSION MASK
//
// Bewusst reduziert:
// weniger sichtbare harte 8x8-Blöcke,
// aber immer noch genug für Low-Bitrate-Look.
// ============================================================
float Artifact =
saturate(
0.20
- LocalVariation * 0.38
- Edge * 0.30
);
Artifact *= Damage;
// ============================================================
// BLOCK COLOR RECONSTRUCTION
// ============================================================
float3 BlockCompressed;
BlockCompressed.r =
Y
BlockCompressed.g =
Y
- 0.344136 *
(Cb + CbError)
- 0.714136 *
(Cr + CrError);
BlockCompressed.b =
Y
// ============================================================
// APPLY BLOCK COLOR ERROR
// ============================================================
float3 ResultColor =
lerp(
Original,
BlockCompressed,
Artifact * 0.62
);
// ============================================================
// BLOCKY COLOR ERROR
//
// Kleine farbige Quantisierungsfehler.
// ============================================================
float ColorError =
(BlockHash - 0.5)
* Artifact
* 0.065;
ResultColor.r +=
ColorError;
ResultColor.g -=
ColorError * 0.45;
ResultColor.b +=
ColorError * 0.75;
// ============================================================
// LUMA COMPRESSION
// ============================================================
ResultColor +=
YError
* Artifact
* 0.30;
// ============================================================
// 16x16 SECONDARY COMPRESSION PATCHES
//
// Größere unregelmäßige Farbflächen.
// ============================================================
float2 BigBlock =
floor(
Pixel / 16.0
);
float BigHash =
frac(
sin(
dot(
BigBlock,
float2(
269.5,
183.3
)
)
)
* 43758.5453
);
float Patch =
smoothstep(
0.48,
0.78,
BigHash
);
float PatchError =
(BigHash - 0.5)
* Patch
* 0.020;
ResultColor.r +=
PatchError;
ResultColor.g -=
PatchError * 0.45;
ResultColor.b +=
PatchError * 0.65;
// ============================================================
// SUBTLE BLOCK EDGE RINGING
// ============================================================
float3 FarLeft =
SceneTextureLookup(
UV - float2(5.0 * InvRes.x, 0.0),
14,
false
).rgb;
float3 FarRight =
SceneTextureLookup(
UV + float2(5.0 * InvRes.x, 0.0),
14,
false
).rgb;
float FarYL =
dot(
FarLeft,
float3(
0.299,
0.587,
0.114
)
);
float FarYR =
dot(
FarRight,
float3(
0.299,
0.587,
0.114
)
);
float Ring =
(
FarYL
Ring =
clamp(
Ring,
-0.035,
0.035
);
ResultColor +=
Ring
* Edge
* 0.28;
// ============================================================
// BLOCKY POSTERIZATION
//
// Absichtlich NICHT glatt.
// Aber wesentlich dezenter als die Kompressionsfehler.
// ============================================================
float3 QuantizedColor =
floor(
saturate(ResultColor)
* 42.0
ResultColor =
lerp(
ResultColor,
QuantizedColor,
0.22
);
// ============================================================
// FINAL COMPRESSION MIX
// ============================================================
float CompressionStrength =
0.86;
float3 FinalResult =
lerp(
Original,
ResultColor,
CompressionStrength
);
// ============================================================
// OUTPUT
// ============================================================
return saturate(FinalResult); make sure to make input scene color for custom node
in the end it look smtng like this hope tht helps.
PS:its the artifacts are more visible in dark areas.