1/* 2DoF with bokeh GLSL shader v2.4 3by Martins Upitis (martinsh) (devlog-martinsh.blogspot.com) 4 5---------------------- 6The shader is Blender Game Engine ready, but it should be quite simple to adapt for your engine. 7 8This work is licensed under a Creative Commons Attribution 3.0 Unported License. 9So you are free to share, modify and adapt it for your needs, and even use it for commercial use. 10I would also love to hear about a project you are using it. 11 12Have fun, 13Martins 14---------------------- 15 16changelog: 17 182.4: 19- physically accurate DoF simulation calculated from "focalDepth" ,"focalLength", "f-stop" and "CoC" parameters. 20- option for artist controlled DoF simulation calculated only from "focalDepth" and individual controls for near and far blur 21- added "circe of confusion" (CoC) parameter in mm to accurately simulate DoF with different camera sensor or film sizes 22- cleaned up the code 23- some optimization 24 252.3: 26- new and physically little more accurate DoF 27- two extra input variables - focal length and aperture iris diameter 28- added a debug visualization of focus point and focal range 29 302.1: 31- added an option for pentagonal bokeh shape 32- minor fixes 33 342.0: 35- variable sample count to increase quality/performance 36- option to blur depth buffer to reduce hard edges 37- option to dither the samples with noise or pattern 38- bokeh chromatic aberration/fringing 39- bokeh bias to bring out bokeh edges 40- image thresholding to bring out highlights when image is out of focus 41 42*/ 43 44uniform sampler2D bgl_RenderedTexture; 45uniform sampler2D bgl_DepthTexture; 46uniform float bgl_RenderedTextureWidth; 47uniform float bgl_RenderedTextureHeight; 48 49#define PI 3.14159265 50 51float width = bgl_RenderedTextureWidth; //texture width 52float height = bgl_RenderedTextureHeight; //texture height 53 54vec2 texel = vec2(1.0/width,1.0/height); 55 56//uniform variables from external script 57 58uniform float focalDepth; //focal distance value in meters, but you may use autofocus option below 59uniform float focalLength; //focal length in mm 60uniform float fstop; //f-stop value 61uniform bool showFocus; //show debug focus point and focal range (red = focal point, green = focal range) 62 63/* 64make sure that these two values are the same for your camera, otherwise distances will be wrong. 65*/ 66 67float znear = 0.1; //camera clipping start 68float zfar = 100.0; //camera clipping end 69 70//------------------------------------------ 71//user variables 72 73int samples = 3; //samples on the first ring 74int rings = 3; //ring count 75 76bool manualdof = false; //manual dof calculation 77float ndofstart = 1.0; //near dof blur start 78float ndofdist = 2.0; //near dof blur falloff distance 79float fdofstart = 1.0; //far dof blur start 80float fdofdist = 3.0; //far dof blur falloff distance 81 82float CoC = 0.03;//circle of confusion size in mm (35mm film = 0.03mm) 83 84bool vignetting = true; //use optical lens vignetting? 85float vignout = 1.3; //vignetting outer border 86float vignin = 0.0; //vignetting inner border 87float vignfade = 22.0; //f-stops till vignete fades 88 89bool autofocus = false; //use autofocus in shader? disable if you use external focalDepth value 90vec2 focus = vec2(0.5,0.5); // autofocus point on screen (0.0,0.0 - left lower corner, 1.0,1.0 - upper right) 91float maxblur = 1.0; //clamp value of max blur (0.0 = no blur,1.0 default) 92 93float threshold = 0.5; //highlight threshold; 94float gain = 2.0; //highlight gain; 95 96float bias = 0.5; //bokeh edge bias 97float fringe = 0.7; //bokeh chromatic aberration/fringing 98 99bool noise = true; //use noise instead of pattern for sample dithering 100float namount = 0.0001; //dither amount 101 102bool depthblur = false; //blur the depth buffer? 103float dbsize = 1.25; //depthblursize 104 105/* 106next part is experimental 107not looking good with small sample and ring count 108looks okay starting from samples = 4, rings = 4 109*/ 110 111bool pentagon = false; //use pentagon as bokeh shape? 112float feather = 0.4; //pentagon shape feather 113 114//------------------------------------------ 115 116 117float penta(vec2 coords) //pentagonal shape 118{ 119 float scale = float(rings) - 1.3; 120 vec4 HS0 = vec4( 1.0, 0.0, 0.0, 1.0); 121 vec4 HS1 = vec4( 0.309016994, 0.951056516, 0.0, 1.0); 122 vec4 HS2 = vec4(-0.809016994, 0.587785252, 0.0, 1.0); 123 vec4 HS3 = vec4(-0.809016994,-0.587785252, 0.0, 1.0); 124 vec4 HS4 = vec4( 0.309016994,-0.951056516, 0.0, 1.0); 125 vec4 HS5 = vec4( 0.0 ,0.0 , 1.0, 1.0); 126 127 vec4 one = vec4( 1.0 ); 128 129 vec4 P = vec4((coords),vec2(scale, scale)); 130 131 vec4 dist = vec4(0.0); 132 float inorout = -4.0; 133 134 dist.x = dot( P, HS0 ); 135 dist.y = dot( P, HS1 ); 136 dist.z = dot( P, HS2 ); 137 dist.w = dot( P, HS3 ); 138 139 dist = smoothstep( -feather, feather, dist ); 140 141 inorout += dot( dist, one ); 142 143 dist.x = dot( P, HS4 ); 144 dist.y = HS5.w - abs( P.z ); 145 146 dist = smoothstep( -feather, feather, dist ); 147 inorout += dist.x; 148 149 return clamp( inorout, 0.0, 1.0 ); 150} 151 152float bdepth(vec2 coords) //blurring depth 153{ 154 float d = 0.0; 155 float kernel[9]; 156 vec2 offset[9]; 157 158 vec2 wh = vec2(texel.x, texel.y) * dbsize; 159 160 offset[0] = vec2(-wh.x,-wh.y); 161 offset[1] = vec2( 0.0, -wh.y); 162 offset[2] = vec2( wh.x -wh.y); 163 164 offset[3] = vec2(-wh.x, 0.0); 165 offset[4] = vec2( 0.0, 0.0); 166 offset[5] = vec2( wh.x, 0.0); 167 168 offset[6] = vec2(-wh.x, wh.y); 169 offset[7] = vec2( 0.0, wh.y); 170 offset[8] = vec2( wh.x, wh.y); 171 172 kernel[0] = 1.0/16.0; kernel[1] = 2.0/16.0; kernel[2] = 1.0/16.0; 173 kernel[3] = 2.0/16.0; kernel[4] = 4.0/16.0; kernel[5] = 2.0/16.0; 174 kernel[6] = 1.0/16.0; kernel[7] = 2.0/16.0; kernel[8] = 1.0/16.0; 175 176 177 for( int i=0; i<9; i++ ) 178 { 179 float tmp = texture2D(bgl_DepthTexture, coords + offset[i]).r; 180 d += tmp * kernel[i]; 181 } 182 183 return d; 184} 185 186 187vec3 color(vec2 coords,float blur) //processing the sample 188{ 189 vec3 col = vec3(0.0); 190 191 col.r = texture2D(bgl_RenderedTexture,coords + vec2(0.0,1.0)*texel*fringe*blur).r; 192 col.g = texture2D(bgl_RenderedTexture,coords + vec2(-0.866,-0.5)*texel*fringe*blur).g; 193 col.b = texture2D(bgl_RenderedTexture,coords + vec2(0.866,-0.5)*texel*fringe*blur).b; 194 195 vec3 lumcoeff = vec3(0.299,0.587,0.114); 196 float lum = dot(col.rgb, lumcoeff); 197 float thresh = max((lum-threshold)*gain, 0.0); 198 return col+mix(vec3(0.0),col,thresh*blur); 199} 200 201vec2 rand(vec2 coord) //generating noise/pattern texture for dithering 202{ 203 float noiseX = ((fract(1.0-coord.s*(width/2.0))*0.25)+(fract(coord.t*(height/2.0))*0.75))*2.0-1.0; 204 float noiseY = ((fract(1.0-coord.s*(width/2.0))*0.75)+(fract(coord.t*(height/2.0))*0.25))*2.0-1.0; 205 206 if (noise) 207 { 208 noiseX = clamp(fract(sin(dot(coord ,vec2(12.9898,78.233))) * 43758.5453),0.0,1.0)*2.0-1.0; 209 noiseY = clamp(fract(sin(dot(coord ,vec2(12.9898,78.233)*2.0)) * 43758.5453),0.0,1.0)*2.0-1.0; 210 } 211 return vec2(noiseX,noiseY); 212} 213 214vec3 debugFocus(vec3 col, float blur, float depth) 215{ 216 float edge = 0.002*depth; //distance based edge smoothing 217 float m = clamp(smoothstep(0.0,edge,blur),0.0,1.0); 218 float e = clamp(smoothstep(1.0-edge,1.0,blur),0.0,1.0); 219 220 col = mix(col,vec3(1.0,0.5,0.0),(1.0-m)*0.6); 221 col = mix(col,vec3(0.0,0.5,1.0),((1.0-e)-(1.0-m))*0.2); 222 223 return col; 224} 225 226float linearize(float depth) 227{ 228 return -zfar * znear / (depth * (zfar - znear) - zfar); 229} 230 231float vignette() 232{ 233 float dist = distance(gl_TexCoord[3].xy, vec2(0.5,0.5)); 234 dist = smoothstep(vignout+(fstop/vignfade), vignin+(fstop/vignfade), dist); 235 return clamp(dist,0.0,1.0); 236} 237 238void main() 239{ 240 //scene depth calculation 241 242 float depth = linearize(texture2D(bgl_DepthTexture,gl_TexCoord[0].xy).x); 243 244 if (depthblur) 245 { 246 depth = linearize(bdepth(gl_TexCoord[0].xy)); 247 } 248 249 //focal plane calculation 250 251 float fDepth = focalDepth; 252 253 if (autofocus) 254 { 255 fDepth = linearize(texture2D(bgl_DepthTexture,focus).x); 256 } 257 258 //dof blur factor calculation 259 260 float blur = 0.0; 261 262 if (manualdof) 263 { 264 float a = depth-fDepth; //focal plane 265 float b = (a-fdofstart)/fdofdist; //far DoF 266 float c = (-a-ndofstart)/ndofdist; //near Dof 267 blur = (a>0.0)?b:c; 268 } 269 270 else 271 { 272 float f = focalLength; //focal length in mm 273 float d = fDepth*1000.0; //focal plane in mm 274 float o = depth*1000.0; //depth in mm 275 276 float a = (o*f)/(o-f); 277 float b = (d*f)/(d-f); 278 float c = (d-f)/(d*fstop*CoC); 279 280 blur = abs(a-b)*c; 281 } 282 283 blur = clamp(blur,0.0,1.0); 284 285 // calculation of pattern for ditering 286 287 vec2 noise = rand(gl_TexCoord[0].xy)*namount*blur; 288 289 // getting blur x and y step factor 290 291 float w = (1.0/width)*blur*maxblur+noise.x; 292 float h = (1.0/height)*blur*maxblur+noise.y; 293 294 // calculation of final color 295 296 vec3 col = vec3(0.0); 297 298 if(blur < 0.05) //some optimization thingy 299 { 300 col = texture2D(bgl_RenderedTexture, gl_TexCoord[0].xy).rgb; 301 } 302 303 else 304 { 305 col = texture2D(bgl_RenderedTexture, gl_TexCoord[0].xy).rgb; 306 float s = 1.0; 307 int ringsamples; 308 309 for (int i = 1; i <= rings; i += 1) 310 { 311 ringsamples = i * samples; 312 313 for (int j = 0 ; j < ringsamples ; j += 1) 314 { 315 float step = PI*2.0 / float(ringsamples); 316 float pw = (cos(float(j)*step)*float(i)); 317 float ph = (sin(float(j)*step)*float(i)); 318 float p = 1.0; 319 if (pentagon) 320 { 321 p = penta(vec2(pw,ph)); 322 } 323 col += color(gl_TexCoord[0].xy + vec2(pw*w,ph*h),blur)*mix(1.0,(float(i))/(float(rings)),bias)*p; 324 s += 1.0*mix(1.0,(float(i))/(float(rings)),bias)*p; 325 } 326 } 327 col /= s; //divide by sample count 328 } 329 330 if (showFocus) 331 { 332 col = debugFocus(col, blur, depth); 333 } 334 335 if (vignetting) 336 { 337 col *= vignette(); 338 } 339 340 gl_FragColor.rgb = col; 341 gl_FragColor.a = 1.0; 342}