// From http://graphics.cs.aueb.gr/graphics/research_illumination.html
// "Real-Time Diffuse Global Illumination Using Radiance Hints"
// paper and shader code

uniform sampler2D ntex;
uniform sampler2D dtex;

uniform sampler3D SHR;
uniform sampler3D SHG;
uniform sampler3D SHB;

uniform vec3 extents;
uniform mat4 RHMatrix;
uniform mat4 InvRHMatrix;

vec4 SHBasis (const in vec3 dir)
{ 
    float   L00  = 0.282095;
    float   L1_1 = 0.488603 * dir.y;
    float   L10  = 0.488603 * dir.z;
    float   L11  = 0.488603 * dir.x;
    return vec4 (L11, L1_1, L10, L00);
}

vec3 SH2RGB (in vec4 sh_r, in vec4 sh_g, in vec4 sh_b, in vec3 dir)
{
    vec4 Y = vec4(1.023326*dir.x, 1.023326*dir.y, 1.023326*dir.z, 0.886226);
    return vec3 (dot(Y,sh_r), dot(Y,sh_g), dot(Y,sh_b));
}

out vec4 Diffuse;

vec3 DecodeNormal(vec2 n);
vec4 getPosFromUVDepth(vec3 uvDepth, mat4 InverseProjectionMatrix);

vec3 resolution = vec3(32, 16, 32);

void main()
{
    vec2 uv = gl_FragCoord.xy / screen;
    vec3 GI = vec3(0.);

    float depth = texture2D(dtex, uv).x;
    // Discard background fragments
    if (depth==1.0) discard;

    vec4 pos_screen_space = getPosFromUVDepth(vec3(uv, depth), InverseProjectionMatrix);
    vec4 tmp = (InvRHMatrix * InverseViewMatrix * pos_screen_space);
    vec3 pos = tmp.xyz / tmp.w;
    vec3 normal_screen_space = normalize(DecodeNormal(2. * texture(ntex, uv).xy - 1.));
    vec3 normal = (transpose(ViewMatrix) * vec4(normal_screen_space, 0.)).xyz;

    // Convert to grid coordinates
    vec3 uvw = .5 + 0.5 * pos / extents;
    if (uvw.x < 0. || uvw.x > 1. || uvw.y < 0. || uvw.y > 1. || uvw.z < 0. || uvw.z > 1.) discard;

    // Sample the RH volume at 4 locations, one directly above the shaded point,
    // three on a ring 80degs away from the normal direction.
    vec3 rnd = vec3(0,0,0);

    // Generate the sample locations
    vec3 v_rand = vec3(0.5);
    vec3 tangent = normalize(cross(normal, v_rand));
    vec3 bitangent = cross(normal, tangent);
    vec3 D[4];
    D[0] = vec3(1.0,0.0,0.0);
    int i;
    for (i=1; i<4; i++)
    {
        D[i] = vec3(0.1, 0.8*cos((rnd.x*1.5+i)*6.2832/3.0), 0.8*sin((rnd.x*1.5+i)*6.2832/3.0));
        D[i] = normalize(D[i]);
    }

    for (i=0; i<4; i++)
    {
        vec3 SampleDir = normal * D[i].x + tangent * D[i].y + bitangent *D[i].z;
        vec3 SampleOffset = (0.5 * normal + SampleDir) / resolution;
        vec3 uvw_new = uvw + SampleOffset;

        vec4 IncidentSHR = texture(SHR, uvw_new);
        vec4 IncidentSHG = texture(SHG, uvw_new);
        vec4 IncidentSHB = texture(SHB, uvw_new);

        GI += SH2RGB(IncidentSHR, IncidentSHG, IncidentSHB, -normal);
    }
    GI /= 4;

    Diffuse = max(16. * vec4(GI, 1.), vec4(0.));
}