A Hagelslag (sprinkles) OSL shader for Blender Cycles

Most of the noise patterns available in Blender are either continuous (like noise or musgrave) or serve a specific function (like voronoi or bands). None of these are easily adapted to generate the not quite microscopic dust particles that leave their marks in for example fingerprints. While developing such a shader I came across a great video by CGPGrey on Holland/The Netherlands, in which he mentions the national breakfast confectionery hagelslag (chocolate sprinkles). Because the shader we present here is easily adapted to generate sprinkle patters, I dubbed it the hagelslag shader, although originally I intended to present a picture of rod-like bacteria such as Bacillus subtilis, my guess is that people find this a tastier example.

The environment lighting and background plate in the picture is provided by HDRSource the Gold Room from HDRLabs.com. And yes I know the bread crust doesn't look tasty at all, that is why I always removed it as a child.
The code I presented earlier doesn't work on all version of the OSL compiler. The culprit was the return statement inside the actual shader definition. I think this is a bug in OSL , a return from a shader function should be possible, but I updated the code nevertheless. I tested it on Blender r53600 and r54202 on 64 bits Windows 7
The code below is self contained, which means I have included a replacement for the missing distance() function but I do not discuss that one here as I published a small blog article on it elsewhere.
The basic pattern is based on determining the distance to randomly scattered line segments. In its most pure form it might look like this:
The shader code is presented below and the inputs of the shader are (beside the position and scale) the number of sprinkles to produce per area (Np), a random Seed to vary the pattern, the Radius which controls the width of the sprinkle, and the Size which governs the length of the sprinkles. The output Fac is between 1 and 0, gradually reducing from centerline to edge.
#include "stdosl.h"

// replacement for the missing distance(p, p1, p2) function
float minimum_distance(point v, point w, point p) {
  vector s = w - v;
  float l2 = dot(s,s);
  if (l2 == 0.0) return distance(p, v);
  float t = dot(p - v, s) / l2;
  if (t < 0.0) return distance(p, v);
  else if (t > 1.0) return distance(p, w);
  vector projection = v + t * (s);
  return distance(p, projection);
}

shader sprinkles(
 point Pos = P,
 float Scale = 1,
 int Np = 1,
 int Seed = 42,
 float Radius = 0.05,
 float Size = 1,
 output float Fac = 0
){
 point p = Pos * Scale;
 point f = floor(p);
 
 int xx,yy,np;
 vector one = 1;
 
 for( xx=-1; xx<=1; xx++){
  for( yy=-1; yy<=1; yy++){
   point ff = f + vector(xx,yy,0);
   
   vector dp = vector(5,7,11);
   vector da = vector(5,3,1);
   vector dm = vector(7,5,2);
   
   for( np=0; np < Np; np++){
    vector pd1 = 2*cellnoise(ff+dp)-one;
    vector pd2 = 2*cellnoise(ff+dp+Seed)-one;
    
    dp += da;
 dp *= dm;
 dp = mod(dp,10000);
    
    point p1 = ff + pd1;
    point p2 = ff + pd2;
    
    p2 = (p2 - p1)*Size+p1;
    
    // reduce to 2D 
    p1[2]=0;
    p2[2]=0;
    p [2]=0;
    
    float r = minimum_distance(p1,p2,p);
    if ( r < Radius ) {
  //printf("%2.f %.2f\n",p,r);
     Fac = 1 - r/Radius;
    }
   }
  }
 }
}

Example node setup

The node setup for the plain pattern looks like this:
It uses a mix node that switches between two material based on whether Fac is larger than zero.
The shader node used for the image of the sandwich with the chocolate sprinkles is a variation on that:
Because the radius of a chocolate sprinkles varies a little bit along its length, we perturb the Radius with some noise (red box). The yellow box lets us switch between a completely transparent material and a chocolate material. (We use a transparent material here because we stacked two layers of sprinkles on top of each other, with different values for Seed to get a the effect of a thick layer of sprinkles. The green box converts the linear values of Fac to a rounded bump by calculating the sqaure root (power 0.5) and plugging it into a bump node. The brown box is our chocolate material, simply a dark brown but rather glossy material.

1 comment:

  1. You may be interested that I am including your great work in the collection of shaders that I have started at https://github.com/sambler/osl-shaders

    One thing I was trying to add with this shader was a way to define a colour for each piece or maybe an index for each piece that could be fed into a colour ramp so we could do coloured sprinkles. The best I could come up with was using a coloured cell noise but that changed colour within each piece.

    ReplyDelete