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 }