Skip to content
This repository was archived by the owner on Nov 30, 2020. It is now read-only.

Commit dbd40a2

Browse files
committed
Optimized the Grain effect and added a "Colored" option
PS4 numbers: - Old Fast grain : 152µs - Old Filmic grain : 464µs - New Filmic grain : 185µs As a result the "fast" variant has been dropped because of it's bad quality and is only slightly faster than the new grain.
1 parent 7f14acc commit dbd40a2

6 files changed

Lines changed: 227 additions & 38 deletions

File tree

PostProcessing/Editor/Models/GrainModelEditor.cs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,43 @@ namespace UnityEditor.PostProcessing
77
[PostProcessingModelEditor(typeof(GrainModel))]
88
public class GrainModelEditor : PostProcessingModelEditor
99
{
10-
SerializedProperty m_Mode;
11-
SerializedProperty m_Amount;
10+
SerializedProperty m_Colored;
11+
SerializedProperty m_Intensity;
12+
SerializedProperty m_WeightR;
13+
SerializedProperty m_WeightG;
14+
SerializedProperty m_WeightB;
1215
SerializedProperty m_Size;
1316
SerializedProperty m_LuminanceContribution;
1417

1518
public override void OnEnable()
1619
{
17-
m_Mode = FindSetting((Settings x) => x.mode);
18-
m_Amount = FindSetting((Settings x) => x.intensity);
20+
m_Colored = FindSetting((Settings x) => x.colored);
21+
m_Intensity = FindSetting((Settings x) => x.intensity);
22+
m_WeightR = FindSetting((Settings x) => x.weightR);
23+
m_WeightG = FindSetting((Settings x) => x.weightG);
24+
m_WeightB = FindSetting((Settings x) => x.weightB);
1925
m_Size = FindSetting((Settings x) => x.size);
2026
m_LuminanceContribution = FindSetting((Settings x) => x.luminanceContribution);
2127
}
2228

2329
public override void OnInspectorGUI()
2430
{
25-
EditorGUILayout.PropertyField(m_Mode);
26-
EditorGUILayout.PropertyField(m_Amount);
31+
EditorGUILayout.PropertyField(m_Intensity);
2732
EditorGUILayout.PropertyField(m_LuminanceContribution);
33+
EditorGUILayout.PropertyField(m_Size);
34+
EditorGUILayout.PropertyField(m_Colored);
2835

29-
if (m_Mode.intValue == (int)GrainModel.Mode.Filmic)
30-
EditorGUILayout.PropertyField(m_Size);
36+
if (m_Colored.boolValue)
37+
{
38+
EditorGUILayout.Space();
39+
EditorGUILayout.LabelField("Channel Weights", EditorStyles.boldLabel);
40+
41+
EditorGUI.indentLevel++;
42+
EditorGUILayout.PropertyField(m_WeightR, EditorGUIHelper.GetContent("Red"));
43+
EditorGUILayout.PropertyField(m_WeightG, EditorGUIHelper.GetContent("Green"));
44+
EditorGUILayout.PropertyField(m_WeightB, EditorGUIHelper.GetContent("Blue"));
45+
EditorGUI.indentLevel--;
46+
}
3147
}
3248
}
3349
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
Shader "Hidden/Post FX/Grain Generator"
2+
{
3+
CGINCLUDE
4+
5+
#include "UnityCG.cginc"
6+
#include "Common.cginc"
7+
8+
float4 _Params; // x: size, y: time, z: cos_angle, w: sin_angle
9+
10+
float3 Rnm(float2 tc, float time)
11+
{
12+
float noise = sin(dot(tc + time.xx, float2(12.9898, 78.233))) * 43758.5453;
13+
return frac(noise.xxx * float3(1.0, 1.2154, 1.3647)) * 2.0 - 1.0;
14+
}
15+
16+
float Fade(float t)
17+
{
18+
return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
19+
}
20+
21+
// 2d gradient noise
22+
float PNoise(float2 p, float time)
23+
{
24+
const float kPermTexUnit = 1.0 / 256.0;
25+
const float kPermTexUnitHalf = 0.5 / 256.0;
26+
27+
float2 pi = kPermTexUnit * floor(p) + kPermTexUnitHalf;
28+
float2 pf = frac(p);
29+
30+
float perm00 = Rnm(pi, time).z;
31+
float2 grad000 = Rnm(float2(perm00, kPermTexUnitHalf), time).xy * 4.0 - 1.0;
32+
float n000 = dot(grad000, pf);
33+
34+
float perm01 = Rnm(pi + float2(0.0, kPermTexUnit), time).z;
35+
half2 grad010 = Rnm(float2(perm01, kPermTexUnitHalf), time).xy * 4.0 - 1.0;
36+
float n010 = dot(grad010, pf - float2(0.0, 1.0));
37+
38+
float perm10 = Rnm(pi + float2(kPermTexUnit, 0.0), time).z;
39+
float2 grad100 = Rnm(float2(perm10, kPermTexUnitHalf), time).xy * 4.0 - 1.0;
40+
float n100 = dot(grad100, pf - float2(1.0, 0.0));
41+
42+
float perm11 = Rnm(pi + float2(kPermTexUnit, kPermTexUnit), time).z;
43+
float2 grad110 = Rnm(float2(perm11, kPermTexUnitHalf), time).xy * 4.0 - 1.0;
44+
float n110 = dot(grad110, pf - float2(1.0, 1.0));
45+
46+
float2 n_x = lerp(float2(n000, n010), float2(n100, n110), Fade(pf.x));
47+
48+
return lerp(n_x.x, n_x.y, Fade(pf.y));
49+
}
50+
51+
float2 CoordRot(float2 tc, float2 angle)
52+
{
53+
float s = angle.y;
54+
float c = angle.x;
55+
tc = tc * 2.0 - 1.0;
56+
float2 rot = float2((tc.x * c) - (tc.y * s), (tc.y * c) + (tc.x * s));
57+
return rot * 0.5 + 0.5;
58+
}
59+
60+
float4 FragGrain(VaryingsDefault i) : SV_Target
61+
{
62+
float2 rotCoords = CoordRot(i.uv, _Params.zw);
63+
float n = PNoise(rotCoords * (192.0).xx / _Params.x, _Params.y);
64+
return n.xxxx;
65+
}
66+
67+
float4 FragGrainColored(VaryingsDefault i) : SV_Target
68+
{
69+
float2 rotCoordsR = CoordRot(i.uv, _Params.zw);
70+
float2 rotCoordsG = CoordRot(i.uv + (0.1).xx, _Params.zw);
71+
float2 rotCoordsB = CoordRot(i.uv - (0.1).xx, _Params.zw);
72+
73+
float r = PNoise(rotCoordsR * (192.0).xx / _Params.x, _Params.y);
74+
float g = PNoise(rotCoordsG * (192.0).xx / _Params.x, _Params.y);
75+
float b = PNoise(rotCoordsB * (192.0).xx / _Params.x, _Params.y);
76+
77+
return float4(r, g, b, 1.0);
78+
}
79+
80+
ENDCG
81+
82+
SubShader
83+
{
84+
Cull Off ZWrite Off ZTest Always
85+
86+
Pass
87+
{
88+
CGPROGRAM
89+
90+
#pragma vertex VertDefault
91+
#pragma fragment FragGrain
92+
93+
ENDCG
94+
}
95+
96+
Pass
97+
{
98+
CGPROGRAM
99+
100+
#pragma vertex VertDefault
101+
#pragma fragment FragGrainColored
102+
103+
ENDCG
104+
}
105+
}
106+
}

PostProcessing/Resources/Shaders/GrainGen.shader.meta

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

PostProcessing/Resources/Shaders/Uber.shader

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ Shader "Hidden/Post FX/Uber Shader"
55
_MainTex ("Texture", 2D) = "white" {}
66
_AutoExposure ("", 2D) = "" {}
77
_BloomTex ("", 2D) = "" {}
8-
_Bloom_DirtTex("", 2D) = "" {}
8+
_Bloom_DirtTex ("", 2D) = "" {}
9+
_GrainTex ("", 2D) = "" {}
910
_LogLut ("", 2D) = "" {}
1011
_UserLut ("", 2D) = "" {}
1112
_Vignette_Mask ("", 2D) = "" {}
@@ -23,12 +24,11 @@ Shader "Hidden/Post FX/Uber Shader"
2324
#pragma multi_compile __ BLOOM_LENS_DIRT
2425
#pragma multi_compile __ COLOR_GRADING COLOR_GRADING_LOG_VIEW
2526
#pragma multi_compile __ USER_LUT
26-
#pragma multi_compile __ GRAIN_FAST GRAIN_FILMIC
27+
#pragma multi_compile __ GRAIN
2728
#pragma multi_compile __ VIGNETTE_CLASSIC VIGNETTE_ROUND VIGNETTE_MASKED
2829

2930
#include "UnityCG.cginc"
3031
#include "Bloom.cginc"
31-
#include "Grain.cginc"
3232
#include "ColorGrading.cginc"
3333

3434
// Auto exposure / eye adaptation
@@ -56,8 +56,9 @@ Shader "Hidden/Post FX/Uber Shader"
5656
half4 _UserLut_Params; // @see _LogLut_Params
5757

5858
// Grain
59-
half4 _Grain_Params1; // x: amount, y: size, z: lum_contrib, w: aspect
60-
half3 _Grain_Params2; // x: cos_angle, y: sin_angle, z: time
59+
half4 _Grain_Params1; // x: lum_contrib, y: intensityR, z: intensityG, w: intensityB
60+
half4 _Grain_Params2; // x: xscale, h: yscale, z: xoffset, w: yoffset
61+
sampler2D _GrainTex;
6162

6263
// Vignette
6364
half3 _Vignette_Color;
@@ -95,7 +96,7 @@ Shader "Hidden/Post FX/Uber Shader"
9596

9697
half4 FragUber(VaryingsFlipped i) : SV_Target
9798
{
98-
half2 uv = i.uv;
99+
float2 uv = i.uv;
99100
half autoExposure = 1.0;
100101

101102
// Store the auto exposure value for later
@@ -117,13 +118,13 @@ Shader "Hidden/Post FX/Uber Shader"
117118
// TODO: Take advantage of TAA to get even smoother results
118119
#if CHROMATIC_ABERRATION
119120
{
120-
half2 coords = 2.0 * uv - 1.0;
121-
half2 end = uv - coords * dot(coords, coords) * _ChromaticAberration_Amount;
121+
float2 coords = 2.0 * uv - 1.0;
122+
float2 end = uv - coords * dot(coords, coords) * _ChromaticAberration_Amount;
122123

123-
half2 diff = end - uv;
124+
float2 diff = end - uv;
124125
int samples = clamp(int(length(_MainTex_TexelSize.zw * diff / 2.0)), 3, 16);
125-
half2 delta = diff / samples;
126-
half2 pos = uv;
126+
float2 delta = diff / samples;
127+
float2 pos = uv;
127128
half3 sum = (0.0).xxx, filterSum = (0.0).xxx;
128129

129130
for (int i = 0; i < samples; i++)
@@ -219,9 +220,16 @@ Shader "Hidden/Post FX/Uber Shader"
219220
color = saturate(color);
220221

221222
// Grain / dithering
222-
#if (GRAIN_FAST || GRAIN_FILMIC)
223+
#if (GRAIN)
223224
{
224-
color = ApplyGrain(color, uv, _Grain_Params1, _Grain_Params2);
225+
float3 grain = tex2D(_GrainTex, uv * _Grain_Params2.xy + _Grain_Params2.zw).rgb;
226+
227+
// Noisiness response curve based on scene luminance
228+
float luminance = lerp(0.0, AcesLuminance(color), _Grain_Params1.x);
229+
float lum = smoothstep(0.2, 0.0, luminance) + luminance;
230+
231+
grain = lerp(grain, 0.0, Pow4(lum));
232+
color += grain * _Grain_Params1.yzw;
225233
}
226234
#endif
227235

PostProcessing/Runtime/Components/GrainComponent.cs

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,40 +6,84 @@ static class Uniforms
66
{
77
internal static readonly int _Grain_Params1 = Shader.PropertyToID("_Grain_Params1");
88
internal static readonly int _Grain_Params2 = Shader.PropertyToID("_Grain_Params2");
9+
internal static readonly int _GrainTex = Shader.PropertyToID("_GrainTex");
10+
internal static readonly int _Params = Shader.PropertyToID("_Params");
911
}
1012

1113
public override bool active
1214
{
1315
get
1416
{
1517
return model.enabled
16-
&& model.settings.intensity > 0f;
18+
&& model.settings.intensity > 0f
19+
&& SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBHalf);
1720
}
1821
}
1922

23+
RenderTexture m_GrainLookupRT;
24+
25+
public override void OnDisable()
26+
{
27+
GraphicsUtils.Destroy(m_GrainLookupRT);
28+
m_GrainLookupRT = null;
29+
}
30+
2031
public override void Prepare(Material uberMaterial)
2132
{
2233
var settings = model.settings;
2334

24-
uberMaterial.EnableKeyword(
25-
settings.mode == GrainModel.Mode.Fast
26-
? "GRAIN_FAST"
27-
: "GRAIN_FILMIC"
28-
);
35+
uberMaterial.EnableKeyword("GRAIN");
2936

3037
#if POSTFX_DEBUG_STATIC_GRAIN
38+
// Chosen by a fair dice roll
3139
float time = 4f;
40+
float rndXOffset = 0f;
41+
float rndYOffset = 0f;
3242
#else
3343
float time = Time.realtimeSinceStartup;
44+
float rndXOffset = Random.value;
45+
float rndYOffset = Random.value;
3446
#endif
3547

3648
// Used for sample rotation in Filmic mode and position offset in Fast mode
3749
const float kRotationOffset = 1.425f;
3850
float c = Mathf.Cos(time + kRotationOffset);
3951
float s = Mathf.Sin(time + kRotationOffset);
4052

41-
uberMaterial.SetVector(Uniforms._Grain_Params1, new Vector4(settings.intensity * 0.25f, settings.size, settings.luminanceContribution, (float)context.width / (float)context.height));
42-
uberMaterial.SetVector(Uniforms._Grain_Params2, new Vector3(c, s, time / 20f));
53+
if (m_GrainLookupRT == null || !m_GrainLookupRT.IsCreated())
54+
{
55+
GraphicsUtils.Destroy(m_GrainLookupRT);
56+
57+
m_GrainLookupRT = new RenderTexture(192, 192, 0, RenderTextureFormat.ARGBHalf)
58+
{
59+
filterMode = FilterMode.Bilinear,
60+
wrapMode = TextureWrapMode.Repeat,
61+
anisoLevel = 0,
62+
name = "Grain Lookup Texture"
63+
};
64+
65+
m_GrainLookupRT.Create();
66+
}
67+
68+
var grainMaterial = context.materialFactory.Get("Hidden/Post FX/Grain Generator");
69+
grainMaterial.SetVector(Uniforms._Params, new Vector4(settings.size, time / 20f, c, s));
70+
71+
Graphics.Blit((Texture)null, m_GrainLookupRT, grainMaterial, settings.colored ? 1 : 0);
72+
73+
uberMaterial.SetTexture(Uniforms._GrainTex, m_GrainLookupRT);
74+
75+
float intensity = settings.intensity * 0.25f;
76+
77+
if (!settings.colored)
78+
{
79+
uberMaterial.SetVector(Uniforms._Grain_Params1, new Vector4(settings.luminanceContribution, intensity, intensity, intensity));
80+
}
81+
else
82+
{
83+
uberMaterial.SetVector(Uniforms._Grain_Params1, new Vector4(settings.luminanceContribution, settings.weightR * intensity, settings.weightG * intensity, settings.weightB * intensity));
84+
}
85+
86+
uberMaterial.SetVector(Uniforms._Grain_Params2, new Vector4((float)context.width / (float)m_GrainLookupRT.width, (float)context.height / (float)m_GrainLookupRT.height, rndXOffset, rndYOffset));
4387
}
4488
}
4589
}

PostProcessing/Runtime/Models/GrainModel.cs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,24 @@ namespace UnityEngine.PostProcessing
55
[Serializable]
66
public class GrainModel : PostProcessingModel
77
{
8-
public enum Mode
9-
{
10-
Fast,
11-
Filmic
12-
}
13-
148
[Serializable]
159
public struct Settings
1610
{
17-
[Tooltip("Grain mode. \"Filmic\" produces a high quality, camera-like grain. \"Fast\" is aimed at lower-end platforms as it's a lot faster but doesn't look as good as \"Filmic\".")]
18-
public Mode mode;
11+
[Tooltip("Enable the use of colored grain.")]
12+
public bool colored;
1913

2014
[Range(0f, 1f), Tooltip("Grain strength. Higher means more visible grain.")]
2115
public float intensity;
2216

17+
[Range(0f, 2f), Tooltip("Grain weight for the red channel. Higher means more visible grain.")]
18+
public float weightR;
19+
20+
[Range(0f, 2f), Tooltip("Grain weight for the green channel. Higher means more visible grain.")]
21+
public float weightG;
22+
23+
[Range(0f, 2f), Tooltip("Grain weight for the blue channel. Higher means more visible grain.")]
24+
public float weightB;
25+
2326
[Range(1.5f, 3f), Tooltip("Grain particle size in \"Filmic\" mode.")]
2427
public float size;
2528

@@ -32,8 +35,11 @@ public static Settings defaultSettings
3235
{
3336
return new Settings
3437
{
35-
mode = Mode.Filmic,
38+
colored = false,
3639
intensity = 0.12f,
40+
weightR = 1f,
41+
weightG = 1f,
42+
weightB = 1f,
3743
size = 1.6f,
3844
luminanceContribution = 0.75f
3945
};

0 commit comments

Comments
 (0)