OSL
Archemedes Spiral


return to main index



Introduction

For a point to be located on an Archemedian spiral it's distance from the center of the spiral (R) must be the same as some percentage (coeff) of it's rotational angle (theta) around the center of the spiral ie.

    R = coeff * theta

Naively, it might be considered that all a shader has to do is,
    - convert the s,t location of the point being shaded to polar coordinates,
    - set a value for coeff,
If the angle of rotation (theta) multiplied by the coeff matches it's radial distance (R) to within a small margin of error then colorize the micro-polygon, say black, otherwise set it to a background color.

Figure 1 shows that this naive approach, listing 1, does not work. The spiral terminates after it's first "turn" - the black line should continue along the full spiral shown in blue.



Figure 1 - woops


Listing 1 - woops.osl


shader 
woops(color bakcolor = 1,
      color pencolor = 0,
      float coeff = 0.03,
      float tolerance = 0.01,
      float s = 0 [[int lockgeom = 0]],
      float t = 0 [[int lockgeom = 0]],
    output color resultRGB = 0)
{
float     R, theta;
  
// Convert the s,t location of the shading point 
// to polar coordinates.
float   xdiff = s - 0.5, ydiff = t - 0.5;
R = sqrt(xdiff * xdiff + ydiff * ydiff);
theta = atan2(ydiff, xdiff); // -PI to PI radians
if(theta < 0.0)
    theta += 2 * M_PI;        // 0 to 2PI radians
  
// Check if we are close enough...
if( abs(R - coeff * theta) < tolerance)
    resultRGB = pencolor;
else
    resultRGB = bakcolor;
}

Clearly what the shader fails to take into account is the number of turns or cycles of the spiral. In other words the rotational angle calculated by the conversion to polar coordinates only goes from 0 to 2PI (0 to 360 degrees) when the angle should go from zero to 2PI * turns, say, 0 to 1080 degrees!

Listing 2 provides the code for a shader that will draw a full spiral. However, becuase of the simple antialiasing that it uses, the "blur" parameter may need to be adjusted in order to avoid the rendering artifacts shown on the right of figure 2.



Figure 2


Listing 2 - a_spiral.osl


shader 
a_spiral(color bakcolor = 1,
         color pencolor = 0,
         float center_s = 0.5,
         float center_t = 0.5, 
         float coeff = 0.03 [[string help = "Smaller values create more turns"]],
         float blur = 0.03  [[string help = "Adjust to avoid artifacts"]],
         float s = 0 [[int lockgeom = 0]],
         float t = 0 [[int lockgeom = 0]],
    output color resultRGB = 0)
{
resultRGB = 1;
float     R, theta;
  
// Convert the s,t location of the shading point to polar coordinates.
float   xdiff = s - center_s, ydiff = t - center_t;
R = sqrt(xdiff * xdiff + ydiff * ydiff);
theta = atan2(ydiff, xdiff); // -PI to PI radians
if(theta < 0.0)
    theta += M_2PI;        // 0 to 2PI radians
  
// Remap theta from 0 to 2PI radians to the range 0.0 to 1.0
float   percent_theta = theta/M_2PI;
  
// The unitRadius defines the maximum size of the spiral when
// theta is 2PI radians ie. 360 degrees. Unless it's size is 
// tweaked by the blur factor "cycles" increments before theta
// reaches 2PI and the spiral will have breaks ie. artifacts.
float     unitRadius = coeff * M_2PI + mix(-blur,blur,percent_theta);
  
// If the radial distance of shading point is, say, 1.5 the unit
// radius is somewhere within the 2nd "turn" of the spiral.
float     turns = floor(R/unitRadius);
  
theta = theta + turns * M_2PI;
  
// Use "turns" to calculate the full rotational angle. 
theta = coeff * theta;
  
// Determine how close the shading point is to the "true" spiral
float blend = smoothstep(0, blur, abs(theta - R));
  
resultRGB = mix(pencolor, bakcolor, blend);
  
// Uncomment the next couple of lines if you wish to view the 
// unitRadius. Because it is calculated with the addition of a
// value based on blur and theta, the unitRadius defines a circle
// with a gap, the size of which is twice the width of blur.
/*
if(abs(R - unitRadius) < 0.002)
    resultRGB = color(1,0,0);
*/
}










© 2002- Malcolm Kesson. All rights reserved.