1 Shader "Hidden/Tonemapper" {
2 Properties {
3 _MainTex ("", 2D) = "black" {}
4 _SmallTex ("", 2D) = "grey" {}
5 _Curve ("", 2D) = "black" {}
6 }
7
8 CGINCLUDE
9
10 #include "UnityCG.cginc"
11
12 struct v2f {
13 float4 pos : SV_POSITION;
14 float2 uv : TEXCOORD0;
15 };
16
17 sampler2D _MainTex;
18 sampler2D _SmallTex;
19 sampler2D _Curve;
20
21 float4 _HdrParams;
22 float2 intensity;
23 float4 _MainTex_TexelSize;
24 float _AdaptionSpeed;
25 float _ExposureAdjustment;
26 float _RangeScale;
27
28 v2f vert( appdata_img v )
29 {
30 v2f o;
31 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
32 o.uv = v.texcoord.xy;
33 return o;
34 }
35
36 float4 fragLog(v2f i) : SV_Target
37 {
38 const float DELTA = 0.0001f;
39
40 float fLogLumSum = 0.0f;
41
42 fLogLumSum += log( Luminance(tex2D(_MainTex, i.uv + _MainTex_TexelSize.xy * float2(-1,-1)).rgb) + DELTA);
43 fLogLumSum += log( Luminance(tex2D(_MainTex, i.uv + _MainTex_TexelSize.xy * float2(1,1)).rgb) + DELTA);
44 fLogLumSum += log( Luminance(tex2D(_MainTex, i.uv + _MainTex_TexelSize.xy * float2(-1,1)).rgb) + DELTA);
45 fLogLumSum += log( Luminance(tex2D(_MainTex, i.uv + _MainTex_TexelSize.xy * float2(1,-1)).rgb) + DELTA);
46
47 float avg = fLogLumSum / 4.0;
48 return float4(avg, avg, avg, avg);
49 }
50
51 float4 fragExp(v2f i) : SV_Target
52 {
53 float2 lum = float2(0.0f, 0.0f);
54
55 lum += tex2D(_MainTex, i.uv + _MainTex_TexelSize.xy * float2(-1,-1)).xy;
56 lum += tex2D(_MainTex, i.uv + _MainTex_TexelSize.xy * float2(1,1)).xy;
57 lum += tex2D(_MainTex, i.uv + _MainTex_TexelSize.xy * float2(1,-1)).xy;
58 lum += tex2D(_MainTex, i.uv + _MainTex_TexelSize.xy * float2(-1,1)).xy;
59
60 lum = exp(lum / 4.0f);
61
62 return float4(lum.x, lum.y, lum.x, saturate(0.0125 * _AdaptionSpeed));
63 }
64
65 float3 ToCIE(float3 FullScreenImage)
66 {
67 // RGB -> XYZ conversion
68 // http://www.w3.org/Graphics/Color/sRGB
69 // The official sRGB to XYZ conversion matrix is (following ITU-R BT.709)
70 // 0.4125 0.3576 0.1805
71 // 0.2126 0.7152 0.0722
72 // 0.0193 0.1192 0.9505
73
74 float3x3 RGB2XYZ = {0.5141364, 0.3238786, 0.16036376, 0.265068, 0.67023428, 0.06409157, 0.0241188, 0.1228178, 0.84442666};
75
76 float3 XYZ = mul(RGB2XYZ, FullScreenImage.rgb);
77
78 // XYZ -> Yxy conversion
79
80 float3 Yxy;
81
82 Yxy.r = XYZ.g;
83
84 // x = X / (X + Y + Z)
85 // y = X / (X + Y + Z)
86
87 float temp = dot(float3(1.0,1.0,1.0), XYZ.rgb);
88
89 Yxy.gb = XYZ.rg / temp;
90
91 return Yxy;
92 }
93
94 float3 FromCIE(float3 Yxy)
95 {
96 float3 XYZ;
97 // Yxy -> XYZ conversion
98 XYZ.r = Yxy.r * Yxy.g / Yxy. b;
99
100 // X = Y * x / y
101 XYZ.g = Yxy.r;
102
103 // copy luminance Y
104 XYZ.b = Yxy.r * (1 - Yxy.g - Yxy.b) / Yxy.b;
105
106 // Z = Y * (1-x-y) / y
107
108 // XYZ -> RGB conversion
109 // The official XYZ to sRGB conversion matrix is (following ITU-R BT.709)
110 // 3.2410 -1.5374 -0.4986
111 // -0.9692 1.8760 0.0416
112 // 0.0556 -0.2040 1.0570
113
114 float3x3 XYZ2RGB = { 2.5651,-1.1665,-0.3986, -1.0217, 1.9777, 0.0439, 0.0753, -0.2543, 1.1892};
115
116 return mul(XYZ2RGB, XYZ);
117 }
118
119 // NOTE/OPTIMIZATION: we're not going the extra CIE detour anymore, but
120 // scale with the OUT/IN luminance ratio,this is sooooo much faster
121
122 float4 fragAdaptive(v2f i) : SV_Target
123 {
124 float avgLum = tex2D(_SmallTex, i.uv).x;
125 float4 color = tex2D (_MainTex, i.uv);
126
127 float cieLum = max(0.000001, Luminance(color.rgb)); //ToCIE(color.rgb);
128
129 float lumScaled = cieLum * _HdrParams.z / (0.001 + avgLum.x);
130
131 lumScaled = (lumScaled * (1.0f + lumScaled / (_HdrParams.w)))/(1.0f + lumScaled);
132
133 //cie.r = lumScaled;
134
135 color.rgb = color.rgb * (lumScaled / cieLum);
136
137 //color.rgb = FromCIE(cie);
138 return color;
139 }
140
141 float4 fragAdaptiveAutoWhite(v2f i) : SV_Target
142 {
143 float2 avgLum = tex2D(_SmallTex, i.uv).xy;
144 float4 color = tex2D(_MainTex, i.uv);
145
146 float cieLum = max(0.000001, Luminance(color.rgb)); //ToCIE(color.rgb);
147
148 float lumScaled = cieLum * _HdrParams.z / (0.001 + avgLum.x);
149
150 lumScaled = (lumScaled * (1.0f + lumScaled / (avgLum.y*avgLum.y)))/(1.0f + lumScaled);
151
152 //cie.r = lumScaled;
153
154 color.rgb = color.rgb * (lumScaled / cieLum);
155
156 //color.rgb = FromCIE(cie);
157 return color;
158 }
159
160 float4 fragCurve(v2f i) : SV_Target
161 {
162 float4 color = tex2D(_MainTex, i.uv);
163 float3 cie = ToCIE(color.rgb);
164
165 // Remap to new lum range
166 float newLum = tex2D(_Curve, float2(cie.r * _RangeScale, 0.5)).r;
167 cie.r = newLum;
168 color.rgb = FromCIE(cie);
169
170 return color;
171 }
172
173 float4 fragHable(v2f i) : SV_Target
174 {
175 const float A = 0.15;
176 const float B = 0.50;
177 const float C = 0.10;
178 const float D = 0.20;
179 const float E = 0.02;
180 const float F = 0.30;
181 const float W = 11.2;
182
183 float3 texColor = tex2D(_MainTex, i.uv).rgb;
184 texColor *= _ExposureAdjustment;
185
186 float ExposureBias = 2.0;
187 float3 x = ExposureBias*texColor;
188 float3 curr = ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;
189
190 x = W;
191 float3 whiteScale = 1.0f/(((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F);
192 float3 color = curr*whiteScale;
193
194 // float3 retColor = pow(color,1/2.2); // we have SRGB write enabled at this stage
195
196 return float4(color, 1.0);
197 }
198
199 // we are doing it on luminance here (better color preservation, but some other problems like very fast saturation)
200 float4 fragSimpleReinhard(v2f i) : SV_Target
201 {
202 float4 texColor = tex2D(_MainTex, i.uv);
203 float lum = Luminance(texColor.rgb);
204 float lumTm = lum * _ExposureAdjustment;
205 float scale = lumTm / (1+lumTm);
206 return float4(texColor.rgb * scale / lum, texColor.a);
207 }
208
209 float4 fragOptimizedHejiDawson(v2f i) : SV_Target
210 {
211 float4 texColor = tex2D(_MainTex, i.uv );
212 texColor *= _ExposureAdjustment;
213 float4 X = max(float4(0.0,0.0,0.0,0.0), texColor-0.004);
214 float4 retColor = (X*(6.2*X+.5))/(X*(6.2*X+1.7)+0.06);
215 return retColor*retColor;
216 }
217
218 float4 fragPhotographic(v2f i) : SV_Target
219 {
220 float4 texColor = tex2D(_MainTex, i.uv);
221 return 1-exp2(-_ExposureAdjustment * texColor);
222 }
223
224 float4 fragDownsample(v2f i) : SV_Target
225 {
226 float4 tapA = tex2D(_MainTex, i.uv + _MainTex_TexelSize * 0.5);
227 float4 tapB = tex2D(_MainTex, i.uv - _MainTex_TexelSize * 0.5);
228 float4 tapC = tex2D(_MainTex, i.uv + _MainTex_TexelSize * float2(0.5,-0.5));
229 float4 tapD = tex2D(_MainTex, i.uv - _MainTex_TexelSize * float2(0.5,-0.5));
230
231 float4 average = (tapA+tapB+tapC+tapD)/4;
232 average.y = max(max(tapA.y,tapB.y), max(tapC.y,tapD.y));
233
234 return average;
235 }
236
237 ENDCG
238
239 Subshader {
240 // adaptive reinhhard apply
241 Pass {
242 ZTest Always Cull Off ZWrite Off
243
244 CGPROGRAM
245 #pragma vertex vert
246 #pragma fragment fragAdaptive
247 ENDCG
248 }
249
250 // 1
251 Pass {
252 ZTest Always Cull Off ZWrite Off
253
254 CGPROGRAM
255 #pragma vertex vert
256 #pragma fragment fragLog
257 ENDCG
258 }
259 // 2
260 Pass {
261 ZTest Always Cull Off ZWrite Off
262 Blend SrcAlpha OneMinusSrcAlpha
263
264 CGPROGRAM
265 #pragma vertex vert
266 #pragma fragment fragExp
267 ENDCG
268 }
269 // 3
270 Pass {
271 ZTest Always Cull Off ZWrite Off
272
273 Blend Off
274
275 CGPROGRAM
276 #pragma vertex vert
277 #pragma fragment fragExp
278 ENDCG
279 }
280
281 // 4 user controllable tonemap curve
282 Pass {
283 ZTest Always Cull Off ZWrite Off
284
285 CGPROGRAM
286 #pragma vertex vert
287 #pragma fragment fragCurve
288 ENDCG
289 }
290
291 // 5 tonemapping in uncharted
292 Pass {
293 ZTest Always Cull Off ZWrite Off
294
295 CGPROGRAM
296 #pragma vertex vert
297 #pragma fragment fragHable
298 ENDCG
299 }
300
301 // 6 simple tonemapping based reinhard
302 Pass {
303 ZTest Always Cull Off ZWrite Off
304
305 CGPROGRAM
306 #pragma vertex vert
307 #pragma fragment fragSimpleReinhard
308 ENDCG
309 }
310
311 // 7 OptimizedHejiDawson
312 Pass {
313 ZTest Always Cull Off ZWrite Off
314
315 CGPROGRAM
316 #pragma vertex vert
317 #pragma fragment fragOptimizedHejiDawson
318 ENDCG
319 }
320
321 // 8 Photographic
322 Pass {
323 ZTest Always Cull Off ZWrite Off
324
325 CGPROGRAM
326 #pragma vertex vert
327 #pragma fragment fragPhotographic
328 ENDCG
329 }
330
331 // 9 Downsample with auto white detection
332 Pass {
333 ZTest Always Cull Off ZWrite Off
334
335 CGPROGRAM
336 #pragma vertex vert
337 #pragma fragment fragDownsample
338 ENDCG
339 }
340
341 // 10 adaptive reinhhard apply with auto white
342 Pass {
343 ZTest Always Cull Off ZWrite Off
344
345 CGPROGRAM
346 #pragma vertex vert
347 #pragma fragment fragAdaptiveAutoWhite
348 ENDCG
349 }
350 }
351
352 Fallback off
353
354 } // shader