precision highp float; varying vec2 v_TexCoords; uniform sampler2D u_Texture; uniform sampler2D u_Segmentation; uniform sampler2D u_InputMask; uniform sampler2D u_Gradient; uniform vec2 u_TextureSize; uniform vec2 u_RenderSize; uniform float u_Time; uniform mat4 u_STMatrix; uniform mat4 u_STMatrix1; // Tweakable uniforms uniform float uThickness; uniform float uInnerScale; uniform float uInnerScaleChange; uniform float uOuterScale; uniform float uOuterScaleChange; uniform float uSaturation; uniform float uBrightness; uniform float uContrast; uniform float uShapeBrightness; uniform float uRotationSpeed; uniform float uRotationRange; uniform float uGradientStretch; // uniforms from our script uniform float uFrameX; uniform float uFrameY; uniform float uHeadSize; uniform float uFrameScale; uniform float uFrameOffsetY; uniform float uFlickerProgress; #define TWO_PI 6.2831853071795864769252867665590 float mirror(in float x) { float f = fract(x); float m = floor(mod(x, 2.)); float fm = f * m; return f + m - fm * 2.; } vec2 mirror(in vec2 xy) { vec2 f = fract(xy); vec2 m = floor(mod(xy, 2.)); vec2 fm = f * m; return f + m - fm * 2.; } float gridSDF(in vec2 st) { vec2 grid = mirror(st); return max(grid.x, grid.y); } vec2 cartesian2polar(in vec2 st) { st = st * 2. - 1.; return vec2(length(st), atan(st.y, st.x)); } float luma(vec3 v) { return dot(v, vec3(0.2989, 0.5870, 0.1140)); } float ramp(float edge0, float edge1, float x) { return clamp( (x - edge0) / (edge1 - edge0), 0.0, 1.0 ); } vec2 rotate(vec2 v, float a) { float s = sin(a); float c = cos(a); mat2 m = mat2(c, -s, s, c); return m * v; } vec2 rotateUV(vec2 uv, float rotation, vec2 mid) { return vec2( cos(rotation) * (uv.x - mid.x) + sin(rotation) * (uv.y - mid.y) + mid.x, cos(rotation) * (uv.y - mid.y) - sin(rotation) * (uv.x - mid.x) + mid.y ); } vec2 transformTexCoords(in mat4 transform, in vec2 v) { vec4 tmp = transform * vec4( v, 0., 1. ); return tmp.st / tmp.q; } mat4 rotate4d(in vec3 axis, in float radians) { axis = normalize(axis); float s = sin(radians); float c = cos(radians); float oc = 1.0 - c; return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0, oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0, oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0, 0.0, 0.0, 0.0, 1.0); } mat4 rotate4dY(in float theta){ return mat4( vec4(cos(theta),0.,-sin(theta),0), vec4(0.,1.,0.,0.), vec4(sin(theta),0.,cos(theta),0.), vec4(0.,0.,0.,1.)); } vec3 rotateY(in vec3 pos, in float radian, in vec3 center) { return (rotate4dY(radian) * vec4((pos - center), 1.)).xyz + center; } vec3 rotateY(in vec3 pos, in float radian) { #ifdef CENTER_3D return rotateY(pos, radian, CENTER_3D); #else return rotateY(pos, radian, vec3(.0)); #endif } // Color transformation and Blending Functions // ------------------------------------------- vec3 desaturate(vec3 color, float amount) { vec3 luma = vec3(0.3, 0.59, 0.11); vec3 gray = vec3(dot(luma, color)); return mix(color, gray, amount); } vec3 blendSoftLight(vec3 base, vec3 blend) { return mix( sqrt(base) * (2.0 * blend - 1.0) + 2.0 * base * (1.0 - blend), 2.0 * base * blend + base * base * (1.0 - 2.0 * blend), step(base, vec3(0.5)) ); } vec3 blendHardLight(vec3 base, vec3 blend) { return vec3( blend.r < 0.5 ? (2.0 * base.r * blend.r) : (1.0 - 2.0 * (1.0 - base.r) * (1.0 - blend.r)), blend.g < 0.5 ? (2.0 * base.g * blend.g) : (1.0 - 2.0 * (1.0 - base.g) * (1.0 - blend.g)), blend.b < 0.5 ? (2.0 * base.b * blend.b) : (1.0 - 2.0 * (1.0 - base.b) * (1.0 - blend.b)) ); } vec3 blendAdd(vec3 base, vec3 blend) { return min(base+blend,vec3(1.0)); } float blendScreen(float base, float blend) { return 1.0-((1.0-base)*(1.0-blend)); } vec3 blendScreen(vec3 base, vec3 blend) { return vec3(blendScreen(base.r,blend.r),blendScreen(base.g,blend.g),blendScreen(base.b,blend.b)); } vec3 hsv2rgb(vec3 c) { vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } vec3 rgb2hsv(in vec3 c) { vec4 K = vec4(0., -.33333333333333333333, .6666666666666666666, -1.); #ifdef RGB2HSV_MIX vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); #else vec4 p = c.g < c.b ? vec4(c.bg, K.wz) : vec4(c.gb, K.xy); vec4 q = c.r < p.x ? vec4(p.xyw, c.r) : vec4(c.r, p.yzx); #endif float d = q.x - min(q.w, q.y); float e = 1.0e-10; return vec3(abs(q.z + (q.w - q.y) / (6. * d + e)), d / (q.x + e), q.x); } vec4 rgb2hsv(in vec4 c) { return vec4(rgb2hsv(c.rgb), c.a); } vec3 enhance(vec3 color, float brightness, float saturation, float contrast) { // enhance saturation float lum = luma(color.rgb); color = clamp(color + saturation * (color - lum), 0.05, 1.0); // enhance brightness color = clamp(color * (1.0 + brightness), 0.0, 1.0); // enhance contrast by blending towards the s-curve vec3 scurve = smoothstep(0.0, 1.0, color); color = mix(color, scurve, contrast); return color; } // Frame and SDF drawing functions // ------------------------------------------ float ndot(vec2 a, vec2 b ) { return a.x*b.x - a.y*b.y; } vec3 drawCircle(vec2 st, vec2 center, float radius, float thickness, float smoothness){ float rad = radius * 0.5; float circle = smoothstep(thickness + smoothness, thickness + 0.0025 - smoothness, abs(length(st - center) - rad)); return vec3(circle); } vec3 haloRing(vec2 st, vec2 center, float radius, float thickness, float smoothness) { float rad = radius * 0.5; float halo = clamp(-(abs(length(st-center) - rad) * 100.0 / thickness) + 0.9, 0.0, 1.0); return vec3(halo); } vec3 drawLine(vec2 uv, vec2 coordA, vec2 coordB, float thickness, float smoothness) { vec2 pa = uv - coordA; vec2 ba = coordB - coordA; float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 ); float d = length( pa - ba*h ); float smth = smoothness; float line = smoothstep(thickness + smth, thickness - smth, d); return vec3(line); } vec3 drawLinePos(vec2 st, vec2 center, vec2 coordA, vec2 coordB, float thickness, float smoothness) { vec2 pos = center - st; vec2 pa = pos - coordA; vec2 ba = coordB - coordA; float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 ); float d = length( pa - ba*h ); float smth = smoothness; float line = smoothstep(thickness + smth, thickness - smth, d); return vec3(line); } float circle(float x, float h, float k, float r) { // (x - h)^2 + (y-k)^2 = r^2 return sqrt(r * r - (x - h) * (x - h)) + k; } float gain(float x, float k) { float a = 0.5 * pow(2.0 * ((x < 0.5) ? x:1.0 - x), k); return (x < 0.5) ? a : 1.0 - a; } float clampedGain(float x, float k) { if (x <= 0.0) return x; if (x >= 1.0) return x; float a = 0.5 * pow(2.0 * mix(x, 1.0 - x, step(0.5, x)), k); return mix(a, 1.0 - a, smoothstep(0.49, 0.51, x)); } void main() { vec2 uv = (u_STMatrix * vec4(v_TexCoords.x, v_TexCoords.y, 0.0, 1.0)).xy; vec2 segUV = (u_STMatrix1 * vec4(v_TexCoords.x, v_TexCoords.y, 0.0, 1.0)).xy; // Calculate flickering float t = uFlickerProgress * TWO_PI; float flicker = max(cos(t) * cos(t * 4.0) * 0.75 + 0.25, 0.0); // Calculate how frame scale will change with face distance // a higher pow will make the scale change faster with distance. 2.0 is the pysically correct one float rad = uInnerScale * pow(uFrameScale, uInnerScaleChange); float rad2 = uOuterScale * pow(uFrameScale, uOuterScaleChange); // Calculate frame shape vec2 res = u_RenderSize.xy / u_RenderSize.y; vec2 frameUv = uv * res; vec2 center = vec2(uFrameX, uFrameY) * res; float brightness = uShapeBrightness * flicker; vec3 frameCenter; vec3 frameSharp; vec3 frameGlow; // Rotate the UV's for a 45 degree gradient vec2 angledUv = rotateUV((uv - 0.5) * res, -0.867, vec2(0.0)) + 0.5; float displacement = sin(u_Time * uRotationSpeed * 5.0) * uRotationRange; // The gradient texture is equally spaced so we need to "stretch" the middle const float scale = 1.35; const float offset = -0.01; angledUv.x = (angledUv.x - 0.5) * scale + 0.5 + offset; angledUv.x = clamp(angledUv.x, 0.01, 0.99); angledUv.x = gain(angledUv.x , uGradientStretch); angledUv.x += displacement; vec3 clrMixed = texture2D(u_Gradient, angledUv).rgb; //frame //frame //Bottom lines frameCenter = drawLine(frameUv, vec2(-1.0, -1.0), vec2(1.0, 0.7), uThickness * 0.55, uThickness * 0.55) * brightness; frameSharp = drawLine(frameUv, vec2(-1.0, -1.0), vec2(1.0, 0.7), uThickness * 0.35, uThickness * 4.0) * brightness; frameGlow = drawLine(frameUv, vec2(-1.0, -1.0), vec2(1.0, 0.7), 0.03, 0.1) * 0.75 * mix(brightness, 1.0, 0.5); frameCenter += drawLine(frameUv, vec2(-1.0, -1.1), vec2(1.0, 0.6), uThickness * 0.55, uThickness * 0.55) * brightness; frameSharp += drawLine(frameUv, vec2(-1.0, -1.1), vec2(1.0, 0.6), uThickness * 0.35, uThickness * 4.0) * brightness; frameGlow += drawLine(frameUv, vec2(-1.0, -1.1), vec2(1.0, 0.6), 0.03, 0.1) * 0.75 * mix(brightness, 1.0, 0.5); vec3 frameFront = min(frameSharp + frameGlow, 1.0) * clrMixed + min(frameCenter, 1.0) * mix(clrMixed, vec3(1.0), 0.75); frameFront = clamp(frameFront, 0., 1.); // Top lines frameCenter = drawLine(frameUv, vec2(-1.0, -0.25), vec2(1.0, 1.45), uThickness * 0.55, uThickness * 0.55) * brightness; frameSharp = drawLine(frameUv, vec2(-1.0, -0.25), vec2(1.0, 1.45), uThickness * 0.35, uThickness * 4.0) * brightness; frameGlow = drawLine(frameUv, vec2(-1.0, -0.25), vec2(1.0, 1.45), 0.03, 0.1) * 0.75 * mix(brightness, 1.0, 0.5); frameCenter += drawLine(frameUv, vec2(-1.0, -0.1), vec2(1.0, 1.6), uThickness * 0.55, uThickness * 0.55) * brightness; frameSharp += drawLine(frameUv, vec2(-1.0, -0.1), vec2(1.0, 1.6), uThickness * 0.35, uThickness * 4.0) * brightness; frameGlow += drawLine(frameUv, vec2(-1.0, -0.1), vec2(1.0, 1.6), 0.03, 0.1) * 0.75 * mix(brightness, 1.0, 0.5); vec3 frameBack = min(frameSharp + frameGlow, 1.0) * clrMixed + min(frameCenter, 1.0) * mix(clrMixed, vec3(1.0), 0.75); frameBack = clamp(frameBack, 0., 1.); // add color wash to the whole frame float screenVignette = (1.0 - length(uv - vec2(0.5))) * 0.2 + 0.8; float faceVignette = (length(uv - center) * 0.25) * 0.65 + 0.1; vec3 colorWashHsv = vec3(rgb2hsv(clrMixed).x, faceVignette * screenVignette, screenVignette); vec3 colorWash = hsv2rgb(colorWashHsv); // apply segmentation const float e = 0.03; float segGradient = smoothstep(center.y - e, center.y + e, uv.y); float segMask = texture2D(u_Segmentation, segUV).r; vec3 segmentedFrame = frameBack * (1.0 - segMask * segGradient) + frameFront; // Face relighting vec4 camImage = texture2D(u_Texture, uv); camImage.rgb = enhance(camImage.rgb, uBrightness, uSaturation, uContrast); vec4 rimMask = texture2D(u_InputMask, uv); vec3 faceColor = clrMixed * rimMask.rgb * 2.0 * flicker; float lum = pow(luma(camImage.rgb), 0.7);// * pow(polarUv.x, 0.4); // Blend everything vec3 camBlend = blendScreen(camImage.rgb, faceColor * lum); // vec3 washBlend = mix(camBlend, camBlend * colorWash.rgb, polarUv.x); vec3 washBlend = mix(camBlend, camBlend * colorWash.rgb, 1.0); vec3 finalBlend = blendScreen(washBlend, segmentedFrame); gl_FragColor = vec4(finalBlend.rgb, 1.0); }