|
@@ -1,7 +1,7 @@
|
|
|
/*
|
|
|
BABYLON.JS Depth-of-field GLSL Shader
|
|
|
Author: Olivier Guyot
|
|
|
- Does depth-of-field blur, edge blur, highlights enhancing
|
|
|
+ Does depth-of-field blur, edge blur
|
|
|
Inspired by Francois Tarlier & Martins Upitis
|
|
|
*/
|
|
|
|
|
@@ -12,12 +12,12 @@ precision highp float;
|
|
|
|
|
|
// samplers
|
|
|
uniform sampler2D textureSampler;
|
|
|
+uniform sampler2D highlightsSampler;
|
|
|
uniform sampler2D depthSampler;
|
|
|
uniform sampler2D grainSampler;
|
|
|
|
|
|
// uniforms
|
|
|
uniform float grain_amount;
|
|
|
-uniform bool pentagon;
|
|
|
uniform float maxZ;
|
|
|
uniform bool blur_noise;
|
|
|
uniform float screen_width;
|
|
@@ -25,24 +25,14 @@ uniform float screen_height;
|
|
|
uniform float distortion;
|
|
|
uniform float focus_depth;
|
|
|
uniform float aperture;
|
|
|
-uniform float gain;
|
|
|
-uniform float threshold;
|
|
|
uniform float edge_blur;
|
|
|
+uniform bool highlights;
|
|
|
|
|
|
// varyings
|
|
|
varying vec2 vUV;
|
|
|
|
|
|
// constants
|
|
|
#define PI 3.14159265
|
|
|
-const int RING_1_SAMPLES = 4;
|
|
|
-const int RING_2_SAMPLES = 6;
|
|
|
-const int RING_3_SAMPLES = 9;
|
|
|
-const int RING_4_SAMPLES = 12;
|
|
|
-const int RING_5_SAMPLES = 16;
|
|
|
-//const int RING_6_SAMPLES = 15;
|
|
|
-const float RING_STEP_DIST = 0.4; // a new blur ring is added each time this distance is passed
|
|
|
-const float PENTAGON_ANGLE_SUB = 1.2566; // 2PI / 5
|
|
|
-const float PENTAGON_ANGLE_SUB_HALF = 0.6283; // 2PI / 10
|
|
|
|
|
|
// common calculations
|
|
|
vec2 centered_screen_pos;
|
|
@@ -66,137 +56,67 @@ vec2 getDistortedCoords(vec2 coords) {
|
|
|
return dist_coords;
|
|
|
}
|
|
|
|
|
|
-// picks either original screen color or highlights only
|
|
|
-vec4 getColor(vec2 coords, bool highlight) {
|
|
|
-
|
|
|
- vec4 color = texture2D(textureSampler, coords);
|
|
|
-
|
|
|
- if(highlight) {
|
|
|
- float luminance = dot(color.rgb, vec3(0.2125, 0.7154, 0.0721));
|
|
|
- float lum_threshold;
|
|
|
- if(threshold > 1.0) { lum_threshold = 0.94 + 0.01 * threshold; }
|
|
|
- else { lum_threshold = 0.5 + 0.44 * threshold; }
|
|
|
- if(luminance < lum_threshold) {
|
|
|
- color.rgb = vec3(0.0, 0.0, 0.0);
|
|
|
- color.a = 1.0;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return color;
|
|
|
-}
|
|
|
+// returns original screen color after blur
|
|
|
+vec4 getBlurColor(vec2 coords, float size) {
|
|
|
|
|
|
-// returns a modifier to be applied on the radius, in order to simulate a pentagon
|
|
|
-float pentagonShape(float angle) {
|
|
|
- float a1 = mod(angle, PENTAGON_ANGLE_SUB) / PENTAGON_ANGLE_SUB - 0.5;
|
|
|
- float a2 = 0.5 - a1 * a1;
|
|
|
- return 1.35 - 0.94 * a2;
|
|
|
-}
|
|
|
+ vec4 col = texture2D(textureSampler, coords);
|
|
|
+ if(size == 0.0) { return col; }
|
|
|
|
|
|
-// returns original screen color after blur
|
|
|
-vec4 getBlurColor(vec2 coords, float size, bool highlight) {
|
|
|
+ // there are max. 30 samples; the number of samples chosen is dependant on the blur size
|
|
|
+ // there can be 10, 20 or 30 samples chosen; levels of blur are then 1, 2 or 3
|
|
|
+ float blur_level = min(3.0, ceil(size / 1.0));
|
|
|
|
|
|
float w = (size/screen_width);
|
|
|
float h = (size/screen_height);
|
|
|
+ float total_weight = 1.0;
|
|
|
+
|
|
|
+ col += texture2D(textureSampler, coords + vec2(-0.53*w, 0.15*h))*0.93;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(0.42*w, -0.69*h))*0.90;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(0.20*w, 1.00*h))*0.87;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(-0.97*w, -0.72*h))*0.85;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(1.37*w, -0.14*h))*0.83;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(-1.02*w, 1.16*h))*0.80;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(-0.03*w, -1.69*h))*0.78;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(1.27*w, 1.34*h))*0.76;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(-1.98*w, -0.14*h))*0.74;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(1.66*w, -1.32*h))*0.72;
|
|
|
+ total_weight += 8.18;
|
|
|
+
|
|
|
+ if(blur_level > 1.0) {
|
|
|
+ col += texture2D(textureSampler, coords + vec2(-0.35*w, 2.22*h))*0.70;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(-1.31*w, -1.98*h))*0.67;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(2.42*w, 0.61*h))*0.65;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(-2.31*w, 1.25*h))*0.63;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(0.90*w, -2.59*h))*0.61;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(1.14*w, 2.62*h))*0.59;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(-2.72*w, -1.21*h))*0.56;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(2.93*w, -0.98*h))*0.54;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(-1.56*w, 2.80*h))*0.52;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(-0.77*w, -3.22*h))*0.49;
|
|
|
+ total_weight += 5.96;
|
|
|
+ }
|
|
|
|
|
|
- vec4 col = getColor(coords, highlight);
|
|
|
- if(size == 0.0) { return col; }
|
|
|
-
|
|
|
- float s = 1.0;
|
|
|
- float pw; // sample x relative coord
|
|
|
- float ph; // sample y relative coord
|
|
|
- float bias = 0.65; // inner/outer ring bias
|
|
|
- if(highlight) { bias = 0.95; }
|
|
|
- float sample_angle;
|
|
|
- float ratio_rings;
|
|
|
- float ring_radius;
|
|
|
- float penta; // pentagon shape modifier
|
|
|
-
|
|
|
- int ring_count;
|
|
|
- if(size >= 6.0 * RING_STEP_DIST) { ring_count = 6; }
|
|
|
- else if(size >= 5.0 * RING_STEP_DIST) { ring_count = 5; }
|
|
|
- else if(size >= 4.0 * RING_STEP_DIST) { ring_count = 4; }
|
|
|
- else if(size >= 3.0 * RING_STEP_DIST) { ring_count = 3; }
|
|
|
- else if(size >= 2.0 * RING_STEP_DIST) { ring_count = 2; }
|
|
|
- else { ring_count = 1; }
|
|
|
-
|
|
|
- // RING 1
|
|
|
- if(size > RING_STEP_DIST) {
|
|
|
- ring_radius = size / float(ring_count);
|
|
|
- ratio_rings = 1.0 / float(ring_count);
|
|
|
- for(int i = 0; i < RING_1_SAMPLES; i++) {
|
|
|
- sample_angle = PI *2.0 * float(i) / float(RING_1_SAMPLES);
|
|
|
- if(pentagon) { penta = pentagonShape(sample_angle); }
|
|
|
- else { penta = 1.0; }
|
|
|
- pw = cos( sample_angle ) * penta * ring_radius;
|
|
|
- ph = sin( sample_angle ) * penta * ring_radius;
|
|
|
- col += getColor(coords + vec2(pw*w,ph*h), highlight) * mix( 1.0, ratio_rings, bias );
|
|
|
- s += 1.0 * mix(1.0, ratio_rings, bias);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // RING 2
|
|
|
- if(size > RING_STEP_DIST * 2.0) {
|
|
|
- ring_radius = 2.0 * size / float(ring_count);
|
|
|
- ratio_rings = 2.0 / float(ring_count);
|
|
|
- for(int i = 0; i < RING_2_SAMPLES; i++) {
|
|
|
- sample_angle = PI *2.0 * float(i) / float(RING_2_SAMPLES);
|
|
|
- if(pentagon) { penta = pentagonShape(sample_angle); }
|
|
|
- else { penta = 1.0; }
|
|
|
- pw = cos( sample_angle ) * penta * ring_radius;
|
|
|
- ph = sin( sample_angle ) * penta * ring_radius;
|
|
|
- col += getColor(coords + vec2(pw*w,ph*h), highlight) * mix( 1.0, ratio_rings, bias );
|
|
|
- s += 1.0 * mix(1.0, ratio_rings, bias);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // RING 3
|
|
|
- if(size > RING_STEP_DIST * 3.0) {
|
|
|
- ring_radius = 3.0 * size / float(ring_count);
|
|
|
- ratio_rings = 3.0 / float(ring_count);
|
|
|
- for(int i = 0; i < RING_3_SAMPLES; i++) {
|
|
|
- sample_angle = PI *2.0 * float(i) / float(RING_3_SAMPLES);
|
|
|
- if(pentagon) { penta = pentagonShape(sample_angle); }
|
|
|
- else { penta = 1.0; }
|
|
|
- pw = cos( sample_angle ) * penta * ring_radius;
|
|
|
- ph = sin( sample_angle ) * penta * ring_radius;
|
|
|
- col += getColor(coords + vec2(pw*w,ph*h), highlight) * mix( 1.0, ratio_rings, bias );
|
|
|
- s += 1.0 * mix(1.0, ratio_rings, bias);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // RING 4
|
|
|
- if(size > RING_STEP_DIST * 4.0) {
|
|
|
- ring_radius = 4.0 * size / float(ring_count);
|
|
|
- ratio_rings = 4.0 / float(ring_count);
|
|
|
- for(int i = 0; i < RING_4_SAMPLES; i++) {
|
|
|
- sample_angle = PI *2.0 * float(i) / float(RING_4_SAMPLES);
|
|
|
- if(pentagon) { penta = pentagonShape(sample_angle); }
|
|
|
- else { penta = 1.0; }
|
|
|
- pw = cos( sample_angle ) * penta * ring_radius;
|
|
|
- ph = sin( sample_angle ) * penta * ring_radius;
|
|
|
- col += getColor(coords + vec2(pw*w,ph*h), highlight) * mix( 1.0, ratio_rings, bias );
|
|
|
- s += 1.0 * mix(1.0, ratio_rings, bias);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // RING 5
|
|
|
- if(size > RING_STEP_DIST * 5.0) {
|
|
|
- ring_radius = 5.0 * size / float(ring_count);
|
|
|
- ratio_rings = 5.0 / float(ring_count);
|
|
|
- for(int i = 0; i < RING_5_SAMPLES; i++) {
|
|
|
- sample_angle = PI *2.0 * float(i) / float(RING_5_SAMPLES);
|
|
|
- if(pentagon) { penta = pentagonShape(sample_angle); }
|
|
|
- else { penta = 1.0; }
|
|
|
- pw = cos( sample_angle ) * penta * ring_radius;
|
|
|
- ph = sin( sample_angle ) * penta * ring_radius;
|
|
|
- col += getColor(coords + vec2(pw*w,ph*h), highlight) * mix( 1.0, ratio_rings, bias );
|
|
|
- s += 1.0 * mix(1.0, ratio_rings, bias);
|
|
|
- }
|
|
|
- }
|
|
|
+ if(blur_level > 2.0) {
|
|
|
+ col += texture2D(textureSampler, coords + vec2(2.83*w, 1.92*h))*0.46;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(-3.49*w, 0.51*h))*0.44;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(2.30*w, -2.82*h))*0.41;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(0.22*w, 3.74*h))*0.38;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(-2.76*w, -2.68*h))*0.34;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(3.95*w, 0.11*h))*0.31;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(-3.07*w, 2.65*h))*0.26;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(0.48*w, -4.13*h))*0.22;
|
|
|
+ col += texture2D(textureSampler, coords + vec2(2.49*w, 3.46*h))*0.15;
|
|
|
+ total_weight += 2.97;
|
|
|
+ }
|
|
|
|
|
|
- col /= s; // scales color according to samples taken
|
|
|
+ col /= total_weight; // scales color according to weights
|
|
|
col.a = 1.0;
|
|
|
|
|
|
+ // blur levels debug
|
|
|
+ // if(blur_level == 1.0) { col.b = 0.0; }
|
|
|
+ // if(blur_level == 2.0) { col.r = 0.0; }
|
|
|
+ // if(blur_level == 3.0) { col.g = 0.0; }
|
|
|
+
|
|
|
return col;
|
|
|
}
|
|
|
|
|
@@ -222,13 +142,17 @@ void main(void)
|
|
|
|
|
|
// blur from depth of field effect
|
|
|
float dof_blur_amount = 0.0;
|
|
|
+ float depth_bias = 0.0; // positive if the pixel is further than focus depth; negative if closer
|
|
|
if(focus_depth != -1.0) {
|
|
|
vec4 depth_sample = texture2D(depthSampler, distorted_coords);
|
|
|
float depth = depth_sample.r;
|
|
|
- dof_blur_amount = abs(depth - focus_depth) * aperture * 3.5;
|
|
|
- if(dof_blur_amount < 0.05) { dof_blur_amount = 0.0; } // no blur at all
|
|
|
- else if( depth - focus_depth < 0.0 ) { dof_blur_amount *= 2.0; } // blur more when close to camera
|
|
|
- dof_blur_amount = clamp(dof_blur_amount, 0.0, 1.0);
|
|
|
+ depth_bias = depth - focus_depth;
|
|
|
+
|
|
|
+ // compute blur amount with distance
|
|
|
+ if( depth_bias > 0.0 ) { dof_blur_amount = depth_bias * aperture * 2.2; }
|
|
|
+ else { dof_blur_amount = depth_bias * depth_bias * aperture * 30.0; }
|
|
|
+
|
|
|
+ if(dof_blur_amount < 0.05) { dof_blur_amount = 0.0; } // no blur at all
|
|
|
}
|
|
|
|
|
|
// blur from edge blur effect
|
|
@@ -242,19 +166,26 @@ void main(void)
|
|
|
|
|
|
// apply blur if necessary
|
|
|
if(blur_amount == 0.0) {
|
|
|
- gl_FragColor = getColor(distorted_coords, false);
|
|
|
+ gl_FragColor = texture2D(textureSampler, distorted_coords);
|
|
|
} else {
|
|
|
- gl_FragColor = getBlurColor(distorted_coords, blur_amount * 1.7, false)
|
|
|
- + gain * blur_amount*getBlurColor(distorted_coords, blur_amount * 2.75, true);
|
|
|
+
|
|
|
+ // add blurred color
|
|
|
+ gl_FragColor = getBlurColor(distorted_coords, blur_amount * 1.7);
|
|
|
+
|
|
|
+ // if further than focus depth & we have computed highlights: enhance highlights
|
|
|
+ if(depth_bias > 0.0 && highlights) {
|
|
|
+ gl_FragColor += clamp(dof_blur_amount, 0.0, 1.0)*texture2D(highlightsSampler, distorted_coords);
|
|
|
+ }
|
|
|
|
|
|
if(blur_noise) {
|
|
|
// we put a slight amount of noise in the blurred color
|
|
|
vec2 noise = rand(distorted_coords) * 0.01 * blur_amount;
|
|
|
vec2 blurred_coord = vec2(distorted_coords.x + noise.x, distorted_coords.y + noise.y);
|
|
|
- gl_FragColor = 0.04 * getColor(blurred_coord, false) + 0.96 * gl_FragColor;
|
|
|
+ gl_FragColor = 0.04 * texture2D(textureSampler, blurred_coord) + 0.96 * gl_FragColor;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // apply grain
|
|
|
if(grain_amount > 0.0) {
|
|
|
vec4 grain_color = texture2D(grainSampler, texels_coords*0.003);
|
|
|
gl_FragColor.rgb += ( -0.5 + grain_color.rgb ) * 0.20;
|