Patterns and Noise

© Mike Williams 2002,2003,2004

Up | Previous: New Tricks | Next: Built In Functions | Alphabetical Index

pattern01.jpg

function {y-f_wood(x*2,0,z*2)*0.2 }

Pattern Functions

It's now possible to generate isosurfaces from patterns in a similar way to the way they can be generated from pigments. Generating directly from patterns is more efficient, since the pattern only generates one float value for each point in space, and that's all we need. When we use a pigment, the underlying pattern is used together with a colour_map to generate a colour vector and then we only use one component of that vector to generate the isosurface. By generating the isosurface directly from the pattern we cut out the middle step.
pattern01b.jpg

#declare P = function {
  pigment { wood 
  colour_map {[0 rgb 0][1 rgb 1]}}
}
isosurface {
  function {y-P(x*2,0,z*2).grey*0.2}
This is how we would generate the same surface from a pigment. The result is the same, but the processing is slightly less efficient. The difference in speed is only about 5% and there's no difference in the memory usage, so there's no real need to use pattern functions if you're happy with pigment functions.
pattern01c.jpg

#declare P = function { 
   pattern{wood turbulence 0.02}
}
isosurface {
  function {y-P(x*2,0,z*2)*0.2 }
Some pattern functions are built into "functions.inc", as above, but we could just as well define our own pattern functions directly. This allows us to add some pattern modifiers such as turbulence and warps.

Available patterns

Other patterns, like brick, gradient, mandel, onion, planar and radial contain discontinuities.
At a discontinuity the gradient becomes infinite and the pattern does not work very well as an isossurface.
 

f_agate

f_boxed

f_bozo

f_bumps

f_crackle

f_cylindrical

f_dents

f_granite

f_leopard

f_marble

f_ripples

f_spherical

f_spiral1

f_spotted

f_waves

f_wrinkles
pattern02.jpg

function {y-f_sine_wave(x,0.1,3)}
pattern03.jpg

function {y-f_scallop_wave(x,0.1,3)}

f_sine_wave and f_scallop_wave

These new functions behave differently to the other built in functions, in that their parameters aren't x,y,z but value,amplitude,frequency.

[If I'd have been creating these functions I would have defined them to be called like "my_sine_wave(x,y,z,amplitude,frequency)" even if the y and z values are not used, for consistency with all the other functions]

The "value" parameter is expected to be something that increases linearly across the space that you're working in. For example "x" or "z" or "3*x + y". The "amplitude" and "frequency" parameters are expected to be constants.

[You don't have to use the expected types of parameters, you could use variables for "amplitude" and "frequency" and a constant for "value" but the result probably won't be a sine_wave or scallop_wave any longer. You can use unexpected things for the parameters of any of the built in functions, e.g. function { f_sphere(x, y, z, abs(sin(x-y)))} misuses the f_sphere function by specifying a radius that isn't constant, but the result isn't much like a sphere].

f_hexagon

Although f_hexagon is documented as if it were a pattern function, it is actually a pigment function, so the syntax is slightly different. See Pigments as functions.
warp01.jpg warp02.jpg warp03.jpg

Warp with Patterns

It's also possible to use warps with patterns.

The top image shows a black hole warp of a wood pattern.


#declare F=function{pattern{
  wood
  scale 0.15
  warp {black_hole 0,1 strength 2}
  }
}
The second image shows a repeat warp of a ripple pattern.

#declare F=function{pattern{
  ripples
  scale 0.2
  rotate y*30
  warp{repeat x*0.4 flip x}
  }
}
The third image shows a cylindrical warp of a leopard pattern.

#declare F=function{pattern{
  leopard
  warp{cylindrical
         orientation y 
         dist_exp 1.5 
       }
  scale 0.1
  }
}
mountain4.jpg mountain4hf.jpg

Isosurfaces vs Height Field Patterns

You may have noticed that it's now possible to use functions in height fields. It may well be preferable to use such height fields instead of isosurfaces when creating certain sorts of scenes, particularly landscapes.

The upper image on the left was created with an isosurface


#declare P1=function{pattern{leopard 
   turbulence 0.3 scale 0.05}}
#declare P2=function{pattern{crackle 
   turbulence 0.3 scale 0.7}}
#declare P=function{P1(x,0,z)*0.3 +P2(x,0,z)}
  isosurface {
    function { y - P(x,0,1-z)}
    contained_by{box{<0,0,0>,<1,1,1>}}
    translate <-0.5,0,-0.5>
The lower image was created with a height field using the same pattern.

height_field{function 300,300 {P(x,0,y)}
  translate <-0.5,0,-0.5>
The odd looking P(x,0,1-z) and P(x,0,y) adjust for the different ways that isosurface and height field data is oriented, so that the same parts of the function are used in the same places. This allows you to develop your scene using the fast height field code and then produce a final render using the isosurface code which may be 20 times slower.

The significant differences are

  • The height_field renders very much faster
  • The resolution of the height_field is defined by the source file. If you render a larger image there will be more fine detail in the isosurface unless you increase the height field resolution.
  • The height field uses more memory because the entire height field pattern is rendered and stored in memory.
density01.jpg

density02.jpg

Density patterns

Anything that can be used as a density pattern for media can now be used as an isosurface function. That includes DF3 format files. The upper image on the left uses the spiral.df3 example file that comes with the POVRay distribution.
#declare F=function{ pattern{
     density_file df3 "spiral.df3"
     interpolate 1 }
}
isosurface {function { 0.05 - F(x,y,z) }
Similarly, any function can be used as a media density pattern.
media{
  scattering {1, 0.7}
  density {
    function {
      (sin(x*10)*0.2+y*2) -  f_noise3d(x*20,y*20,z*20)
    }
  }
}
noise01.jpg

function{y-f_noise_generator
  (x*3,0,z*3,1)*0.2}
noise02.jpg

function{y-f_noise_generator
  (x*3,0,z*3,2)*0.4}
noise03.jpg

function{y-f_noise_generator
  (x*3,0,z*3,3)*0.4}
noise04.jpg

function{y-f_snoise3d(x*3,0,z*3)*0.2}

Types of noise

POV-Ray 3.5 has three types of noise.

The old noise algorithm looked OK when used for generating bozo pigments, and bump and dent normals, but when used in an isosurface it becomes clear that there are ugly plateau regions. This is now known as noise type 1. The plateau artefacts are generated when the noise function evaluates to a value outside the region 0 to 1, and the result gets clipped to keep it within that region.

Noise type 2 is the same algorithm as type 1, except that the values are scaled down so that they don't need to be clipped.

Noise type 3 is an entirely new perlin noise algorithm.

Type 3 is default, and it's probably a good idea to stick with it unless you have an image created with a previous version where you want to reproduce the behaviour of the old noise. In which case use type 1 if you want to reproduce the plateau artefacts or type 2 if you just need the bumps in the same places and don't want the plateaux.

Noise type 1 produces a stronger effect than the other two types (because it is effectively scaled over a range greater than [0,1]) so I've scaled it down in the top image on the left.

The type of noise can either be chosen by using

  • f_noise_generator(x, y, z, n)
  • noise_generator n as a pattern modifier
  • global_settings {noise_generator n} in which case it affects everything that uses the noise algorithm, such as bozo, bumps, dents and f_noise3d().
The f_snoise3d() function returns a noise value in the range [-1,+1] instead of the range [0,+1]. I've reduced the scale to get about the same amount of bumpiness as the other noise functions.

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

Up | Previous: New Tricks | Next: Built In Functions | Alphabetical Index