summaryrefslogtreecommitdiffstats
path: root/res/effectlib/physGlossyBSDF.glsllib
blob: eabd6c50888f50375e6fef2415add00aa4fef64d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#ifndef PHYS_GLOSSY_BSDF_GLSLLIB
#define PHYS_GLOSSY_BSDF_GLSLLIB 1

float sqr(float v)
{
    return v*v;
}

float Gterm( float cosTheta, float roughness )
{
   float k = roughness * 0.31830988618;        // roughness / pi
   return clamp( ( cosTheta / (cosTheta*(1.0-k) + k) + (1.0 - k*k) ) * 0.5, 0.0, 1.0 );
}


// PKC -- I came up with an anisotropic microfacet BSDF that has some superficial similarity to GGX in
// its appearance, but is far simpler and more compact in its implementation.  It uses a Cauchy-like lobe
// shape and because it has an analytic solution to its antiderivative, we can compute the integral given
// the support boundaries.  It's also possible to importance sample at perfect efficiency.
// This is generally a good choice for any material which has a polish coating to it.
// TODO : The way roughness scales with this BSDF is roughly equivalent to roughness^2 would affect Ward.
// It's debatable whether we want that to be done as a sqrt here or as square in Ward.
vec4 kggxGlossyBSDF( in mat3 tanFrame, in vec3 L, in vec3 V, in vec3 lightSpecular, float ior,
                     in float roughnessU, in float roughnessV, int mode )
{
    vec4 rgba = vec4( 0.0f, 0.0f, 0.0f, 1.0f );
    vec3 H = normalize(L + V);

    // NOTE : This BSDF allows roughness up to 2.0 which allows it
    // to trend partially into Oren-Nayar diffuse territory, but we should
    // assume that anything that falls under "glossy" should still be
    // in the range of 0..1
    float ax = clamp(roughnessU, 0.0001, 2.0);
    float ay = clamp(roughnessV, 0.0001, 2.0);

    float NdotL = dot(tanFrame[2], L);
    float HdotL = clamp(dot(H, L), 0.0, 1.0);

 //   if (0.0f < NdotL)
 //   {
        vec3 Haf = L + V;

        float HdotN = clamp( dot(H, tanFrame[2]), 0.0001, 1.0 );
        float HdotX = clamp( abs(dot(H, tanFrame[0])), 0.0001, 1.0 );
        float HdotY = clamp( abs(dot(H, tanFrame[1])), 0.0001, 1.0 );

        float sigma = sqrt(ax * ay);
        float sigma2 = ax * ay * HdotN;

        float thetaI = acos( dot(V, tanFrame[2]) );
        float maxThetaI = (thetaI + 1.57079632679) * 0.5;
        float minThetaI = (thetaI - 1.57079632679) * 0.5;
        float range = atan(maxThetaI / sigma) - atan(minThetaI / sigma);
        range = max( range, ax*ay );

        if ( ( mode == scatter_reflect ) || ( mode == scatter_reflect_transmit ) )
        {
            float PDF = sigma2 / (sigma2 + sqr(HdotX / ax) + sqr(HdotY / ay));
            PDF *= dot(Haf, Haf) / (4.71238898038 * sqr(dot(Haf, L)) * ax*ay * sigma * sqr(range));

            rgba.rgb = Gterm(HdotL, sigma) * lightSpecular * PDF * max(NdotL, 0.0);
        }
        if ( ( mode == scatter_transmit ) || ( mode == scatter_reflect_transmit ) )
        {
            rgba.a = pow(1.0 - clamp(HdotL, 0.0, 1.0), 5.0);
        }
//    }
    return rgba;
}

vec4 kggxGlossyDefaultMtl( in vec3 normal, in vec3 tangent, in vec3 L, in vec3 V, in vec3 lightSpecular,
            in vec3 materialSpecular, in float roughU, in float roughV )
{
   vec3 bitan = normalize(cross(normal, tangent));
   mat3 tanFrame = mat3( normalize(cross( bitan, normal) ), bitan, normal );
   return vec4(materialSpecular, 1.0) * kggxGlossyBSDF( tanFrame, L, V, lightSpecular, 1.5, roughU, roughV, scatter_reflect );
}

// To be exact, this is not the Ward lobe as Ward originally described (there are a few flaws in
// the original paper, which had spawned half a dozen corrective measures as papers of their own).
// This is a Ward-Duer variant with Geisler-Moroder's modified normalization factor which serves
// to bound the albedo.
vec4 wardGlossyBSDF( in mat3 tanFrame, in vec3 L, in vec3 V, in vec3 lightSpecular, in float ior,
                     in float roughnessU, in float roughnessV, int mode )
{
    vec4 rgba = vec4( 0.0f, 0.0f, 0.0f, 1.0f );
    vec3 H = normalize(L + V);

    // specular
    float ax = clamp(roughnessU, 0.0001, 1.0);
    float ay = clamp(roughnessV, 0.0001, 1.0);

    float NdotL = dot(tanFrame[2], L);
    float HdotL = clamp(dot(H, L), 0.0, 1.0);

//    if (0.0f < NdotL)
//    {
        vec3 Haf = L + V;

        float HdotN = clamp( dot(H, tanFrame[2]), 0.0001, 1.0 );
        float HdotX = clamp( abs(dot(H, tanFrame[0])), 0.0001, 1.0 );
        float HdotY = clamp( abs(dot(H, tanFrame[1])), 0.0001, 1.0 );

        if ( ( mode == scatter_reflect ) || ( mode == scatter_reflect_transmit ) )
        {
            float exponent = -(sqr(HdotX/ax) + sqr(HdotY/ay));
            exponent /= sqr(HdotN);
            float PDF = exp(exponent) / (4.0 * 3.1415926535 * ax * ay);
            PDF *= 4.0 * dot(Haf, Haf) / sqr(sqr(dot(Haf,tanFrame[2])));

            rgba.rgb = Gterm(HdotL, sqrt(ax * ay)) * lightSpecular * PDF * max(NdotL, 0.0);
        }
        if ( ( mode == scatter_transmit ) || ( mode == scatter_reflect_transmit ) )
        {
            rgba.a = pow(1.0 - clamp(HdotL, 0.0, 1.0), 5.0);
        }
//    }
    return rgba;
}

vec4 wardGlossyDefaultMtl( in vec3 normal, in vec3 tangent, in vec3 L, in vec3 V, in vec3 lightSpecular,
            in vec3 materialSpecular, in float roughU, in float roughV )
{
   vec3 bitan = normalize(cross(normal, tangent));
   mat3 tanFrame = mat3( normalize(cross( bitan, normal) ), bitan, normal );
   return vec4(materialSpecular, 1.0) * wardGlossyBSDF( tanFrame, L, V, lightSpecular, 1.5, roughU, roughV, scatter_reflect );
}

#endif