Functions as Pigments

© Mike Williams 2001,2002,2003,2004

Up | Previous: Using Pigments as Functions Next: Parametric Equations | Alphabetical Index

As we saw on the previous page, pigment patterns and isosurface functions are fundamentally just things that supply numerical values to all points in space.

It turns out that we are allowed to create patterns from functions and use them for pigments or normals. In this way it's possible to create a pigment that obeys any mathematical equation we like.


sphere {0,1
  pigment { function {sin(x*10) + 10*y}
    color_map { [0.0 rgb <1,0,0>]
                [0.5 rgb <1,1,0>]
                [1.0 rgb <1,0,0>]
    }
  }
}

sphere {0,1
  pigment { function {sin(x*30) + sin(y*30)}
    color_map { [0.0 rgb <1,0,0>]
                [0.2 rgb <0,0,1>]
                [0.5 rgb <1,1,0>]
                [0.7 rgb <0,1,0>]
                [1.0 rgb <1,0,0>]
    }
  }
}

box {-1,1
  pigment { function {x*x + y*y +z*z}
    color_map { [0.0 rgb <1,0,0>]
                [0.5 rgb <1,1,0>]
                [1.0 rgb <1,0,0>]
    }
  }
}

box {-1,1
  pigment { function {(x*x + z*z)*3}
    color_map { [0.0 rgb <1,0,0>]
                [0.5 rgb <1,1,0>]
                [1.0 rgb <1,0,0>]
    }
  }
}

sphere {0,1
  pigment { function { sin(x*20)/sin(y*20)*0.7}
    color_map { [0.0 rgb <1,0,0>]
                [0.5 rgb <1,1,0>]
                [1.0 rgb <1,0,0>]
    }
  }
}

sphere {0,1
  pigment { function { atan2(z,x)*2 }
    color_map { [0.0 rgb <1,0,0>]
                [0.5 rgb <1,1,0>]
                [1.0 rgb <1,0,0>]
    }
  }
}
It's even possible to start out with pigment patterns, convert them into functions, perform mathematical operations on those functions that are not possible with pigments, then convert them back into pigments again.

#declare F1 = function{pigment{onion scale 0.5}}
#declare F2 = function{pigment{leopard scale 0.1}}
                    
box {-1,1
  pigment { function {F1(x,y,z).grey
            + F2(x,y,z).grey*0.5}
    color_map { [0.0 rgb <1,0,0>]
                [0.5 rgb <1,1,0>]
                [1.0 rgb <1,0,0>]
    }
  }
}
In this case, I've made functions F1 and F2 from the onion and leopard pigment patterns and simply added them together.
If you use certain built in functions to generate pigments you may find some areas (like alternate corners of the following cube) where there are patches of plain colour. This isn't a natural feature of the function, but an artefact of the library code.

These functions set a bound on the maximum numerical value that can be returned. This value is usually 10.0 but a few surfaces allow the value to be passed as a parameter. If there's a point where the function calculates a value greater than 10.0, the library returns the value 10.0, which for the purposes of calculating a pigment is equivalent to 0.0

I've made this more apparent in this image by mapping values that are very close to 0.0 to white. Where the function values vary the white stripe is so thin that it gets anti-aliassed into invisibility, but in areas where the function would exceed 10.0 there is plain whiteness. There are 10 yellow bands visible in this image, corresponding to regions where the Steiner's Roman function takes the values 0.5 to 9.5.


#include "functions.inc"
box {-0.5, 0.5
  pigment { function { f_steiners_roman(x,y,z,90) }
    color_map { [0.0   rgb <1,1,1>]
                [0.001 rgb <1,0,0>]
                [0.5   rgb <1,1,0>]
                [0.999 rgb <1,0,0>]
                [1.0   rgb <1,1,1>]
    }
  }
}

Download a zip file containing the POV source files for all the images that appear on this page.

Up | Previous: Using Pigments as Functions Next: Parametric Equations | Alphabetical Index