





 
 




 
 


"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 premainpattern 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 fulldisplacement 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 recomputing 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


 
 




 
 


"Bald Eagle" <cre### [at] netscapenet> wrote:
> "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 premainpattern 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/
>
> ...
Thanks, that's very helpful. I should probably spend some time looking over the
source code more often. My C++ is a little rusty from disuse, though.
So, the _map entry based on the node point of the cell, and uses the noise
generator chosen in your global options for the scene. It doesn't appear there
is a way to separately scale the node points for the call to the noise
generator, so creating more randomness across neighboring cells is a matter of
having finegrained _maps to choose from.
 Chris R.
Post a reply to this message


 
 




 
 


"Chris R" <car### [at] comcastnet> wrote:
> Thanks, that's very helpful.
Well, I'm glad it's of _some_ use. ;)
> I should probably spend some time looking over the
> source code more often. My C++ is a little rusty from disuse, though.
I've learned a lot from reading through the source code, even though my cpp
knowledge is mostly acquired through hunt (online) and code, or writing Arduino
sketches.
> So, the _map entry based on the node point of the cell, and uses the noise
> generator chosen in your global options for the scene. It doesn't appear there
> is a way to separately scale the node points for the call to the noise
> generator, so creating more randomness across neighboring cells is a matter of
> having finegrained _maps to choose from.
There isn't any way (to my knowledge) to affect the distribution of random
points, which is why so many people have expressed interest over the years in
having an SDL version of the voronoi pattern.
I've had a lot of success (not 100%) with adapting ShaderToy code to SDL, so
maybe at some point I can figure out how to make a voronoi pattern in SDL based
on some of the scripts.
Maybe some of that will be clearer / of more use to you.
https://iquilezles.org/articles/voronoilines/
https://iquilezles.org/articles/smoothvoronoi/
https://iquilezles.org/articles/voronoise/
https://www.shadertoy.com/results?query=voronoi
Post a reply to this message


 
 




 
 


"Bald Eagle" <cre### [at] netscapenet> wrote:
> I've had a lot of success (not 100%) with adapting ShaderToy code to SDL, so
> maybe at some point I can figure out how to make a voronoi pattern in SDL based
> on some of the scripts.
Y'know  like this:
I'm not sure when I make all the squares or spheres fill up everything that it
turns black, with a few colored spots. My POVRay suckage, likely.
But it's proof of concept, so here we go.
Next stage would be to code it using only functions, and then it could be used
as a pigment pattern.
#version 3.8;
global_settings {assumed_gamma 1.0}
#include "colors.inc"
//#include "BezierInclude.inc"
#include "math.inc"
#declare E = 0.0000001;
#declare Camera_Orthographic = true;
#declare Camera_Position = <0.5, 0.5, 100> ; // front view
#declare Camera_Look_At = <0.5, 0.5, 0> ;
#declare Fraction = 450; // functions as a zoom for the orthographic view: 4
zooms in 4x, 8 zooms in 8x, etc.
// ###########################################
camera {
#if (Camera_Orthographic = true)
orthographic
right x*image_width/(Fraction)
up y*image_height/(Fraction)
#else
right x*image_width/image_height
up y
#end
location Camera_Position
look_at Camera_Look_At}
// ###########################################
sky_sphere {pigment {rgb <1, 1, 1>}}
#declare LS = <0, 0, 20>;
light_source {LS rgb 1}
#declare Line = 0.5/image_width; // line thickness
// DOT product
#declare SFn_vdot = function (ax, ay, az, bx, by, bz) {ax*bx + ay*by + az*bz}
// Length or Norm
#declare SFn_vlength = function (ax, ay, az) {sqrt(ax*ax + ay*ay + az*az)}
#declare Clip = function (_Val, _Min, _Max) {min(_Max, max(_Val, _Min))}
#declare Window = function (_Val, _Eq, _Min, _Max, _E) {select (_Val_Min, _Val,
select(_Max_Val, _Val, _Eq))}
#declare LimitReps = function (Element, Period, Min, Max) {Element  Period *
Clip (Element/Period, Min, Max)}
#declare Smoothstep0 = function (_Val, _From, _To) {Clip ((_Val  _From) / (_To
 _From), 0.0, 1.0)}
#declare Smoothstep = function (_Val, _From, _To) {Smoothstep0 (_Val, _From,
_To) * Smoothstep0 (_Val, _From, _To) * (3.0  2.0 * Smoothstep0 (_Val, _From,
_To))}
#macro Clamp (N, minVal, maxVal)
#local Val = min (max (N, minVal), maxVal);
Val
#end
#macro hash (p)
// input: 2D vector
// output: 2D vector
//p = mod(p, 4.0); // tile
#local PX = vdot (p, <127.1, 311.7>);
#local PY = vdot (p, <269.5, 183.3>);
#local PXr = mod (sin (PX)*18.5453, 1);
#local PXy = mod (sin (PY)*18.5453, 1);
<PXr, PXy>
#end
// return distance, and cell id
#macro voronoi (P)
// input: 2D vector
// output: 2D vector
#local nx = floor (P.x);
#local ny = floor (P.y);
#local n = <nx, ny>;
#local fx = mod (P.x, 1);
#local fy = mod (P.y, 1);
#local m = <8, 8, 8>;
#for (j, 1, 1)
#for (i, 1, 1)
#local g = <i, j>;
#local o = hash (n + g);
// #local r = g  f + o;
#local rx = g.x  fx + (0.5 + 0.5*sin(clock + 6.2831*o.x));
#local ry = g.y  fy + (0.5 + 0.5*sin(clock + 6.2831*o.y));
#local r = <rx, ry>;
#local d = vdot (r, r);
#if(d < m.x)
#local m = <d, o.x, o.y>; // m = vec3 (d, o);
#end
#end
#end
<sqrt (m.x), m.y + m.z>;
#end
#local M = max(image_width,image_height);
#local D = 1.75;
#macro mainImage (X, Y)
#local PX = X / M;
#local PY = Y / M;
// computer voronoi pattern
#local C = voronoi ( <(14.0 + 6.0 * sin (0.2*clock)) * PX, (14.0 + 6.0 * sin
(0.2*clock)) * PY>);
// colorize
#local ColorR = 0.5 + 0.5 * cos (C.y*6.2831 + 0);
#local ColorG = 0.5 + 0.5 * cos (C.y*6.2831 + 1);
#local ColorB = 0.5 + 0.5 * cos (C.y*6.2831 + 2);
#local C1 = Clamp (1.0  0.4*C.x*C.x, 0.0, 1.0);
#local ColorR = ColorR * C1;
#local ColorG = ColorG * C1;
#local ColorB = ColorB * C1;
#local C2 = (1.0  Smoothstep (0.08, 0.09, C.x));
#local ColorR = ColorR  C2;
#local ColorG = ColorG  C2;
#local ColorB = ColorB  C2;
#local fragColor = <ColorR, ColorG, ColorB>;
//sphere {<PX, PY>, Line*2 texture {pigment {rgb fragColor} finish {diffuse
1}} }
//sphere {<PX, PY>, Line*2 pigment {rgb fragColor} }
box {<PXLine*D, PYLine*D, 0>, <PX+Line*D, PY+Line*D, Line> pigment {rgb
fragColor} }
#end
#declare Step = 2;
#for (Y, 0, image_width, Step)
#debug concat("Y = ", str(Y, 0, 0), "\n")
#for (X, 0, image_width, Step)
mainImage (X, Y)
#end
#end
/*
vec2 hash( vec2 p )
{
//p = mod(p, 4.0); // tile
p = vec2(dot(p,vec2(127.1,311.7)),
dot(p,vec2(269.5,183.3)));
return fract(sin(p)*18.5453);
}
// return distance, and cell id
vec2 voronoi( in vec2 x )
{
vec2 n = floor( x );
vec2 f = fract( x );
vec3 m = vec3( 8.0 );
for( int j=1; j<=1; j++ )
for( int i=1; i<=1; i++ )
{
vec2 g = vec2( float(i), float(j) );
vec2 o = hash( n + g );
//vec2 r = g  f + o;
vec2 r = g  f + (0.5+0.5*sin(iTime+6.2831*o));
float d = dot( r, r );
if( d<m.x )
m = vec3( d, o );
}
return vec2( sqrt(m.x), m.y+m.z );
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 p = fragCoord.xy/max(iResolution.x,iResolution.y);
// computer voronoi patterm
vec2 c = voronoi( (14.0+6.0*sin(0.2*iTime))*p );
// colorize
vec3 col = 0.5 + 0.5*cos( c.y*6.2831 + vec3(0.0,1.0,2.0) );
col *= clamp(1.0  0.4*c.x*c.x,0.0,1.0);
col = (1.0smoothstep( 0.08, 0.09, c.x));
fragColor = vec4( col, 1.0 );
}
*/
Post a reply to this message
Attachments:
Download 'new_iq_voronoipattern.png' (416 KB)
Preview of image 'new_iq_voronoipattern.png'


 
 




 
 


"Bald Eagle" <cre### [at] netscapenet> wrote:
> Next stage would be to code it using only functions, and then it could be used
> as a pigment pattern.
Indeed. If I knew how this all worked and could code it right. <eyeroll>
Also, these things are SLOW.
I thought the function version would be faster, but this small render took 23.75
min.
My initial frustration with trying to make one of these things was the nested
loops, but I figured I could "unroll" that and just daisy chain the functions.
Which I did, and it works  sorta.
But it's 102 functions, and for some reason it looks like I got the color code
wrong too.
It also look like there are different methods for generating the Voronoi
pattern, and this one is (obviously) grid based, like POVRay's. I'll have to
try my hand at one with a much more arbitrary data set.
Also: Chris R  are you aware that you can get a Voronoi pattern just using
overlapping cones? Depending on what you're planning to do with it, that would
be both super easy and FAST. And from what I understand, it's actually a
legitimate Voronoi result.
https://smathermather.com/2012/01/20/fastcalculationofvoronoipolygonsinpovray/
Post a reply to this message


 
 




 
 


Maybe it would help if I attached the render. :
Post a reply to this message
Attachments:
Download 'new_iq_voronoifunction.png' (137 KB)
Preview of image 'new_iq_voronoifunction.png'


 
 




 
 


"Bald Eagle" <cre### [at] netscapenet> wrote:
> "Bald Eagle" <cre### [at] netscapenet> wrote:
>
> > Next stage would be to code it using only functions, and then it could be used
> > as a pigment pattern.
>
> Indeed. If I knew how this all worked and could code it right. <eyeroll>
>
> Also, these things are SLOW.
> I thought the function version would be faster, but this small render took 23.75
> min.
>
> My initial frustration with trying to make one of these things was the nested
> loops, but I figured I could "unroll" that and just daisy chain the functions.
> Which I did, and it works  sorta.
> But it's 102 functions, and for some reason it looks like I got the color code
> wrong too.
>
> It also look like there are different methods for generating the Voronoi
> pattern, and this one is (obviously) grid based, like POVRay's. I'll have to
> try my hand at one with a much more arbitrary data set.
>
> Also: Chris R  are you aware that you can get a Voronoi pattern just using
> overlapping cones? Depending on what you're planning to do with it, that would
> be both super easy and FAST. And from what I understand, it's actually a
> legitimate Voronoi result.
>
> https://smathermather.com/2012/01/20/fastcalculationofvoronoipolygonsinpovray/
Thanks for the pointers. I'll check them out.
Most of my work is done with isosurfaces, especially for closeup views of
objects. I end up coding the basic shape of the object I want as a function and
then apply perturbations using noise generators and pigment patterns to get the
"texture" I want on the surface of the object. I then apply an SDL texture to
get the color and finish look to the object.
For the tiled floors, and some of my brick wall work, I have to find ways to
make the pigment pattern I use to generate the surface texture to match up with
the pigment pattern I use to generate the colors and finishes. So, the crackle
pigment pattern works well that way, now that you have shown me how to get a
consistent texture to apply to the cells. If I need more variability between
cells I'll just have to play with the texture maps and randomize them more.
 Chris R.
Post a reply to this message


 
 




 
 


"Bald Eagle" <cre### [at] netscapenet> wrote:
> Also, these things are SLOW.
At least they are when I did it the way I did it.
This was a very short scene file, and fast.
This renders in 18 sec.
The color values are crap, and I have a lot of fudge factors, but it's headed in
the right direction.
Fully confident that at some point a version allowing SDLgenerated Voronoi of
any userdefined data set will be possible.
Post a reply to this message
Attachments:
Download 'shadertoyvoronoi.png' (83 KB)
Preview of image 'shadertoyvoronoi.png'


 
 




 
 


"Bald Eagle" <cre### [at] netscapenet> wrote:
> Fully confident that at some point a version allowing SDLgenerated Voronoi of
> any userdefined data set will be possible.
Like so.
Now to just figure out my damned screen coordinates and calculations, remove any
fudge factors, and figure out what's making it so dull & gray.
Then maybe there's away to assign a color to the cells.
Post a reply to this message
Attachments:
Download 'shadertoyvoronoi.png' (43 KB)
Preview of image 'shadertoyvoronoi.png'


 
 




 
 


"Bald Eagle" <cre### [at] netscapenet> wrote:
> Now to just figure out my damned screen coordinates and calculations, remove any
> fudge factors, and figure out what's making it so dull & gray.
>
> Then maybe there's away to assign a color to the cells.
Cleaned it up a bit, explicitly understood all of the function outputs, and
removed all of the fudge factors.
The gray color was the Voronoi function pigmenting the plane at a distance from
the cell centers.
I tried doing something along the lines of crackle solid, using cosine, and
raising the result to a power, and trying to use select() or modifying the color
map. Maybe someone has a better idea than I do.
As for adding percell color, I don't think it's possible through SDL, as the
pattern (as currently implemented) just yields the minimum of all of the
distances between the current pixel and all of the cell centers. There's no way
that I can see to carryover a tag or other identifying value, principally
because it's the min () that generates the extent of every cell.
I don't see how there's a way around the "scrubbing" of all additional data
through the min () and POVRay's scalaronly functions. But maybe someone has a
clever idea.
I think it could be done with an algorithm, but that would probably require
sacrificing the speed of the current method. But if a usercontrolled Voronoi
pattern with specified colors or textures, or finishes were required for a
project, it could be done  just with a lot of waiting for the result.
I do think that the current results would make a neat heightfield for a terrain
scene.
Post a reply to this message
Attachments:
Download 'shadertoyvoronoi.png' (55 KB)
Preview of image 'shadertoyvoronoi.png'


 
 




 

