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

Commit 2c9d0db

Browse files
committed
Fully moved auto exposure to compute shaders
+ slight speed boost for the averaging pass
1 parent 80afb00 commit 2c9d0db

13 files changed

+136
-125
lines changed

PostProcessing/Editor/PostProcessResourceStripper.cs

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using System;
21
using UnityEditor.Build;
32
using UnityEngine;
43
using UnityEngine.Rendering.PostProcessing;
@@ -7,9 +6,9 @@ namespace UnityEditor.Rendering.PostProcessing
76
{
87
public sealed class PostProcessResourceStripper : ScriptableObject
98
{
10-
[SerializeField] private PostProcessResources resources;
11-
[SerializeField] private PostProcessResources unstrippedResources;
12-
[SerializeField] private PostProcessStrippingConfig stripping;
9+
[SerializeField] PostProcessResources resources;
10+
[SerializeField] PostProcessResources unstrippedResources;
11+
[SerializeField] PostProcessStrippingConfig stripping;
1312

1413
public const string DefaultStrippingConfigAssetPath = "Assets/PostProcessStrippingConfig.asset";
1514
bool enabled = true;
@@ -30,7 +29,7 @@ public static PostProcessResourceStripper instance
3029
}
3130
}
3231

33-
static private string FindPostProcessStrippingConfigGUID()
32+
static string FindPostProcessStrippingConfigGUID()
3433
{
3534
var guids = AssetDatabase.FindAssets("t:PostProcessStrippingConfig", null);
3635
if (guids.Length > 0)
@@ -39,7 +38,7 @@ static private string FindPostProcessStrippingConfigGUID()
3938
return null;
4039
}
4140

42-
static public string EnsurePostProcessStrippingConfigAssetExists()
41+
public static string EnsurePostProcessStrippingConfigAssetExists()
4342
{
4443
var guid = FindPostProcessStrippingConfigGUID();
4544
if (guid != null)
@@ -54,7 +53,7 @@ static public string EnsurePostProcessStrippingConfigAssetExists()
5453
return FindPostProcessStrippingConfigGUID();
5554
}
5655

57-
private void LazyLoadStrippingConfig()
56+
void LazyLoadStrippingConfig()
5857
{
5958
if (stripping != null)
6059
return;
@@ -74,7 +73,7 @@ void OnDestroy()
7473
unstrippedResources.changeHandler = null;
7574
}
7675

77-
private void StripMultiScaleAO()
76+
void StripMultiScaleAO()
7877
{
7978
resources.computeShaders.multiScaleAODownsample1 = null;
8079
resources.computeShaders.multiScaleAODownsample2 = null;
@@ -83,13 +82,19 @@ private void StripMultiScaleAO()
8382
resources.shaders.multiScaleAO = null;
8483
}
8584

86-
private void StripScreenSpaceReflections()
85+
void StripScreenSpaceReflections()
8786
{
8887
resources.shaders.screenSpaceReflections = null;
8988
resources.computeShaders.gaussianDownsample = null;
9089
}
9190

92-
private void StripDebugShaders()
91+
void StripAutoExposure()
92+
{
93+
resources.computeShaders.autoExposure = null;
94+
resources.computeShaders.exposureHistogram = null;
95+
}
96+
97+
void StripDebugShaders()
9398
{
9499
resources.shaders.lightMeter = null;
95100
resources.shaders.gammaHistogram = null;
@@ -102,7 +107,7 @@ private void StripDebugShaders()
102107
resources.computeShaders.vectorscope = null;
103108
}
104109

105-
private void Apply(BuildTarget target)
110+
void Apply(BuildTarget target)
106111
{
107112
if (!enabled)
108113
return;
@@ -135,9 +140,9 @@ private void Apply(BuildTarget target)
135140
if (stripping.stripComputeShaders)
136141
{
137142
resources.computeShaders = new PostProcessResources.ComputeShaders();
138-
resources.shaders.autoExposure = null;
139143
StripScreenSpaceReflections();
140144
StripMultiScaleAO();
145+
StripAutoExposure();
141146
StripDebugShaders();
142147
}
143148

PostProcessing/Editor/PostProcessStrippingConfig.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using UnityEngine;
2-
using UnityEditor.Rendering.PostProcessing;
32

43
namespace UnityEditor.Rendering.PostProcessing
54
{
-8 Bytes
Binary file not shown.
Binary file not shown.

PostProcessing/Runtime/Effects/AutoExposure.cs

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,7 @@ public override bool IsEnabledAndSupported(PostProcessRenderContext context)
4141
return enabled.value
4242
&& SystemInfo.supportsComputeShaders
4343
&& RenderTextureFormat.RFloat.IsSupported()
44-
&& context.resources.shaders.autoExposure
45-
&& context.resources.shaders.autoExposure.isSupported
44+
&& context.resources.computeShaders.autoExposure
4645
&& context.resources.computeShaders.exposureHistogram;
4746
}
4847
}
@@ -69,7 +68,7 @@ void CheckTexture(int eye, int id)
6968
{
7069
if (m_AutoExposurePool[eye][id] == null || !m_AutoExposurePool[eye][id].IsCreated())
7170
{
72-
m_AutoExposurePool[eye][id] = new RenderTexture(1, 1, 0, RenderTextureFormat.RFloat);
71+
m_AutoExposurePool[eye][id] = new RenderTexture(1, 1, 0, RenderTextureFormat.RFloat) { enableRandomWrite = true };
7372
m_AutoExposurePool[eye][id].Create();
7473
}
7574
}
@@ -79,9 +78,6 @@ public override void Render(PostProcessRenderContext context)
7978
var cmd = context.command;
8079
cmd.BeginSample("AutoExposureLookup");
8180

82-
var sheet = context.propertySheets.Get(context.resources.shaders.autoExposure);
83-
sheet.ClearKeywords();
84-
8581
// Prepare autoExpo texture pool
8682
CheckTexture(context.xrActiveEye, 0);
8783
CheckTexture(context.xrActiveEye, 1);
@@ -99,31 +95,44 @@ public override void Render(PostProcessRenderContext context)
9995
settings.minLuminance.value = Mathf.Min(minLum, maxLum);
10096
settings.maxLuminance.value = Mathf.Max(minLum, maxLum);
10197

102-
// Compute auto exposure
103-
sheet.properties.SetBuffer(ShaderIDs.HistogramBuffer, context.logHistogram.data);
104-
sheet.properties.SetVector(ShaderIDs.Params, new Vector4(lowPercent * 0.01f, highPercent * 0.01f, RuntimeUtilities.Exp2(settings.minLuminance.value), RuntimeUtilities.Exp2(settings.maxLuminance.value)));
105-
sheet.properties.SetVector(ShaderIDs.Speed, new Vector2(settings.speedDown.value, settings.speedUp.value));
106-
sheet.properties.SetVector(ShaderIDs.ScaleOffsetRes, context.logHistogram.GetHistogramScaleOffsetRes(context));
107-
sheet.properties.SetFloat(ShaderIDs.ExposureCompensation, settings.keyValue.value);
98+
// Compute average luminance & auto exposure
99+
bool isStatic = m_ResetHistory || !Application.isPlaying;
100+
string adaptation = null;
101+
102+
if (isStatic)
103+
adaptation = "KAutoExposureAvgLuminance_fixed";
104+
else if (settings.eyeAdaptation.value == EyeAdaptation.Progressive)
105+
adaptation = "KAutoExposureAvgLuminance_progressive";
106+
107+
var compute = context.resources.computeShaders.autoExposure;
108+
int kernel = compute.FindKernel(adaptation);
109+
cmd.SetComputeBufferParam(compute, kernel, "_HistogramBuffer", context.logHistogram.data);
110+
cmd.SetComputeVectorParam(compute, "_Params1", new Vector4(lowPercent * 0.01f, highPercent * 0.01f, RuntimeUtilities.Exp2(settings.minLuminance.value), RuntimeUtilities.Exp2(settings.maxLuminance.value)));
111+
cmd.SetComputeVectorParam(compute, "_Params2", new Vector4(settings.speedDown.value, settings.speedUp.value, settings.keyValue.value, Time.deltaTime));
112+
cmd.SetComputeVectorParam(compute, "_ScaleOffsetRes", context.logHistogram.GetHistogramScaleOffsetRes(context));
108113

109-
if (m_ResetHistory || !Application.isPlaying)
114+
if (isStatic)
110115
{
111116
// We don't want eye adaptation when not in play mode because the GameView isn't
112117
// animated, thus making it harder to tweak. Just use the final audo exposure value.
113118
m_CurrentAutoExposure = m_AutoExposurePool[context.xrActiveEye][0];
114-
cmd.BlitFullscreenTriangle(BuiltinRenderTextureType.None, m_CurrentAutoExposure, sheet, (int)EyeAdaptation.Fixed);
119+
cmd.SetComputeTextureParam(compute, kernel, "_Destination", m_CurrentAutoExposure);
120+
cmd.DispatchCompute(compute, kernel, 1, 1, 1);
115121

116122
// Copy current exposure to the other pingpong target to avoid adapting from black
117123
RuntimeUtilities.CopyTexture(cmd, m_AutoExposurePool[context.xrActiveEye][0], m_AutoExposurePool[context.xrActiveEye][1]);
118-
119124
m_ResetHistory = false;
120125
}
121126
else
122127
{
123128
int pp = m_AutoExposurePingPong[context.xrActiveEye];
124129
var src = m_AutoExposurePool[context.xrActiveEye][++pp % 2];
125130
var dst = m_AutoExposurePool[context.xrActiveEye][++pp % 2];
126-
cmd.BlitFullscreenTriangle(src, dst, sheet, (int)settings.eyeAdaptation.value);
131+
132+
cmd.SetComputeTextureParam(compute, kernel, "_Source", src);
133+
cmd.SetComputeTextureParam(compute, kernel, "_Destination", dst);
134+
cmd.DispatchCompute(compute, kernel, 1, 1, 1);
135+
127136
m_AutoExposurePingPong[context.xrActiveEye] = ++pp % 2;
128137
m_CurrentAutoExposure = dst;
129138
}

PostProcessing/Runtime/PostProcessResources.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ public sealed class PostProcessResources : ScriptableObject
1212
[Serializable]
1313
public sealed class Shaders
1414
{
15-
public Shader autoExposure;
1615
public Shader bloom;
1716
public Shader copy;
1817
public Shader copyStd;
@@ -45,6 +44,7 @@ public Shaders Clone()
4544
[Serializable]
4645
public sealed class ComputeShaders
4746
{
47+
public ComputeShader autoExposure;
4848
public ComputeShader exposureHistogram;
4949
public ComputeShader lut3DBaker;
5050
public ComputeShader texture3dLerp;

PostProcessing/Runtime/Utils/LogHistogram.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public void Generate(PostProcessRenderContext context)
1818
{
1919
m_ThreadX = 16;
2020
m_ThreadY = RuntimeUtilities.isAndroidOpenGL ? 8 : 16;
21-
data = new ComputeBuffer (k_Bins, sizeof(uint));
21+
data = new ComputeBuffer(k_Bins, sizeof(uint));
2222
}
2323

2424
var scaleOffsetRes = GetHistogramScaleOffsetRes(context);
@@ -37,8 +37,8 @@ public void Generate(PostProcessRenderContext context)
3737
cmd.SetComputeTextureParam(compute, kernel, "_Source", context.source);
3838
cmd.SetComputeVectorParam(compute, "_ScaleOffsetRes", scaleOffsetRes);
3939
cmd.DispatchCompute(compute, kernel,
40-
Mathf.CeilToInt(scaleOffsetRes.z / (float)m_ThreadX),
41-
Mathf.CeilToInt(scaleOffsetRes.w / (float)m_ThreadY),
40+
Mathf.CeilToInt(scaleOffsetRes.z / m_ThreadX),
41+
Mathf.CeilToInt(scaleOffsetRes.w / m_ThreadY),
4242
1
4343
);
4444

PostProcessing/Runtime/Utils/ShaderIDs.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,7 @@ static class ShaderIDs
7979
internal static readonly int AutoExposureTex = Shader.PropertyToID("_AutoExposureTex");
8080
internal static readonly int HistogramBuffer = Shader.PropertyToID("_HistogramBuffer");
8181
internal static readonly int Params = Shader.PropertyToID("_Params");
82-
internal static readonly int Speed = Shader.PropertyToID("_Speed");
8382
internal static readonly int ScaleOffsetRes = Shader.PropertyToID("_ScaleOffsetRes");
84-
internal static readonly int ExposureCompensation = Shader.PropertyToID("_ExposureCompensation");
8583

8684
internal static readonly int BloomTex = Shader.PropertyToID("_BloomTex");
8785
internal static readonly int SampleScale = Shader.PropertyToID("_SampleScale");
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#pragma kernel KAutoExposureAvgLuminance_fixed MAIN=KAutoExposureAvgLuminance_fixed
2+
#pragma kernel KAutoExposureAvgLuminance_progressive MAIN=KAutoExposureAvgLuminance_progressive PROGRESSIVE
3+
4+
#include "../StdLib.hlsl"
5+
#include "ExposureHistogram.hlsl"
6+
7+
StructuredBuffer<uint> _HistogramBuffer;
8+
Texture2D<float> _Source;
9+
RWTexture2D<float> _Destination;
10+
11+
CBUFFER_START(Params)
12+
float4 _Params1; // x: lowPercent, y: highPercent, z: minBrightness, w: maxBrightness
13+
float4 _Params2; // x: speed down, y: speed up, z: exposure compensation, w: delta time
14+
float4 _ScaleOffsetRes; // x: scale, y: offset, w: histogram pass width, h: histogram pass height
15+
CBUFFER_END
16+
17+
groupshared uint gs_pyramid[HISTOGRAM_BINS];
18+
19+
float GetExposureMultiplier(float avgLuminance)
20+
{
21+
avgLuminance = max(EPSILON, avgLuminance);
22+
//float keyValue = 1.03 - (2.0 / (2.0 + log2(avgLuminance + 1.0)));
23+
float keyValue = _Params2.z;
24+
float exposure = keyValue / avgLuminance;
25+
return exposure;
26+
}
27+
28+
float InterpolateExposure(float newExposure, float oldExposure)
29+
{
30+
float delta = newExposure - oldExposure;
31+
float speed = delta > 0.0 ? _Params2.x : _Params2.y;
32+
float exposure = oldExposure + delta * (1.0 - exp2(-_Params2.w * speed));
33+
return exposure;
34+
}
35+
36+
[numthreads(HISTOGRAM_THREAD_X, HISTOGRAM_BINS / HISTOGRAM_THREAD_X, 1)]
37+
void MAIN(uint2 groupThreadId : SV_GroupThreadID)
38+
{
39+
const uint thread_id = groupThreadId.y * 8u + groupThreadId.x;
40+
gs_pyramid[thread_id] = _HistogramBuffer[thread_id];
41+
42+
GroupMemoryBarrierWithGroupSync();
43+
44+
// Parallel reduction to find the max value
45+
for (uint i = HISTOGRAM_BINS >> 1u; i > 32u; i >>= 1u)
46+
{
47+
if (thread_id < i)
48+
gs_pyramid[thread_id] = max(gs_pyramid[thread_id], gs_pyramid[thread_id + i]);
49+
50+
GroupMemoryBarrierWithGroupSync();
51+
}
52+
53+
if (thread_id < 32u)
54+
{
55+
gs_pyramid[thread_id] = max(gs_pyramid[thread_id], gs_pyramid[thread_id + 32]);
56+
gs_pyramid[thread_id] = max(gs_pyramid[thread_id], gs_pyramid[thread_id + 16]);
57+
gs_pyramid[thread_id] = max(gs_pyramid[thread_id], gs_pyramid[thread_id + 8]);
58+
gs_pyramid[thread_id] = max(gs_pyramid[thread_id], gs_pyramid[thread_id + 4]);
59+
gs_pyramid[thread_id] = max(gs_pyramid[thread_id], gs_pyramid[thread_id + 2]);
60+
gs_pyramid[thread_id] = max(gs_pyramid[thread_id], gs_pyramid[thread_id + 1]);
61+
}
62+
63+
GroupMemoryBarrierWithGroupSync();
64+
65+
if (thread_id == 0u)
66+
{
67+
float maxValue = 1.0 / float(gs_pyramid[0]);
68+
69+
#if PROGRESSIVE
70+
float avgLuminance = GetAverageLuminance(_HistogramBuffer, _Params1, maxValue, _ScaleOffsetRes.xy);
71+
float exposure = GetExposureMultiplier(avgLuminance);
72+
float prevExposure = _Source[uint2(0u, 0u)].x;
73+
exposure = InterpolateExposure(exposure, prevExposure);
74+
_Destination[uint2(0u, 0u)].x = exposure.x;
75+
#else
76+
float avgLuminance = GetAverageLuminance(_HistogramBuffer, _Params1, maxValue, _ScaleOffsetRes.xy);
77+
float exposure = GetExposureMultiplier(avgLuminance);
78+
_Destination[uint2(0u, 0u)].x = exposure.x;
79+
#endif
80+
}
81+
}

PostProcessing/Shaders/Builtins/AutoExposure.compute.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.

0 commit comments

Comments
 (0)