106 lines
3.9 KiB
HLSL

#ifndef BEAUTIFY_PPS_FILM_GRAIN
#define BEAUTIFY_PPS_FILM_GRAIN
float4 _FilmGrainData; // x: intensity, y: luma attenuation, z: resolution, w: unused
float4 _FilmArtifactsData; // x: dirt spots amount, y: dirt spots intensity, z: scratches amount, w: scratches intensity
#define FILM_GRAIN_INTENSITY _FilmGrainData.x
#define FILM_GRAIN_LUMA_ATTENUATION _FilmGrainData.y
#define FILM_GRAIN_RESOLUTION _FilmGrainData.z
#define DIRT_SPOTS_AMOUNT _FilmArtifactsData.x
#define DIRT_SPOTS_INTENSITY _FilmArtifactsData.y
#define SCRATCHES_AMOUNT _FilmArtifactsData.z
#define SCRATCHES_INTENSITY _FilmArtifactsData.w
float3 r3(float2 uv) {
static const float2 magic1 = float2(321.8942, 1225.6548);
static const float magic2 = 4251.4865;
float d1 = dot(uv, magic1);
float d2 = dot(uv + 0.1, magic1);
float d3 = dot(uv + 0.2, magic1);
return frac(sin(float3(d1, d2, d3)) * magic2 + _Time.y);
}
inline float r1(float x) {
static const float magic = 4251.4865;
return frac(sin(x * 321.8942) * magic + _Time.y);
}
// Generate film artifacts like dirt spots and scratches
float getFilmArtifacts(float2 uv) {
float artifacts = 0.0;
// Dirt spots - small circular spots that appear randomly
UNITY_BRANCH
if (DIRT_SPOTS_AMOUNT > 0) {
float2 dirtUV = uv * 20.0;
float2 dirtCell = floor(dirtUV);
float2 dirtFrac = dirtUV - dirtCell;
float3 dirtRandom = r3(dirtCell + floor(_Time.y * 0.5));
if (dirtRandom.x > DIRT_SPOTS_AMOUNT) {
float2 dirtOffset = dirtFrac - 0.5;
float dirtDistanceSqr = dot(dirtOffset, dirtOffset);
float dirtSize = dirtRandom.y * 0.3 + 0.1;
float dirtSpot = 1.0 - smoothstep(0.0, dirtSize * dirtSize, dirtDistanceSqr);
artifacts += dirtSpot * (dirtRandom.z - 0.5) * DIRT_SPOTS_INTENSITY;
}
}
// Vertical scratches - appear occasionally and persist for a few frames
UNITY_BRANCH
if (SCRATCHES_AMOUNT > 0) {
float scratchTime = floor(dot(_Time.xyzw, 1.0));
float scratchRandom = r1(scratchTime);
if (scratchRandom > SCRATCHES_AMOUNT) {
float scratchX = r1(scratchTime + 0.1);
float scratchWidth = 0.001 + r1(scratchTime + 0.2) * 0.003;
float scratchDistance = abs(uv.x - scratchX);
float scratch = 1.0 - smoothstep(0.0, scratchWidth, scratchDistance);
artifacts += scratch * SCRATCHES_INTENSITY;
}
}
return artifacts;
}
void ApplyFilmGrain(inout float3 rgb, float luma, float2 uv) {
float2 noiseSize = _MainTex_TexelSize.zw * FILM_GRAIN_RESOLUTION;
float2 scaledUV = uv * noiseSize;
float2 cell = floor(scaledUV);
#if BEAUTIFY_TURBO
static const float c0 = 1.0f;
static const float c1 = -1.828427f;
static const float c2 = 0.828427f;
float luminanceFactor = c0 + luma * (c1 + luma * c2);
float3 grain = r3(cell / noiseSize);
float artifacts = 0.0;
#else
float luminanceFactor = 1.0 - sqrt(luma);
// Bilinear interpolation
float2 fracUV = scaledUV - cell;
float2 invNoiseSize = rcp(noiseSize);
float3 rand00 = r3(cell * invNoiseSize);
float3 rand10 = r3((cell + float2(1, 0)) * invNoiseSize);
float3 rand01 = r3((cell + float2(0, 1)) * invNoiseSize);
float3 rand11 = r3((cell + float2(1, 1)) * invNoiseSize);
float3 randX0 = lerp(rand00, rand10, fracUV.x);
float3 randX1 = lerp(rand01, rand11, fracUV.x);
float3 grain = lerp(randX0, randX1, fracUV.y);
float artifacts = getFilmArtifacts(uv);
#endif
static const float3 channelSensibility = float3(1, 0.95, 1.05);
grain = (grain - 0.5) * channelSensibility;
float grainAmount = lerp(1.0, luminanceFactor, FILM_GRAIN_LUMA_ATTENUATION);
rgb *= 1.0 + grain * grainAmount * FILM_GRAIN_INTENSITY + artifacts;
rgb = saturate(rgb);
}
#endif // BEAUTIFY_PPS_FILM_GRAIN