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 antialiassed 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>]
}
}
}

