1 /*
2 Copyright (c) 2017-2018 Timur Gafarov
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 Permission is hereby granted, free of charge, to any person or organization
6 obtaining a copy of the software and accompanying documentation covered by
7 this license (the "Software") to use, reproduce, display, distribute,
8 execute, and transmit the Software, and to prepare derivative works of the
9 Software, and to permit third-parties to whom the Software is furnished to
10 do so, all subject to the following:
11 
12 The copyright notices in the Software and this entire statement, including
13 the above license grant, this restriction and the following disclaimer,
14 must be included in all copies of the Software, in whole or in part, and
15 all derivative works of the Software, unless such copies or derivative
16 works are solely in the form of machine-executable object code generated by
17 a source language processor.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26 */
27 
28 module dagon.graphics.filters.hdr;
29 
30 import derelict.opengl;
31 import dagon.core.ownership;
32 import dagon.graphics.postproc;
33 import dagon.graphics.framebuffer;
34 import dagon.graphics.texture;
35 import dagon.graphics.rc;
36 
37 /*
38  * tonemapHable is based on a function by John Hable:
39  * http://filmicworlds.com/blog/filmic-tonemapping-operators
40  *
41  * tonemapACES is based on a function by Krzysztof Narkowicz:
42  * https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve
43  *
44  * LUT function (lookupColor) is based on a code by Matt DesLauriers:
45  * https://github.com/mattdesl/glsl-lut
46  */
47 
48 enum Tonemapper
49 {
50     Reinhard = 0,
51     Hable = 1,
52     ACES = 2
53 }
54 
55 class PostFilterHDR: PostFilter
56 {
57     private string vs = "
58         #version 330 core
59         
60         uniform mat4 modelViewMatrix;
61         uniform mat4 projectionMatrix;
62 
63         uniform vec2 viewSize;
64         
65         layout (location = 0) in vec2 va_Vertex;
66         layout (location = 1) in vec2 va_Texcoord;
67 
68         out vec2 texCoord;
69         
70         void main()
71         {
72             texCoord = va_Texcoord;
73             gl_Position = projectionMatrix * modelViewMatrix * vec4(va_Vertex * viewSize, 0.0, 1.0);
74         }
75     ";
76 
77     private string fs = "
78         #version 330 core
79         
80         uniform sampler2D fbColor;
81         uniform sampler2D fbVelocity;
82         uniform sampler2D colorTable;
83         uniform sampler2D vignette;
84         //uniform sampler2D blurred;
85         uniform vec2 viewSize;
86         uniform float timeStep;
87 
88         uniform bool useMotionBlur;
89         uniform int motionBlurSamples;
90         uniform float shutterFps;
91         
92         //uniform bool useGlow;
93         //uniform float glowBrightness;
94         
95         uniform float exposure;
96         uniform int tonemapFunction;
97         
98         uniform bool useLUT;
99         uniform bool useVignette;
100         
101         in vec2 texCoord;
102         
103         out vec4 frag_color;
104         
105         vec3 hableFunc(vec3 x)
106         {
107             return ((x * (0.15 * x + 0.1 * 0.5) + 0.2 * 0.02) / (x * (0.15 * x + 0.5) + 0.2 * 0.3)) - 0.02 / 0.3;
108         }
109 
110         vec3 tonemapHable(vec3 x, float expo)
111         {
112             const vec3 whitePoint = vec3(11.2);
113             vec3 c = x * expo;
114             c = hableFunc(c * 2.0) * (1.0 / hableFunc(whitePoint));
115             return pow(c, vec3(1.0 / 2.2));
116         }
117         
118         vec3 tonemapReinhard(vec3 x, float expo)
119         {
120             vec3 c = x * expo;
121             c = c / (c + 1.0);
122             return pow(c, vec3(1.0 / 2.2));
123         }
124         
125         vec3 tonemapACES(vec3 x, float expo)
126         {
127             float a = 2.51;
128             float b = 0.03;
129             float c = 2.43;
130             float d = 0.59;
131             float e = 0.14;
132             vec3 res = x * expo * 0.6;
133             res = clamp((res*(a*res+b))/(res*(c*res+d)+e), 0.0, 1.0);
134             return pow(res, vec3(1.0 / 2.2));
135         }
136         
137         vec3 lookupColor(sampler2D lookupTable, vec3 textureColor)
138         {
139             textureColor = clamp(textureColor, 0.0, 1.0);
140 
141             float blueColor = textureColor.b * 63.0;
142 
143             vec2 quad1;
144             quad1.y = floor(floor(blueColor) / 8.0);
145             quad1.x = floor(blueColor) - (quad1.y * 8.0);
146 
147             vec2 quad2;
148             quad2.y = floor(ceil(blueColor) / 8.0);
149             quad2.x = ceil(blueColor) - (quad2.y * 8.0);
150 
151             vec2 texPos1;
152             texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);
153             texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g);
154 
155             vec2 texPos2;
156             texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);
157             texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g);
158 
159             vec3 newColor1 = texture(lookupTable, texPos1).rgb;
160             vec3 newColor2 = texture(lookupTable, texPos2).rgb;
161 
162             vec3 newColor = mix(newColor1, newColor2, fract(blueColor));
163             return newColor;
164         }
165 
166         void main()
167         {
168             vec3 res = texture(fbColor, texCoord).rgb;
169             
170             if (useMotionBlur)
171             {
172                 vec2 blurVec = texture(fbVelocity, texCoord).xy;
173                 blurVec = blurVec / (timeStep * shutterFps);
174                 float invSamplesMinusOne = 1.0 / float(motionBlurSamples - 1);
175                 float usedSamples = 1.0;
176                 
177                 for (float i = 1.0; i < motionBlurSamples; i++)
178                 {
179                     vec2 offset = blurVec * (i * invSamplesMinusOne - 0.5);
180                     float mask = texture(fbVelocity, texCoord + offset).w;
181                     res += texture(fbColor, texCoord + offset).rgb * mask;
182                     usedSamples += mask;
183                 }
184                 
185                 res = res / usedSamples;
186             }
187             
188             /*
189             if (useGlow)
190             {
191                 vec3 glow = texture(blurred, texCoord).rgb;
192                 float lum = glow.r * 0.2126 + glow.g * 0.7152 + glow.b * 0.0722;
193                 const float minLuminance = 0.01;
194                 const float maxLuminance = 1.0;
195                 lum = (clamp(lum, minLuminance, maxLuminance) - minLuminance) / (maxLuminance - minLuminance);
196                 res += glow * lum * glowBrightness;
197             }
198             */
199             
200             if (tonemapFunction == 2)
201                 res = tonemapACES(res, exposure);
202             else if (tonemapFunction == 1)
203                 res = tonemapHable(res, exposure);
204             else
205                 res = tonemapReinhard(res, exposure);
206                 
207             if (useVignette)
208                 res = mix(res, res * texture(vignette, vec2(texCoord.x, 1.0 - texCoord.y)).rgb, 0.8);
209             
210             if (useLUT)
211                 res = lookupColor(colorTable, res);
212 
213             frag_color = vec4(res, 1.0);
214         }
215     ";
216 
217     override string vertexShader()
218     {
219         return vs;
220     }
221 
222     override string fragmentShader()
223     {
224         return fs;
225     }
226     
227     GLint colorTableLoc;
228     GLint exposureLoc;
229     GLint tonemapFunctionLoc;
230     GLint useLUTLoc;
231     GLint vignetteLoc;
232     GLint useVignetteLoc;
233     //GLint blurredLoc;
234     //GLint useGlowLoc;
235     //GLint glowBrightnessLoc;
236     GLint fbVelocityLoc;
237     GLint useMotionBlurLoc;
238     GLint motionBlurSamplesLoc;
239     GLint shutterFpsLoc;
240     GLint timeStepLoc;
241     
242     float minLuminance = 0.001f;
243     float maxLuminance = 100000.0f;
244     float keyValue = 0.5f;
245     float adaptationSpeed = 4.0f;
246     
247     float exposure = 0.0f;
248     Tonemapper tonemapFunction = Tonemapper.Reinhard;
249     
250     GLuint velocityTexture;
251     bool mblurEnabled = false;
252     int motionBlurSamples = 20;
253     float shutterFps = 24.0;
254     float shutterSpeed = 1.0 / 24.0;
255     
256     //bool glowEnabled = false;
257     //float glowBrightness = 1.0;
258     
259     //GLuint blurredScene;
260     Texture colorTable;
261     Texture vignette;
262 
263     this(Framebuffer inputBuffer, Framebuffer outputBuffer, Owner o)
264     {
265         super(inputBuffer, outputBuffer, o);
266         
267         colorTableLoc = glGetUniformLocation(shaderProgram, "colorTable");
268         exposureLoc = glGetUniformLocation(shaderProgram, "exposure");
269         tonemapFunctionLoc = glGetUniformLocation(shaderProgram, "tonemapFunction");
270         useLUTLoc = glGetUniformLocation(shaderProgram, "useLUT");
271         vignetteLoc = glGetUniformLocation(shaderProgram, "vignette");
272         useVignetteLoc = glGetUniformLocation(shaderProgram, "useVignette");
273         //blurredLoc = glGetUniformLocation(shaderProgram, "blurred");
274         //useGlowLoc = glGetUniformLocation(shaderProgram, "useGlow");
275         //glowBrightnessLoc = glGetUniformLocation(shaderProgram, "glowBrightness");
276         fbVelocityLoc = glGetUniformLocation(shaderProgram, "fbVelocity");
277         useMotionBlurLoc = glGetUniformLocation(shaderProgram, "useMotionBlur");
278         motionBlurSamplesLoc = glGetUniformLocation(shaderProgram, "motionBlurSamples");
279         shutterFpsLoc = glGetUniformLocation(shaderProgram, "shutterFps");
280         timeStepLoc = glGetUniformLocation(shaderProgram, "timeStep");
281     }
282     
283     override void bind(RenderingContext* rc)
284     {
285         super.bind(rc);
286         
287         glActiveTexture(GL_TEXTURE2);
288         glBindTexture(GL_TEXTURE_2D, velocityTexture);
289         
290         glActiveTexture(GL_TEXTURE3);
291         if (colorTable)
292             colorTable.bind();
293         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
294         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
295         glActiveTexture(GL_TEXTURE0);
296         
297         glActiveTexture(GL_TEXTURE4);
298         if (vignette)
299             vignette.bind();
300             
301         //glActiveTexture(GL_TEXTURE5);
302         //glBindTexture(GL_TEXTURE_2D, blurredScene);
303         glActiveTexture(GL_TEXTURE0);
304         
305         glUniform1i(fbVelocityLoc, 2);
306         glUniform1i(colorTableLoc, 3);
307         glUniform1f(exposureLoc, exposure);
308         glUniform1i(tonemapFunctionLoc, tonemapFunction);
309         glUniform1i(useLUTLoc, (colorTable !is null));
310         glUniform1i(vignetteLoc, 4);
311         glUniform1i(useVignetteLoc, (vignette !is null));
312         //glUniform1i(blurredLoc, 5);
313         //glUniform1i(useGlowLoc, glowEnabled);
314         //glUniform1f(glowBrightnessLoc, glowBrightness);
315         glUniform1i(useMotionBlurLoc, mblurEnabled);
316         glUniform1i(motionBlurSamplesLoc, motionBlurSamples);
317         glUniform1f(shutterFpsLoc, shutterFps);
318         glUniform1f(timeStepLoc, rc.eventManager.deltaTime);
319     }
320     
321     override void unbind(RenderingContext* rc)
322     {
323         glActiveTexture(GL_TEXTURE2);
324         glBindTexture(GL_TEXTURE_2D, 0);
325     
326         glActiveTexture(GL_TEXTURE3);
327         if (colorTable)
328             colorTable.unbind();
329         //glActiveTexture(GL_TEXTURE0);
330         
331         glActiveTexture(GL_TEXTURE4);
332         if (vignette)
333             vignette.unbind();
334         glActiveTexture(GL_TEXTURE0);
335         
336         /*
337         glActiveTexture(GL_TEXTURE5);
338         glBindTexture(GL_TEXTURE_2D, 0);
339         glActiveTexture(GL_TEXTURE0);
340         */
341     }
342 }