//
//  EditEnhance_f.shader
//  BeautyPlus
//
//  Created by Webster Wu on 2/18/16.
//  Copyright © 2016 美图网. All rights reserved.
//

#ifdef GL_ES//for discriminate GLES & GL
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
#else
#define highp
#define mediump
#define lowp
#endif

uniform sampler2D inputImageTexture0; //rendered scene sampler

uniform float textureWidth; //scene sampler width
uniform float textureHeight; //scene sampler height
uniform float grainSize; //grain particle size (1.5 - 2.5)
uniform float grainAmount; //grain amount

varying vec2 texcoordOut;

float timer = 0.5;          //这里应该是随机数种子

const float permTexUnit = 1.0 / 256.0;        // Perm texture texel-size
const float permTexUnitHalf = 0.5 / 256.0;    // Half perm texture texel-size

//bool colored = false; //colored noise?
float coloramount = 0.6;
float lumamount = 1.0; //

//a random texture generator, but you can also use a pre-computed perturbation texture
vec4 rnm(in vec2 tc)
{
    float noise =  sin(dot(tc + vec2(timer,timer),vec2(12.9898,78.233))) * 43758.5453;
    
    float noiseR = fract(noise) * 2.0 - 1.0;
    float noiseG = fract(noise * 1.2154) * 2.0 - 1.0;
    float noiseB = fract(noise * 1.3453) * 2.0 - 1.0;
    float noiseA = fract(noise * 1.3647) * 2.0 - 1.0;
    
    return vec4(noiseR,noiseG,noiseB,noiseA);
}

float fade(in float t) {
    return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
}

float pnoise3D(in vec3 p)
{
    vec3 pi = permTexUnit*floor(p)+permTexUnitHalf; // Integer part, scaled so +1 moves permTexUnit texel
    // and offset 1/2 texel to sample texel centers
    vec3 pf = fract(p);     // Fractional part for interpolation
    
    // Noise contributions from (x=0, y=0), z=0 and z=1
    float perm00 = rnm(pi.xy).a ;
    vec3  grad000 = rnm(vec2(perm00, pi.z)).rgb * 4.0 - 1.0;
    float n000 = dot(grad000, pf);
    vec3  grad001 = rnm(vec2(perm00, pi.z + permTexUnit)).rgb * 4.0 - 1.0;
    float n001 = dot(grad001, pf - vec3(0.0, 0.0, 1.0));
    
    // Noise contributions from (x=0, y=1), z=0 and z=1
    float perm01 = rnm(pi.xy + vec2(0.0, permTexUnit)).a ;
    vec3  grad010 = rnm(vec2(perm01, pi.z)).rgb * 4.0 - 1.0;
    float n010 = dot(grad010, pf - vec3(0.0, 1.0, 0.0));
    vec3  grad011 = rnm(vec2(perm01, pi.z + permTexUnit)).rgb * 4.0 - 1.0;
    float n011 = dot(grad011, pf - vec3(0.0, 1.0, 1.0));
    
    // Noise contributions from (x=1, y=0), z=0 and z=1
    float perm10 = rnm(pi.xy + vec2(permTexUnit, 0.0)).a ;
    vec3  grad100 = rnm(vec2(perm10, pi.z)).rgb * 4.0 - 1.0;
    float n100 = dot(grad100, pf - vec3(1.0, 0.0, 0.0));
    vec3  grad101 = rnm(vec2(perm10, pi.z + permTexUnit)).rgb * 4.0 - 1.0;
    float n101 = dot(grad101, pf - vec3(1.0, 0.0, 1.0));
    
    // Noise contributions from (x=1, y=1), z=0 and z=1
    float perm11 = rnm(pi.xy + vec2(permTexUnit, permTexUnit)).a ;
    vec3  grad110 = rnm(vec2(perm11, pi.z)).rgb * 4.0 - 1.0;
    float n110 = dot(grad110, pf - vec3(1.0, 1.0, 0.0));
    vec3  grad111 = rnm(vec2(perm11, pi.z + permTexUnit)).rgb * 4.0 - 1.0;
    float n111 = dot(grad111, pf - vec3(1.0, 1.0, 1.0));
    
    // Blend contributions along x
    vec4 n_x = mix(vec4(n000, n001, n010, n011), vec4(n100, n101, n110, n111), fade(pf.x));
    
    // Blend contributions along y
    vec2 n_xy = mix(n_x.xy, n_x.zw, fade(pf.y));
    
    // Blend contributions along z
    float n_xyz = mix(n_xy.x, n_xy.y, fade(pf.z));
    
    // We're done, return the final noise value.
    return n_xyz;
}

//Simplex 3D Noise
vec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}
vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}

//2d coordinate orientation thing
vec2 coordRot(in vec2 tc, in float angle)
{
    float aspect = textureWidth/textureHeight;
    float rotX = ((tc.x*2.0-1.0)*aspect*cos(angle)) - ((tc.y*2.0-1.0)*sin(angle));
    float rotY = ((tc.y*2.0-1.0)*cos(angle)) + ((tc.x*2.0-1.0)*aspect*sin(angle));
    rotX = ((rotX/aspect)*0.5+0.5);
    rotY = rotY*0.5+0.5;
    return vec2(rotX,rotY);
}

float compare(float a, float x, float y)
{
    if (a < 0.0)
        return x;
    else
        return y;
}

void main()
{
    vec3 rotOffset = vec3(1.425,3.892,5.835); //rotation offset values
    vec2 rotCoordsR = coordRot(texcoordOut, timer + rotOffset.x);
//    vec3 noise = vec3(pnoise3D(vec3(rotCoordsR*vec2(textureWidth/grainSize,textureHeight/grainSize),0.0)));
    vec3 noise = vec3(pnoise3D(vec3(rotCoordsR*vec2(grainSize * textureWidth / textureHeight, grainSize), 0.0)));
    
    vec3 col = texture2D(inputImageTexture0, texcoordOut).rgb;
    
    //noisiness response curve based on scene luminance
    vec3 lumcoeff = vec3(0.299,0.587,0.114);
    float luminance = mix(0.0,dot(col, lumcoeff),lumamount);

    float coef4thOrder = 1.8133;
    float coef3rdOrder = -5.8133;
    float coef2ndOrder = 1.8867;
    float coef1stOrder = 2.2633;
    float coefConstant = 0.0;
    
    // New curve that reduce graininess in shadow and hightlight
    float grayCurve = coef4thOrder * pow(luminance, 4.0) + coef3rdOrder * pow(luminance, 3.0) + coef2ndOrder * pow(luminance, 2.0) + coef1stOrder * luminance+ coefConstant;
    grayCurve = compare(grayCurve - 1.0, compare(grayCurve, 0.0, grayCurve), 1.0);
    noise = mix(vec3(0.0), noise,grayCurve);
    col = col + noise * grainAmount;
    
    gl_FragColor =  vec4(col,1.0);
}
