/*-------------------------------------------------------------------------
 *	carpet - creates a carpeted surface with variable nap and scuffing.
 *	Makes a reasonable attempt at anti-aliasing.
 *
 *	Kd, Ka - the usual meaning
 *	scuff - the "amount of scuffing": footprints, vacuum cleaner marks,
 *	   etc. (the relative frequency of intensity variation)
 *	nap - the "shagginess" of the carpet (size of the speckles)
 *-------------------------------------------------------------------------*/
surface
carpet( float Kd=.6, float Ka=.1, float scuff = 1, float nap = 1)
{
    normal  Nf;
	point	PP;
	float	pixelsize, twice,
			pixscale, weight, turbulence,
			napfreq, speckle, mottling;

	Nf = faceforward( normalize(N), I);


    /* large-scale mottling */
	if (scuff==0) mottling = 1;
	else {
	    /* get pixel size */
		PP = transform("shader",P) * scuff;
		PP = PP/2;	/* frequency adjustment (S-shaped curve) */
		pixelsize = sqrt(area(PP)); twice = 2 * pixelsize;
	    /* create some turbulence */
		turbulence = 0;
		for (pixscale = 1; pixscale > twice; pixscale /= 2) {
			turbulence += pixscale * abs(noise(PP/pixscale)-0.5);
		}
	    /* gradual fade out of highest frequency component near limit */
		if (pixscale > pixelsize) {
			weight = (pixscale / pixelsize) - 1;
			weight = clamp(weight, 0, 1);
			turbulence += weight * pixscale * abs(noise(PP/pixscale)-0.5);
		}
	    /* scuffing will be between 0.5 and 1 */
		mottling = min(turbulence+0.5, 1);
	}

    /* small-scale speckling */
	if (nap==0) speckle = 1;
	else {
	    /* get high-frequency speckles */
		napfreq = nap/100;	/* scale up speckle frequency */
		speckle = noise(transform("shader",P)/napfreq);
		speckle = speckle * speckle;
	    /* two-level speckles */
		speckle = (speckle <= 0.25) ? 0.2 : 1;
	    /* smooth out speckles as they get smaller */
		pixelsize = sqrt(area(transform("shader",P)));
		if (speckle<1) speckle = (1-smoothstep(0,1,napfreq/pixelsize))
					*(1-speckle) + speckle;
	}

    /* get color */
	C *= opacity * mottling * speckle * (Ka*ambient() + Kd*diffuse(Nf));
}
