/************************************************************************
 * locillum.h - Functions that compute the light response of locillums.
 *
 * Author: Larry Gritz (gritzl@acm.org)
 *
 * Reference:
 *   _Advanced RenderMan: Creating CGI for Motion Picture_, 
 *   by Anthony A. Apodaca and Larry Gritz, Morgan Kaufmann, 1999.
 *
 * $Revision: 1.1 $    $Date: 2004/05/19 18:15:19 $
 *
 ************************************************************************/


#ifndef LOCILLUM_H
#define LOCILLUM_H




/*
 * Oren and Nayar's generalization of Lambert's reflection model.
 * The roughness parameter gives the standard deviation of angle
 * orientations of the presumed surface grooves.  When roughness=0,
 * the model is identical to Lambertian reflection.
 */
color
LocIllumOrenNayar (normal N;  vector V;  float roughness;)
{
    /* Surface roughness coefficients for Oren/Nayar's formula */
    float sigma2 = roughness * roughness;
    float A = 1 - 0.5 * sigma2 / (sigma2 + 0.33);
    float B = 0.45 * sigma2 / (sigma2 + 0.09);
    /* Useful precomputed quantities */
    float  theta_r = acos (V . N);        /* Angle between V and N */
    vector V_perp_N = normalize(V-N*(V.N)); /* Part of V perpendicular to N */

    /* Accumulate incoming radiance from lights in C */
    color  C = 0;
    extern point P;
    illuminance (P, N, PI/2) {
	/* Must declare extern L & Cl because we're in a function */
	extern vector L;  extern color Cl;
	float nondiff = 0;
	lightsource ("__nondiffuse", nondiff);
	if (nondiff < 1) {
	    vector LN = normalize(L);
	    float cos_theta_i = LN . N;
	    float cos_phi_diff = V_perp_N . normalize(LN - N*cos_theta_i);
	    float theta_i = acos (cos_theta_i);
	    float alpha = max (theta_i, theta_r);
	    float beta = min (theta_i, theta_r);
	    C += (1-nondiff) * Cl * cos_theta_i * 
		(A + B * max(0,cos_phi_diff) * sin(alpha) * tan(beta));
	}
    }
    return C;

}



/*
 * Greg Ward Larson's anisotropic specular local illumination model.
 * The derivation and formulae can be found in:  Ward, Gregory J.
 * "Measuring and Modeling Anisotropic Reflection," ACM Computer 
 * Graphics 26(2) (Proceedings of Siggraph '92), pp. 265-272, July, 1992.
 * Notice that compared to the paper, the implementation below appears
 * to be missing a factor of 1/pi, and to have an extra L.N term.
 * This is not an error!  It is because the paper's formula is for the
 * BRDF, which is only part of the kernel of the light integral, whereas
 * shaders must compute the result of the integral.
 *
 * Inputs:
 *   N - unit surface normal
 *   V - unit viewing direction (from P toward the camera)
 *   xdir - a unit tangent of the surface defining the reference
 *          direction for the anisotropy.
 *   xroughness - the apparent roughness of the surface in xdir.
 *   yroughness - the roughness for the direction of the surface 
 *          tangent perpendicular to xdir.
 */
color
LocIllumWardAnisotropic (normal N;  vector V;
                         vector xdir;  float xroughness, yroughness;)
{
    float square (float x) { return x*x; }

    float cos_theta_r = clamp (N.V, 0.0001, 1);
    vector X = xdir / xroughness;
    vector Y = (N ^ xdir) / yroughness;

    color C = 0;
    extern point P;
    illuminance (P, N, PI/2) {
	/* Must declare extern L & Cl because we're in a function */
	extern vector L;  extern color Cl; 
	float nonspec = 0;
	lightsource ("__nonspecular", nonspec);
	if (nonspec < 1) {
	    vector LN = normalize (L);
	    float cos_theta_i = LN . N;
	    if (cos_theta_i > 0.0) {
		vector H = normalize (V + LN);
		float rho = exp (-2 * (square(X.H) + square(Y.H)) / (1 + H.N))
		    / sqrt (cos_theta_i * cos_theta_r);
		C += Cl * ((1-nonspec) * cos_theta_i * rho);
	    }
	}
    }
    return C / (4 * xroughness * yroughness);
}



/*
 * LocIllumGlossy - a possible replacement for specular(), with a
 * more uniformly bright core and a sharper falloff.  It's a nice
 * specular function to use for something made of glass or liquid.
 * Inputs:
 *  roughness - related to the size of the highlight, larger is bigger
 *  sharpness - 1 is infinitely sharp, 0 is very dull
 */
color LocIllumGlossy ( normal N;  vector V;
                       float roughness, sharpness; )
{
    color C = 0;
    float w = .18 * (1-sharpness);
    extern point P;
    illuminance (P, N, PI/2) {
	/* Must declare extern L & Cl because we're in a function */
	extern vector L;  extern color Cl; 
	float nonspec = 0;
	lightsource ("__nonspecular", nonspec);
	if (nonspec < 1) {
	    vector H = normalize(normalize(L)+V);
	    C += Cl * ((1-nonspec) * 
		       smoothstep (.72-w, .72+w,
				   pow(max(0,N.H), 1/roughness)));
	}
    }
    return C;
}

#endif /* defined(LOCILLUM_H) */

