A color stencil node.

Tired and unhappy, as always.
But I think my last OSL might be useful for you.

Its a stencil node.

( ~~~ )

Stencil Parameters:
C: a user defined color.
range: determines the radius of a “sphere” in the color space. Center of the sphere is color C.

Curve Parameters
Curve1: one of “linear”, “constant”, “round”, “smooth”, “sharp”
Curve2: one of “linear”, “constant”, “round”, “smooth”, “sharp”
blend_curve: blend between Curve1 and Curve2.

Color input, used to create stencil.
Switch: color channel input. (E.g. … use it with textures.)

The stencil is computed, depending whether Switch is inside the sphere around C.
Curves control how the stencil blends.

( ~~~ )

Curves
linear … a straight blending.
constant … gives the stencil a sharp edge.
round … “dome” like blending.
smooth … smooth blending.
sharp … beginning with a soft blending, leading to a sharp tip.

( ~~~ )

The code …


/* File: color_stencil.osl 
By Leonard Siebeneicher.

I am not used to Licensing stuff, sorry.
You can use it as you wish.
*/
#include <stdosl.h>



/* 
 * Currently supportet curves. (Blendings/Gradients) 
 */
 
float curv_smooth(float x)
{
    float arr[4] = {0,0,1,1};
    return spline ("bezier", x, 4, arr);
}

float curv_sharp(float x)
{
    float arr[4] = {0,0,0,1};
    return spline ("bezier", x, 4, arr);
}

float curv_round(float x)
{
    float arr[4] = {0,1,1,1};
    return spline ("bezier", x, 4, arr);
}

/* Support waves? */

float curv_cos(float x)
{
    return 1-cos(x*3*M_PI);
}

float curv_sharp_tri(float x)
{
    float arr[4] = {0,1,0,1};
    return spline ("linear", x, 4, arr);
}




/*
 * This function is used to apply a curve chosen by a user.
 */

float apply_curve (string name, float blend)
{
    /* We clamp 'blend' before we use it
     * in a curve. */

    float x = clamp (blend, 0, 1);
 
    /* Very basic are "constant" and "linear". We
     * do not nee to use spline function. 
     * All other curves use osl's spline function. 
     * New curves can be added easily. */
       
    if ( name == "constant" ) {
        return 1;
        }
    else if ( name == "linear") {
        return x;
    }   
    else if ( name == "smooth" ) {
        return curv_smooth(x);
    }
    else if ( name == "sharp" ) {
        return curv_sharp(x);
    }
    else if ( name == "round" ) {
        return curv_round(x);
    }
    else 
    {
        printf ("Shader Error; Curve %s not defined.
", name);
        return 0;
    }

}



/* 
 * Main shader 
 */

shader node_color_stencil(
    /* We have a color C and a "sphere" around
     * it with the radius "range". */
    color C = 0.8,
    float range = 0.2,

    /* We alter the stencil, using spline curve 
     * gradients. Possible values for Curve1 and 
     * Curve2 are  "constant", "smooth",
     * "sharp, "round", wave and "linear". */
    string Curve1 = "linear",
    string Curve2 = "smooth",
    float blend_curve = 0,

    /* The input color data is called 'Switch'.
     * If color 'Switch' is inside the shpere around
     * C, it is considered as a match. There we set 
     * a stencil value. */
    color Switch = 0.8, 

    /* value of stencil should be between 0 and 1 */
    output float stencil = 0)
{
    /* We calculate distance between 'C' and 'Switch'. */

    float dst = distance(vector(C), vector(Switch));


    /* Now we test, whether 'Switch' is inside the spere
     * arount C with radius 'range'. */
     
    if ( dst <= range ) {
        /* We are inside range. */
        
        /* Transforming 'dst' (which can be any value)
         * to a ratio which range between 0 and 1.
         * This ratio is used to set strength of
         * 'stencil'. */

        float r = dst/range; 
        
        /* Now we can use 'r' to calculate stencil value.
         * Using 'blend_curve' to blend between chosen curves. */

        stencil = mix(apply_curve(Curve1, 1-r), 
                      apply_curve(Curve2, 1-r),
                      blend_curve);
    }
    else {
        /* We are outside range. */
        stencil = 0;
    }
         
}

Some examples using color_stencil appended to this posting.
Happy blending.

Attachments



Very interesting patterns! This could be useful for generating all sorts of stone textures for example. Excellent work, thanks for sharing!

Hi varkenvarken.

Thank you for your answer.

Kaluura shared a very good idea with the public, it changed the way I use procedual textures.
(He used color output of a texture node as vector input of another texture node.)

Kaluuras texture node idea gives the input pattern, the source. The stencil node picks a part of it, dropping all remaining patterns, then it generates a stencil (a value from 0 to 1). The generated stencil can be used in other nodes later (mix nodes, math node, etc.).

At the moment, the stencil node uses 2 curves and blend them, to give a bit control over its float output.
It would be better to have something like a curve editor (like vector curves node or RGB curves node).
Together with a curve editor, the stencil node would be complete. But, at the moment, I do not have any idea how to achieve this.

Happy coding.

Instead of curve node can you use a ramp node to remap values?

Hi 3pointEdit.

A Colorramp would be a good idea.

I think, whether a color ramp or a curve editor is used for the shader, the shader code would remain the same.

Now I start to doubt it would be a good idea, it would intrude cycles nodes.
Maybe there will be others who like to use curves or colorramps.

What about abdonning idea to use curve-appings or colorramp inside the node?
We could put a curve mapping or a colorramp any time on the stencils output. That would do the job, too.

( ~~~ )

Found some strength.
Tried another pattern.

Attachments


Bit more energy.

Updated shader. Curves which remap the result of the distance operator have been removed. Remapping is done now using nodes like colorramp or rgb curves.

It uses the Minkovski distance now.
The shader has a new parameter, “degree”. Its a value between 0 and 1.

degree == 0 —> distance operator is the manhattan distance.
degree == 0.2 —> distance operator is the euclidean distance.
degree == 1 —> the chebyshev distance is approximated.

The names of the color parameters has been changed. We dont have C and Switch, but Switch1 and Switch2. The parameter “Range” remains, it is the size of a sphere around Switch1.

Happy blending.

/*
 * File: color_stencil.osl
 * By Leonard Siebeneicher
 * Feel free to use it.
 */

#include <stdosl.h>



/*
 * Keeping it simple: we only use the minkovski distance.
 * 
 * (1)
 * the manhattan distance and euclidean distance
 * are both special cases of the minkovski distance.
 *
 * dst_manhatten (v1, v2) == dst_minkovski (v1, v2, 1)
 * dst_euclidean (v1, v2) == dst_minkovski (v1, v2, 2)
 *
 * (2)
 * The higher the exponent of the minkovski distance, the more
 * close its values are to the chebyshev distance function. If
 * we use an exponent of 50, the values of the chebyshev distance are 
 * well approximated.
 * 
 * A 32-Bit floating point numbers could have an exp10 above 120.
 * I think we do not need to fear a floating point overflow.
 */

float dst_minkovski (color v1, color v2, float exponent )
{
  return pow( pow( fabs( v2[0] - v1[0] ), exponent ) + 
          pow( fabs( v2[1] - v1[1] ), exponent ) + 
          pow( fabs( v2[2] - v1[2] ), exponent ),
          1/exponent );
}




/*
 * Before we can apply the minkovski distance, we need to remap the user 
 * input 'degree', which is in the range [0:1]. A degree number from 0 to 1 
 * will be mapped to a number between 1 and 50.
 * The mapped number will be used in dst_minkowski as exponent. 
 */

float calculate_distance (color v1, color v2, float degree)
{
  float arr[12] =
  {
    1,  0,
    2,  2,
    4,  4  - 0.8,
    8,  8  - 1.6,
    16, 16 - 3.2,
    50, 50
  };

  return dst_minkovski (v1, v2, spline("hermite", degree, 12, arr)); 
}



/* 
 * Main shader. 
 */

shader node_color_stencil
       [[ string help = "Generates a stencil out of input color values Switch1 and Switch2" ]]
(
    float range = 0.2
    [[ string help = "Defines the radius of a sphere around Switch1." ]],
    
    float degree = 0
    [[ string help = "This value controls the exponent of the minkowski distance", float min=0, float max=1 ]],

    color Switch1 = 0.8,
    color Switch2 = 0.8,
    output float stencil = 0
    [[ string help = "The value of the stencil node is a float between 0 and 1."]]
)
{
  /* We calculate distance between 'Switch1' and 'Switch2'. */
  float dst = calculate_distance (Switch1,Switch2,degree);

  /* Now we test, whether 'Switch2' is inside the spere
   * arount Switch1 with radius 'range'. */
  
  if ( dst <= range ) {
    /* We are inside sphere. */
    stencil = 1-dst/range; 
  }
  else {
    /* We are outside sphere. */
    stencil = 0;
  }  
}



A simple example.
Matching colors of a noise texture against colors of a magic texture.

Attachments