Irregular stone patterns in OSL, a first attempt

On the BlenderArtists forum a member was analyzing irregular stone walls and posed the interesting question: does this resemble a tree pattern and can it be done in OSL? Quite a lot of thinking and tinkering is needed for a full solution put in this post we explore some of the basic requirements.

Irregular stone patterns


A sample pattern with primary colors, Mondrian eat your heart out :-)
The sample image shows that we have generated a pattern consisting of rows with different heights, each consiting of stones of varying width. Additionally, some stones within a row are further split horizontally. The code to generate the pattern is shown below:
shader stones(
  point p = P,
  vector Scale = 1,
  float w = 0.02,
  float s = 2,
    
  output float Fac = 0
){
  point Pos = p * Scale;
  
  float bot = floor(Pos[1]-1)+cellnoise(Pos[1]-1);
  float lev = floor(Pos[1])+cellnoise(Pos[1]);
  float top = floor(Pos[1]+1)+cellnoise(Pos[1]+1);

  if( Pos[1] < lev ){
    Pos[0] += s*cellnoise(Pos[1]);
  }else{
    Pos[0] += s*cellnoise(Pos[1]+1);
  }
  float left = floor(Pos[0]-1)+cellnoise(Pos[0]-1);
  float mid = floor(Pos[0])+cellnoise(Pos[0]);
  float right = floor(Pos[0]+1)+cellnoise(Pos[0]+1);
  if( 
    ((Pos[0] > left+w) && ( Pos[0] < mid - w )) 
    || 
    ((Pos[0] > mid+w ) && ( Pos[0] < right - w))
  ){
    if( 
      ((Pos[1] > bot+w) && ( Pos[1] < lev - w )) 
      || 
      ((Pos[1] > lev+w ) && ( Pos[1] < top - w))
    ){
      int stoneindex=0;
      float seeda = left;
      float seedb = bot;
      float bounda = mid;
      float boundb = lev;
      
      if( Pos[0] > mid ){ stoneindex += 2; seeda = mid; bounda = right; }
      if( Pos[1] > lev ){ stoneindex += 1; seedb = lev; boundb = top; }
      int pattern = (int)floor(cellnoise(seeda,seedb)*4);
      if( pattern == 0 ){
            // horizontally halved
            float nlev = (seedb + boundb)/2;
            if( (Pos[1] > nlev - w) &&  (Pos[1] < nlev + w) ){
              Fac = 0;
            } else {
              Fac = cellnoise(vector(seeda,seedb,Pos[1]>nlev));
            }
      } else {
        Fac = cellnoise(vector(seeda,seedb,-1));
      }
    }
  }
}
(The code is also available on GitHub.)

Sample node setup

The example image at the top of this article was made with the following node setup (click to enlarge)
:

Further work

Obviously we need more sub patterns for the individual stones and add some distortion to the underlying coordinates to make really random stones. It would probably also be a good idea to vary the spacing between the stones and use the output value to drive displacement and bump maps and the code could certainly do with a bit of cleanup but I thinks this approach is at least shows promiss.

5 comments:

  1. Replies
    1. didn't test it but nothing has changed with regard to OSL in the last couple of Blender versions so I guess it should just work. (Just don't forget to enable OSL in the render settings and remember that OSL only works on the CPU)

      Delete
  2. Using 2.78, beginner here. This is perfect for constructing a seawall I'm attempting. After I add the mesh wall, I get an error.
    "Traceback (most recent call last)....
    line 188, in execute offset = Vector((random(). ramdp,(), 0}} of obj.randomuv else Vector ((0,0,0))
    AttributeError: 'Object' object has not attribute 'randomuv'
    location: :-1

    I do not code.
    Some assistance would be deeply appreciated. Would love to make it work.
    Thanks,
    Alan (Bumper)

    ReplyDelete
  3. Thank you Alan, fixed it (although you are actually talking about the little add-on mentioned in this post (http://blog.michelanders.nl/2014/09/drystone-create-irregular-stone-walls_64.html )

    ReplyDelete
  4. Hi Roy,

    Couldn't you perhaps just feed the factor output into some comparison node and check whether is is < 0.02? Only the stones get a (random) output bigger than w (defined in line 4) but the mortar is always 0.0 (i don't know much about Octane, but in Blender the Math node can do comparisons). Alternatively you could add an extra output and do it directly in osl.

    ReplyDelete