POV-Ray : Newsgroups : povray.binaries.images : Crackle : Re: Crackle Server Time
30 Nov 2022 06:43:28 EST (-0500)
  Re: Crackle  
From: Bald Eagle
Date: 11 Nov 2022 14:10:00
Message: <web.636e9ce9302862a41f9dae3025979125@news.povray.org>
"Chris R" <car### [at] comcastnet> wrote:

> What controls the pigment value selected for the cell?  Is it just a random
> number, or do proximate cells select from the same value range.  I'm finding the
> more variation with smaller ranges in my pigment_map, the less "clumpy" the
> cells become, but does it follow the normal ramp_wave or is it just a linear
> random number?


From source/core/material/pattern.cpp

There's a few extra pre-main-pattern sections, and I've always wanted to
translate the source code algorithm into SDL so that people could vary the field
of random points, or otherwise modify the pattern to suit their needs.

The following may also interest you:

http://news.povray.org/povray.binaries.images/thread/%3C5acf08bc%241%40news.povray.org%3E/


* FUNCTION
*
*   crackle_pattern
*
* INPUT
*
*   EPoint -- The point in 3d space at which the pattern
*   is evaluated.
* OUTPUT
*
* RETURNS
*
*   DBL value in the range 0.0 to 1.0
*
* AUTHOR
*
*   Jim McElhiney
*
* DESCRIPTION
*
*   "crackle":
*
*   New colour function by Jim McElhiney,
*     CompuServe 71201,1326, aka mce### [at] acmorg
*
*   Large scale, without turbulence, makes a pretty good stone wall.
*   Small scale, without turbulence, makes a pretty good crackle ceramic glaze.
*   Highly turbulent (with moderate displacement) makes a good marble, solving
*   the problem of apparent parallel layers in Perlin's method.
*   2 octaves of full-displacement turbulence make a great "drizzled paint"
*   pattern, like a 1950's counter top.
*   Rule of thumb:  put a single colour transition near 0 in your colour map.
*
*   Mathematically, the set crackle(p)=0 is a 3D Voronoi diagram of a field of
*   semirandom points, and crackle(p)>0 is distance from set along shortest
path.
*   (A Voronoi diagram is the locus of points equidistant from their 2 nearest
*   neighbours from a set of disjoint points, like the membranes in suds are
*   to the centres of the bubbles).
*
*   The original "crackle" specific source code and examples are in the public
domain.
*
* CHANGES
*   Oct 1994    : adapted from pigment by [CY]
*   Other changes: enhanced by Ron Parker, Integer math by Nathan Kopp
*
******************************************************************************/
static int IntPickInCube(int tvx, int tvy, int tvz, Vector3d& p1);

DBL CracklePattern::EvaluateRaw(const Vector3d& EPoint, const Intersection
*pIsection, const Ray *pRay, TraceThreadData *pThread) const
{
    Vector3d tmpPoint = EPoint;
    DBL sum, minsum, minsum2, minsum3, tf;
    int minVecIdx = 0;
    Vector3d dv;

    int flox, floy, floz;

    bool UseSquare = ( crackleMetric == 2);
    bool UseUnity  = ( crackleMetric == 1);

    if (repeat.x())
        tmpPoint.x() = wrap(tmpPoint.x(), DBL(repeat.x()));
    if (repeat.y())
        tmpPoint.y() = wrap(tmpPoint.y(), DBL(repeat.y()));
    if (repeat.z())
        tmpPoint.z() = wrap(tmpPoint.z(), DBL(repeat.z()));

    /*
     * This uses floor() not FLOOR, so it will not be a mirror
     * image about zero in the range -1.0 to 1.0. The viewer
     * won't see an artefact around the origin.
     */

    flox = (int)floor(tmpPoint[X] - EPSILON);
    floy = (int)floor(tmpPoint[Y] - EPSILON);
    floz = (int)floor(tmpPoint[Z] - EPSILON);

    /*
     * Check to see if the input point is in the same unit cube as the last
     * call to this function, to use cache of cubelets for speed.
     */

    CrackleCellCoord ccoord(flox, floy, floz, repeat.x(), repeat.y(),
repeat.z());
    pThread->Stats()[CrackleCache_Tests]++;

    CrackleCacheEntry dummy_entry;
    CrackleCacheEntry* entry = &dummy_entry;

    if (pThread->mpCrackleCache->Lookup(entry, ccoord))
    {
        // Cache hit. `entry` now points to the cached entry.
        pThread->Stats()[CrackleCache_Tests_Succeeded]++;
    }
    else
    {
        // Cache miss. `entry` now points to a pristine entry set up in the
        // cache, or to `dummy_entry` if the cache is too crowded already.
        // In either case we need to fill in the blanks.

        // Calculate the random points for this new
        // cube and its 80 neighbours which differ in any axis by 1 or 2.
        // Why distance of 2?  If there is 1 point in each cube, located
        // randomly, it is possible for the closest random point to be in the
        // cube 2 over, or the one two over and one up.  It is NOT possible
        // for it to be two over and two up.  Picture a 3x3x3 cube with 9 more
        // cubes glued onto each face.

        // TODO - Note that we're currently re-computing each cell up to 81
        //        times - once as a main cell and 80 times as a neighbor -
        //        even in the best case scenario. Wouldn't it be more efficient
        //        to just cache the individual cells?

        int *pc = gaCrackleCubeTable;
        for (int i = 0; i < 81; i++, pc += 3)
        {
            Vector3d wrappingOffset(0.0);
            int cacheX = flox + pc[X];
            int cacheY = floy + pc[Y];
            int cacheZ = floz + pc[Z];
            if (repeat.x())
            {
                int wrapped = wrapInt(cacheX, repeat.x());
                wrappingOffset.x() += (cacheX - wrapped);
                cacheX = wrapped;
            }
            if (repeat.y())
            {
                int wrapped = wrapInt(cacheY, repeat.y());
                wrappingOffset.y() += (cacheY - wrapped);
                cacheY = wrapped;
            }
            if (repeat.z())
            {
                int wrapped = wrapInt(cacheZ, repeat.z());
                wrappingOffset.z() += (cacheZ - wrapped);
                cacheZ = wrapped;
            }
            IntPickInCube(cacheX, cacheY, cacheZ, entry->aCellNuclei[i]);
            entry->aCellNuclei[i] += wrappingOffset;
        }
    }

    // Find the 3 points with the 3 shortest distances from the input point.
    // Set up the loop so the invariant is true:  minsum <= minsum2 <= minsum3
    dv = entry->aCellNuclei[0] - tmpPoint;

    if(UseSquare)
    {
        minsum = dv.lengthSqr();

        dv = entry->aCellNuclei[1] - tmpPoint;
        minsum2 = dv.lengthSqr();

        dv = entry->aCellNuclei[2] - tmpPoint;
        minsum3  = dv.lengthSqr();
    }
    else if(UseUnity)
    {
        minsum = fabs(dv[X]) + fabs(dv[Y]) + fabs(dv[Z]);

        dv = entry->aCellNuclei[1] - tmpPoint;
        minsum2 = fabs(dv[X]) + fabs(dv[Y]) + fabs(dv[Z]);

        dv = entry->aCellNuclei[2] - tmpPoint;
        minsum3 = fabs(dv[X]) + fabs(dv[Y]) + fabs(dv[Z]);
    }
    else
    {
        minsum = pow(fabs(dv[X]), crackleMetric) +
                 pow(fabs(dv[Y]), crackleMetric) +
                 pow(fabs(dv[Z]), crackleMetric);

        dv = entry->aCellNuclei[1] - tmpPoint;
        minsum2 = pow(fabs(dv[X]), crackleMetric) +
                  pow(fabs(dv[Y]), crackleMetric) +
                  pow(fabs(dv[Z]), crackleMetric);

        dv = entry->aCellNuclei[2] - tmpPoint;
        minsum3 = pow(fabs(dv[X]), crackleMetric) +
                  pow(fabs(dv[Y]), crackleMetric) +
                  pow(fabs(dv[Z]), crackleMetric);
    }

    // sort the 3 computed sums
    if(minsum2 < minsum)
    {
        tf = minsum; minsum = minsum2; minsum2 = tf;
        minVecIdx = 1;
    }

    if(minsum3 < minsum)
    {
        tf = minsum; minsum = minsum3; minsum3 = tf;
        minVecIdx = 2;
    }

    if(minsum3 < minsum2)
    {
        tf = minsum2; minsum2 = minsum3; minsum3 = tf;
    }

    // Loop for the 81 cubelets to find closest and 2nd closest.
    for(int i = 3; i < 81; i++)
    {
        dv = entry->aCellNuclei[i] - tmpPoint;

        if(UseSquare)
            sum = dv.lengthSqr();
        else if(UseUnity)
            sum = fabs(dv[X]) + fabs(dv[Y]) + fabs(dv[Z]);
        else
            sum = pow(fabs(dv[X]), crackleMetric) +
                  pow(fabs(dv[Y]), crackleMetric) +
                  pow(fabs(dv[Z]), crackleMetric);

        if(sum < minsum)
        {
            minsum3 = minsum2;
            minsum2 = minsum;
            minsum = sum;
            minVecIdx = i;
        }
        else if(sum < minsum2)
        {
            minsum3 = minsum2;
            minsum2 = sum;
        }
        else if( sum < minsum3 )
        {
            minsum3 = sum;
        }
    }

    if (crackleOffset)
    {
        if(UseSquare)
        {
            minsum += crackleOffset*crackleOffset;
            minsum2 += crackleOffset*crackleOffset;
            minsum3 += crackleOffset*crackleOffset;
        }
        else if (UseUnity)
        {
            minsum += crackleOffset;
            minsum2 += crackleOffset;
            minsum3 += crackleOffset;
        }
        else
        {
            minsum += pow( crackleOffset, crackleMetric );
            minsum2 += pow( crackleOffset, crackleMetric );
            minsum3 += pow( crackleOffset, crackleMetric );
        }
    }

    if(crackleIsSolid)
    {
        tf = Noise( entry->aCellNuclei[minVecIdx], GetNoiseGen(pThread) );
    }
    else if(UseSquare)
    {
        tf = crackleForm[X]*sqrt(minsum) +
             crackleForm[Y]*sqrt(minsum2) +
             crackleForm[Z]*sqrt(minsum3);
    }
    else if(UseUnity)
    {
        tf = crackleForm[X]*minsum +
             crackleForm[Y]*minsum2 +
             crackleForm[Z]*minsum3;
    }
    else
    {
        tf = crackleForm[X]*pow(minsum, 1.0/crackleMetric) +
             crackleForm[Y]*pow(minsum2, 1.0/crackleMetric) +
             crackleForm[Z]*pow(minsum3, 1.0/crackleMetric);
    }

    return max(min(tf, 1.), 0.);
}


Post a reply to this message

Copyright 2003-2021 Persistence of Vision Raytracer Pty. Ltd.