EyeAdaptation.shader
6.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
Shader "Hidden/Post FX/Eye Adaptation"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
}
CGINCLUDE
#pragma target 4.5
#pragma multi_compile __ AUTO_KEY_VALUE
#include "UnityCG.cginc"
#include "Common.cginc"
#include "EyeAdaptation.cginc"
// Eye adaptation pass
float4 _Params; // x: lowPercent, y: highPercent, z: minBrightness, w: maxBrightness
float2 _Speed; // x: down, y: up
float4 _ScaleOffsetRes; // x: scale, y: offset, w: histogram pass width, h: histogram pass height
float _ExposureCompensation;
StructuredBuffer<uint> _Histogram;
float GetBinValue(uint index, float maxHistogramValue)
{
return float(_Histogram[index]) * maxHistogramValue;
}
// Done in the vertex shader
float FindMaxHistogramValue()
{
uint maxValue = 0u;
for (uint i = 0; i < HISTOGRAM_BINS; i++)
{
uint h = _Histogram[i];
maxValue = max(maxValue, h);
}
return float(maxValue);
}
void FilterLuminance(uint i, float maxHistogramValue, inout float4 filter)
{
float binValue = GetBinValue(i, maxHistogramValue);
// Filter dark areas
float offset = min(filter.z, binValue);
binValue -= offset;
filter.zw -= offset.xx;
// Filter highlights
binValue = min(filter.w, binValue);
filter.w -= binValue;
// Luminance at the bin
float luminance = GetLuminanceFromHistogramBin(float(i) / float(HISTOGRAM_BINS), _ScaleOffsetRes.xy);
filter.xy += float2(luminance * binValue, binValue);
}
float GetAverageLuminance(float maxHistogramValue)
{
// Sum of all bins
uint i;
float totalSum = 0.0;
UNITY_LOOP
for (i = 0; i < HISTOGRAM_BINS; i++)
totalSum += GetBinValue(i, maxHistogramValue);
// Skip darker and lighter parts of the histogram to stabilize the auto exposure
// x: filtered sum
// y: accumulator
// zw: fractions
float4 filter = float4(0.0, 0.0, totalSum * _Params.xy);
UNITY_LOOP
for (i = 0; i < HISTOGRAM_BINS; i++)
FilterLuminance(i, maxHistogramValue, filter);
// Clamp to user brightness range
return clamp(filter.x / max(filter.y, EPSILON), _Params.z, _Params.w);
}
float GetExposureMultiplier(float avgLuminance)
{
avgLuminance = max(EPSILON, avgLuminance);
#if AUTO_KEY_VALUE
half keyValue = 1.03 - (2.0 / (2.0 + log2(avgLuminance + 1.0)));
#else
half keyValue = _ExposureCompensation;
#endif
half exposure = keyValue / avgLuminance;
return exposure;
}
float InterpolateExposure(float newExposure, float oldExposure)
{
float delta = newExposure - oldExposure;
float speed = delta > 0.0 ? _Speed.x : _Speed.y;
float exposure = oldExposure + delta * (1.0 - exp2(-unity_DeltaTime.x * speed));
//float exposure = oldExposure + delta * (unity_DeltaTime.x * speed);
return exposure;
}
float4 FragAdaptProgressive(VaryingsDefault i) : SV_Target
{
float maxValue = 1.0 / FindMaxHistogramValue();
float avgLuminance = GetAverageLuminance(maxValue);
float exposure = GetExposureMultiplier(avgLuminance);
float prevExposure = tex2D(_MainTex, (0.5).xx);
exposure = InterpolateExposure(exposure, prevExposure);
return exposure.xxxx;
}
float4 FragAdaptFixed(VaryingsDefault i) : SV_Target
{
float maxValue = 1.0 / FindMaxHistogramValue();
float avgLuminance = GetAverageLuminance(maxValue);
float exposure = GetExposureMultiplier(avgLuminance);
return exposure.xxxx;
}
// ---- Editor stuff
int _DebugWidth;
struct VaryingsEditorHisto
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float maxValue : TEXCOORD1;
float avgLuminance : TEXCOORD2;
};
VaryingsEditorHisto VertEditorHisto(AttributesDefault v)
{
VaryingsEditorHisto o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy;
o.maxValue = 1.0 / FindMaxHistogramValue();
o.avgLuminance = GetAverageLuminance(o.maxValue);
return o;
}
float4 FragEditorHisto(VaryingsEditorHisto i) : SV_Target
{
const float3 kRangeColor = float3(0.05, 0.4, 0.6);
const float3 kAvgColor = float3(0.8, 0.3, 0.05);
float4 color = float4(0.0, 0.0, 0.0, 0.7);
uint ix = (uint)(round(i.uv.x * HISTOGRAM_BINS));
float bin = saturate(float(_Histogram[ix]) * i.maxValue);
float fill = step(i.uv.y, bin);
// Min / max brightness markers
float luminanceMin = GetHistogramBinFromLuminance(_Params.z, _ScaleOffsetRes.xy);
float luminanceMax = GetHistogramBinFromLuminance(_Params.w, _ScaleOffsetRes.xy);
color.rgb += fill.rrr;
if (i.uv.x > luminanceMin && i.uv.x < luminanceMax)
{
color.rgb = fill.rrr * kRangeColor;
color.rgb += kRangeColor;
}
// Current average luminance marker
float luminanceAvg = GetHistogramBinFromLuminance(i.avgLuminance, _ScaleOffsetRes.xy);
float avgPx = luminanceAvg * _DebugWidth;
if (abs(i.pos.x - avgPx) < 2)
color.rgb = kAvgColor;
return color;
}
ENDCG
SubShader
{
Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex VertDefault
#pragma fragment FragAdaptProgressive
ENDCG
}
Pass
{
CGPROGRAM
#pragma vertex VertDefault
#pragma fragment FragAdaptFixed
ENDCG
}
Pass
{
CGPROGRAM
#pragma vertex VertEditorHisto
#pragma fragment FragEditorHisto
ENDCG
}
}
}