Difference between revisions of "Documentation:Tutorial Section 3.2"

From POV-Wiki
Jump to navigation Jump to search
m (image reformatting)
m (Indexentries fix)
 
(30 intermediate revisions by 3 users not shown)
Line 7: Line 7:
 
<br>
 
<br>
 
<!--</wikitalk>--->
 
<!--</wikitalk>--->
<!--REMOVE--->====Isosurface Object====<!--THIS--->
+
====Isosurface Object====
 +
{{#indexentry:isosurface, tutorial}}
 +
<p>Isosurfaces are shapes described by mathematical functions.</p>
 +
 
 +
<p>In contrast to the other mathematically based shapes in POV-Ray, isosurfaces
 +
are approximated during rendering and therefore they are sometimes more
 +
difficult to handle. However, they offer many interesting possibilities, like real deformations and surface displacements</p>
 +
 
 +
<p>Some knowledge about mathematical functions and geometry is useful,
 +
but not necessarily required to work with isosurfaces.</p>
 
=====Simple functions=====
 
=====Simple functions=====
<p>For the start we will choose a most simple function: <code>x</code>
+
<p>Let's begin with something simple. In this first series of images, let's explore the <!--<linkto "Functions, user-defined">user defined function</linkto>--->[[Reference:Function|user defined function]] shown as <code>function&nbsp;{&nbsp;x&nbsp;}</code> that we see in the code example below. It produces the first image on the left, a simple box. The container, which is a requirement for the isosurface object, is represented by the box object and the <code>contained_by</code> keyword in the isosurface definition.</p>
The value of this function is exactly the current x-coordinate.</p>
 
 
 
<p>The isosurface object takes this function as a
 
<!--<linkto "Functions, user-defined">user defined function</linkto>--->[[Documentation:Reference Section 2.2#User-Defined Functions|user defined function]]:</p>
 
  
 
<pre>
 
<pre>
Line 22: Line 27:
 
</pre>
 
</pre>
  
<p>[[Image:TutImgIso_01.png|Isosurface sample (function { x })]]</p>
+
<p>You should have also noticed that in the image on the left, only half the box was produced, that's because the <code>threshold</code> keyword was omitted, so the <em>default value</em> 0 was used to evaluate the x-coordinate.</p>
 +
<p>In this next code example <code>threshold 1</code> was added to produce the center image.</p>
  
<p>the resulting shape is fairly simple: a box.</p>
+
<pre>
 +
  isosurface {
 +
    function { x }
 +
    threshold 1
 +
    contained_by { box { -2, 2 } }
 +
  }
 +
</pre>
  
<p>The fact that it is a box is only caused by the container object which is
+
<p>It is also possible to <em>remove</em> the visible surfaces of the container by adding the <code>open</code> keyword to the isosurface definition. </p>
required for an isosurface. You can either use a box or a sphere for this
+
<p>For the final image on the right, the following code example was used. Notice that the <em>omission</em> of the <code>threshold</code> keyword causes the x-coordinate to be again evaluated to zero.</p>
purpose.</p>
 
  
<p>So only one side of the box is made by the function in fact.  This surface
+
<pre>
is where the x-coordinate is 0 since 0 is the default threshold.  There usually
+
  isosurface {
is no reason to change this, since it is the most common and most suggestive
+
    function { x }
value, but you can specify something different by adding</p>
+
    open
 +
    contained_by { box { -2, 2 } }
 +
  }
 +
</pre>
  
<p><code> threshold 1</code></p>
+
<table class="centered" width="770px" cellpadding="0" cellspacing="10">
 +
<tr>
 +
  <td>
 +
    [[Image:TutImgIso_01.png|center|220px<!--leftpanel--->]]
 +
  </td>
 +
  <td>
 +
    [[Image:TutImgIso_02.png|center|220px<!--centerpanel--->]]
 +
  </td>
 +
  <td>
 +
    [[Image:TutImgIso_03.png|center|220px<!--rightpanel--->]]
 +
  </td>
 +
</tr>
 +
<tr>
 +
  <td>
 +
    <p class="caption">function { x }</p>
 +
  </td>
 +
  <td>
 +
    <p class="caption">function { x } with threshold 1</p>
 +
  </td>
 +
  <td>
 +
    <p class="caption">function { x } with open</p>
 +
  </td>
 +
</tr>
 +
</table>
  
<p>to the isosurface definition.</p>
+
<p class="Hint"><strong>Hint:</strong> The checkered ground plane is scaled to one unit squares.</p>
  
<p>[[Image:TutImgIso_02.png|Isosurface sample (function { x }, threshold 1)]]</p>
+
<p>For the last series of images in this section, let's try something different. These next two code examples were used to show the results of changing the user defined function to <code>function&nbsp;{&nbsp;x+y&nbsp;}</code> and <code>function&nbsp;{&nbsp;x+y+z&nbsp;}</code> respectively. They describe planes going through the origin, the function just describes the normal vector of the plane.</p>
  
<p>As you can see, the surface is now at x-coordinate 1.</p>
+
<pre>
 +
  isosurface {
 +
    function { x+y }
 +
    max_gradient 4
 +
    contained_by { box { -2, 2 } }
 +
  }
 +
</pre>
  
<p>We can also remove the visible surfaces of the container object by adding
+
<p class="Note"><strong>Note:</strong> To properly render these examples <code>max_gradient 4</code> was added to the isosurface definition, and will be explained later.</p>
the word 'open' to the isosurface definition.</p>
 
  
<p>[[Image:TutImgIso_03.png|Isosurface sample (function { x }, open)]]</p>
+
<pre>
 +
  isosurface {
 +
    function { x+y+z }
 +
    max_gradient 4
 +
    contained_by { box { -2, 2 } }
 +
  }
 +
</pre>
  
<p>For making it clearer what surfaces are the actual isosurface and what are
+
<table class="centered" width="460px" cellpadding="0" cellspacing="10">
caused by the container object, the color will be different in all the
+
<tr>
following pictures.</p>
+
  <td>
 +
    [[Image:TutImgIso_04.png|center|220px<!--leftpanel--->]]
 +
  </td>
 +
  <td>
 +
    [[Image:TutImgIso_05.png|center|220px<!--rightpanel--->]]
 +
  </td>
 +
</tr>
 +
<tr>
 +
  <td>
 +
    <p class="caption">plane function { x+y }</p>
 +
  </td>
 +
  <td>
 +
    <p class="caption">plane function { x+y+z }</p>
 +
  </td>
 +
</tr>
 +
</table>
  
<p>Now we replace the used function with something different:</p>
+
<p class="Note"><strong>Note:</strong> When appropriate, to better visualize the difference between the isosurface and the container object, the images in this tutorial have been color coded.</p>
 
 
<p><code>  function { x+y }</code></p>
 
 
 
<p>[[Image:TutImgIso_04.png|Isosurface sample (plane function)]]</p>
 
 
 
<p><code>  function { x+y+z }</code></p>
 
 
 
<p>[[Image:TutImgIso_05.png|Isosurface sample (plane function)]]</p>
 
 
 
<p class="Note"><strong>Note:</strong> We added <code>max_gradient 4</code> to the isosurface definition here, this will be explained later on.</p>
 
 
 
<p>All these functions describe planes going through the origin.  The
 
function just describes the normal vector of this plane.</p>
 
  
 
=====Several surfaces=====
 
=====Several surfaces=====
<p>The following two functions lead to identical results:</p>
+
<p>Now that you're starting to become familiar with <code>isosurface</code> syntax, there really isn't any need to show a code example for each and every image. You can always look back at the earlier examples when needed. The image captions will most often contain additional keyword hints when appropriate.</p>
 +
<p class="Note"><strong>Note:</strong> The user defined function portion will <em>always</em> use this color coded format: <code>function&nbsp;{&nbsp;x+y+z&nbsp;}</code></p>
  
<p><code> function { abs(x)-1 }</code></p>
+
<p>For the first image on the left, these two functions lead to identical results: <code>function&nbsp;{&nbsp;abs(x)-1&nbsp;}</code> and <code>function&nbsp;{&nbsp;sqrt(x*x)-1&nbsp;}</code> because both of these formulas have the same solution where the function value is 0, specifically <code>x=-1</code> and <code>x=1</code> in this example.</p>
<p><code> function { sqrt(x*x)-1 }</code></p>
+
<p>You can easily mix any of these elements in different combinations, but the results always produce planar surfaces. The last two images in this series used <code>function&nbsp;{&nbsp;abs(x)-1+y&nbsp;}</code> and <code>function&nbsp;{&nbsp;abs(x)+abs(y)+abs(z)-2&nbsp;}</code> respectively.</p>
  
<p>[[Image:TutImgIso_06.png|Isosurface sample (function { abs(x)-1 }, open)]]</p>
+
<table class="centered" width="700px" cellpadding="0" cellspacing="10">
 
+
<tr>
<p>You can see that there are two planes now.  The reason is that both formulas
+
  <td>
have the same two solutions (where the function value is 0),
+
    [[Image:TutImgIso_06.png|center|220px<!--leftpanel--->]]
namely <code>x=-1</code> and <code>x=1</code>.</p>
+
  </td>
 
+
  <td>
<p>We can now mix all these elements in different combinations, the results
+
    [[Image:TutImgIso_07.png|center|220px<!--centerpanel--->]]
always consist of plane surfaces:</p>
+
  </td>
 
+
  <td>
<p><code> function { abs(x)-1+y }</code></p>
+
    [[Image:TutImgIso_08.png|center|220px<!--rightpanel--->]]
 
+
  </td>
<p>[[Image:TutImgIso_07.png|Isosurface sample (combined linear functions)]]</p>
+
</tr>
 
+
<tr>
<p><code> function { abs(x)+abs(y)+abs(z)-2 }</code></p>
+
  <td>
 
+
    <p class="caption">identical results with open</p>
<p>[[Image:TutImgIso_08.png|Isosurface sample (combined linear functions)]]</p>
+
  </td>
 +
  <td>
 +
    <p class="caption">linear functions x &amp; y axis</p>
 +
  </td>
 +
  <td>
 +
    <p class="caption">linear functions x, y &amp; z axis</p>
 +
  </td>
 +
</tr>
 +
</table>
  
 
=====Non-linear functions=====
 
=====Non-linear functions=====
<p>Curved surfaces of many different kinds can be achieved with non-linear
+
<table class="centered" width="570px" cellpadding="0" cellspacing="10">
functions.</p>
+
<tr>
 +
  <td>
 +
    [[Image:TutImgIso_09.png|center|220px<!--leftpanel--->]]
 +
  </td>
 +
  <td>
 +
    <p class="tabletext">Curved surfaces of many different kinds can be achieved with non-linear
 +
functions. A square function creates the parabolic shape:<br><code>function&nbsp;{&nbsp;pow(x,2)+y&nbsp;}</code></p>
 +
  </td>
 +
</tr>
 +
<tr>
 +
  <td>
 +
    <p class="caption">a parabolic shape</p>
 +
  </td>
 +
  <td></td>
 +
</tr>
 +
</table>
  
<p><code> function { pow(x,2) + y }</code></p>
+
<table class="centered" width="570px" cellpadding="0" cellspacing="10">
 +
<tr>
 +
  <td>
 +
    <p class="tabletext">If you describe a circle in 2 dimensions with a constant in the 3rd dimension you get a cylinder:<br><code> function&nbsp;{&nbsp;sqrt(pow(x,2)+pow(z,2))-1&nbsp;}</code></p>
 +
  </td>
 +
  <td>
 +
    [[Image:TutImgIso_10.png|center|220px<!--rightpanel--->]]
 +
  </td>
 +
</tr>
 +
<tr>
 +
  <td></td>
 +
  <td>
 +
    <p class="caption">the cylinder shape</p>
 +
  </td>
 +
</tr>
 +
</table>
  
<p>[[Image:TutImgIso_09.png|Isosurface sample (non-linear function)]]</p>
+
<table class="centered" width="570px" cellpadding="0" cellspacing="10">
 +
<tr>
 +
  <td>
 +
    [[Image:TutImgIso_11.png|center|220px<!--leftpanel--->]]
 +
  </td>
 +
  <td>
 +
    <p class="tabletext">It's easy to change a cylinder into a cone, we just need
 +
to add a linear component in y-direction:<br><code>function&nbsp;{&nbsp;sqrt(pow(x,2)+pow(z,2))+y&nbsp;}</code></p>
 +
  </td>
 +
</tr>
 +
<tr>
 +
  <td>
 +
    <p class="caption">the cone shape</p>
 +
  </td>
 +
  <td></td>
 +
  <td></td>
 +
</tr>
 +
</table>
  
<p>You can see the parabolic shape caused by the square function.</p>
+
<table class="centered" width="570px" cellpadding="0" cellspacing="10">
 
+
<tr>
<p>To get a cylindrical surface we can use the following function.</p>
+
  <td>
 
+
    <p class="tabletext">No worries, creating a sphere is easy too. In this example <code>2</code> specifies the radius:<br><code> function&nbsp;{&nbsp;sqrt(pow(x,2)+pow(y,2)+pow(z,2))-2&nbsp;}</code></p>
<p><code> function { sqrt(pow(x,2) + pow(z,2)) - 1 }</code></p>
+
  </td>
 
+
  <td>
<p>In 2 dimensions it describes a circle, since it is constant in the 3rd
+
    [[Image:TutImgIso_12.png|center|220px<!--rightpanel--->]]
dimension, we get a cylinder:</p>
+
  </td>
 
+
</tr>
<p>[[Image:TutImgIso_10.png|Isosurface sample (cylinder function)]]</p>
+
<tr>
 
+
  <td></td>
<p>It is of course not difficult to change this into a cone, we just need
+
  <td>
to add a linear component in y-direction:</p>
+
    <p class="caption">the sphere shape</p>
 
+
  </td>
<p><code>  function { sqrt(pow(x,2) + pow(z,2)) + y }</code></p>
+
</tr>
 
+
</table>
<p>[[Image:TutImgIso_11.png|Isosurface sample (cone function)]]</p>
 
 
 
<p>And we of course can also make a sphere:</p>
 
 
 
<p><code>  function { sqrt(pow(x,2) + pow(y,2) + pow(z,2)) - 2 }</code></p>
 
 
 
<p>[[Image:TutImgIso_12.png|Isosurface sample (sphere function)]]</p>
 
 
 
<p>The <code>2</code> specifies the radius here.</p>
 
  
 
=====Specifying functions=====
 
=====Specifying functions=====
<p>As we have seen, the functions used to define the isosurface are written in the <code>function {...}</code> block.</p>
+
<p>Until now, we have seen, the functions used to define the isosurface were literally written in the <code>function {...}</code> block:</p>
 +
<pre>
 +
#declare Threshold = 1;
  
<p>Allowed are:</p>
 
<p>User defined functions (like equations). All float expressions and operators (see section
 
<!--<linkto "User-Defined Functions">User-Defined Functions</linkto>--->[[Documentation:Reference Section 2.2#User-Defined Functions|User-Defined Functions]]) which are legal
 
in POV-Ray, can be used.
 
<br>With the equation of a sphere <code>x^2+y^2+z^2 = Threshold</code> we get:</p>
 
<pre>
 
 
isosurface {
 
isosurface {
 
function {pow(x,2) + pow(y,2) + pow(z,2)}
 
function {pow(x,2) + pow(y,2) + pow(z,2)}
Line 140: Line 233:
 
</pre>
 
</pre>
  
<p>Functions can be declared first (see section <!--<linkto "User-Defined Functions">Declaring Functions</linkto>--->[[Documentation:Reference Section 2.2#User-Defined Functions|Declaring Functions]]) and
+
<p>Let's expand on that concept, and add some flexibility. Remember that user defined functions (like equations), all float expressions and operators which are legal in POV-Ray can be used, and that functions should be declared first, and then used in the isosurface. See the section <!--<linkto "User-Defined Functions">User-Defined Functions</linkto>--->[[Reference:Function|user defined function]] for more information.</p>
then used in the isosurface.</p>
+
 
 +
<p>This next example takes the above equation, and rewrites it as a user defined function. By default a function that takes three parameters (x,y,z) does not require you to explicitly specify the parameter names when declaring it,  however when <em>using</em> the identifier, the parameters <em>must</em> be specified.</p>
 +
 
 
<pre>
 
<pre>
#declare Sphere = function {pow(x,2) + pow(y,2) + pow(z,2)}
+
#declare Threshold = 1;
 +
 
 +
#declare Sphere = function {pow(x,2) + pow(y,2) + pow(z,2)};
 +
 
 
isosurface {
 
isosurface {
 
   function { Sphere(x,y,z) }
 
   function { Sphere(x,y,z) }
Line 151: Line 249:
 
</pre>
 
</pre>
  
<p>By default a function takes three parameters (x,y,z) and you do not have to explicitly specify
+
<p>However, if you need more or less than three parameters when declaring a function, you will also have to explicitly specify the parameter names.</p>
the parameter names when declaring it.
 
<br>When <em>using</em> the identifier, the parameters <em>must</em> be specified.
 
<br>On the other hand, if you need more or less than three parameters when declaring a function, you also have
 
to explicitly specify the parameter names.</p>
 
 
<pre>
 
<pre>
#declare Sphere = function(x,y,z,Radius) {
+
#declare Sphere = function (x,y,z,Radius) {pow(x,2) + pow(y,2) + pow(z,2) - pow(Radius,2)};
    pow(x,2) + pow(y,2) + pow(z,2) - pow(Radius,2)  
+
 
}
 
 
isosurface {
 
isosurface {
 
   function { Sphere(x,y,z,1) }
 
   function { Sphere(x,y,z,1) }
Line 167: Line 260:
  
 
=====Internal functions=====
 
=====Internal functions=====
<p>There are a lot of internal functions available in POV-Ray. For example
+
<p>There are a lot of internal functions available in POV-Ray. For example a sphere could also be generated with <code>function&nbsp;{&nbsp;f_sphere(x,&nbsp;y,&nbsp;z,&nbsp;2)&nbsp;}</code>, for these and other functions, see the <code>functions.inc</code> include file. Most of them are more complicated and it is usually faster to use them instead of a hand coded equivalent.</p>
a sphere could also be generated with <code>function { f_sphere(x, y, z, 2) }</code>
+
<p>See the <!--<linkto "internal functions">complete list</linkto>--->[[Reference:Functions.inc|complete list]] for details.</p>
These functions are declared in the <code>functions.inc</code> include file.
 
Most of them are more complicated and it is usually faster to use them instead of a
 
hand coded equivalent. See the
 
<!--<linkto "internal functions">complete list</linkto>--->[[Documentation:Reference Section 7.1#functions.inc|complete list]] for details.</p>
 
  
 
<p>The following makes a torus just like POV-Ray's torus object:</p>
 
<p>The following makes a torus just like POV-Ray's torus object:</p>
Line 185: Line 274:
 
</pre>
 
</pre>
  
<p>[[Image:TutImgIso_13.png|Isosurface sample (torus function)]]</p>
+
<table class="centered" width="570px" cellpadding="0" cellspacing="10">
 
+
<tr>
<p>The 4th and 5th parameter are the major and minor radius,
+
  <td>
just like the corresponding values in the <code>torus{}</code> object.</p>
+
    [[Image:TutImgIso_13.png|center|220px<!--leftpanel--->]]
 
+
  </td>
<p>The parameters x, y and z are required, because it is a declared function.
+
  <td>
You can also declare functions yourself like it is explained in the
+
    <p class="tabletext">The 4th and 5th parameters are the major and minor radius, just like the corresponding values in the <code>torus{}</code> object.</p>
<!--<linkto "Declaring User-Defined Float Functions">reference section</linkto>--->[[Documentation:Reference Section 2.2#Declaring User-Defined Float Functions|reference section]].</p>
+
    <p class="tabletext">The parameters x, y and z are required, because it is a declared function. You can also declare functions yourself like it is explained in the <!--<linkto "Declaring User-Defined Float Functions">reference section</linkto>--->[[Reference:Function#Declaring User-Defined Float Functions|reference section]].</p>
 +
  </td>
 +
</tr>
 +
<tr>
 +
  <td>
 +
    <p class="caption">the torus function</p>
 +
  </td>
 +
  <td></td>
 +
</tr>
 +
</table>
  
 
=====Combining isosurface functions=====
 
=====Combining isosurface functions=====
<p>We can also simulate some Constructive Solid Geometry with isosurface functions.  If
+
<p>We can also simulate some Constructive Solid Geometry with isosurface functions.  If you do not know about CSG we suggest you have a look at <em>[[Documentation:Tutorial Section 2#What is CSG?|What is CSG?]]</em> or the corresponding part of the [[Reference:Constructive Solid Geometry|reference section]] first.</p>
you do not know about CSG we suggest you have a look at
 
<!--<linkto "What is CSG?">What is CSG?</linkto>--->[[Documentation:Tutorial Section 2#What is CSG?|"What is CSG?"]] or the corresponding part of the <!--<linkto "Constructive Solid Geometry">reference section</linkto>--->[[Documentation:Reference Section 4.3#Constructive Solid Geometry|reference section]] first.</p>
 
  
<p>We will take two functions: a cylinder and a rotated box:</p>
+
<p>For this next group of images, consider the two functions for a cylinder and a rotated box:</p>
  
 
<pre>
 
<pre>
Line 206: Line 302:
 
</pre>
 
</pre>
  
<p>If we combine them the following way, we get a <em>merge</em>:</p>
+
<ol>
 +
  <li>If we combine them the following way, we get a <em>merge</em>:<br>
 +
<code>function { min(fn_A(x, y, z), fn_B(x, y, z)) }</code></li>
 +
  <li>An <em>intersection</em> can be obtained by using <code>max()</code> instead of <code>min()</code>:<br>
 +
<code>function { max(fn_A(x, y, z), fn_B(x, y, z)) }</code>
 +
</li>
 +
  <li>A <em>difference</em> is possible, by adding a minus (-) before the second function:<br>
 +
<code>function { max(fn_A(x, y, z), -fn_B(x, y, z)) }</code>
 +
</li>
 +
</ol>
  
<p><code>function { min(fn_A(x, y, z), fn_B(x, y, z)) }</code></p>
+
<table class="centered" width="700px" cellpadding="0" cellspacing="10">
 
+
<tr>
<p>[[Image:TutImgIso_14.png|Isosurface sample (merge)]]</p>
+
  <td>
 
+
    [[Image:TutImgIso_14.png|center|220px<!--leftpanel--->]]
<p>An <em>intersection</em> can be obtained by using
+
  </td>
<code>max()</code> instead of <code>min()</code>:</p>
+
  <td>
 +
    [[Image:TutImgIso_15.png|center|220px<!--centerpanel--->]]
 +
  </td>
 +
  <td>
 +
    [[Image:TutImgIso_16.png|center|220px<!--rightpanel--->]]
 +
  </td>
 +
</tr>
 +
<tr>
 +
  <td>
 +
    <p class="caption">merge example</p>
 +
  </td>
 +
  <td>
 +
    <p class="caption">intersection example</p>
 +
  </td>
 +
  <td>
 +
    <p class="caption">difference example</p>
 +
  </td>
 +
</tr>
 +
</table>
  
<p><code>function { max(fn_A(x, y, z), fn_B(x, y, z)) }</code></p>
+
<p>Apart from basic CSG you can also obtain smooth transits between the different surfaces, for instance the <!--<linkto "blob object">blob object</linkto>--->[[Documentation:Tutorial Section 3.1#Blob Object|blob object]]:</p>
 
 
<p>[[Image:TutImgIso_15.png|Isosurface sample (intersection)]]</p>
 
 
 
<p>Of course also <em>difference</em> is possible, we just have to
 
add a minus (-) before the second function:</p>
 
 
 
<p><code>function { max(fn_A(x, y, z), -fn_B(x, y, z)) }</code></p>
 
 
 
<p>[[Image:TutImgIso_16.png|Isosurface sample (difference)]]</p>
 
 
 
<p>Apart from basic CSG you can also obtain smooth transits between the
 
different surfaces (like with the <!--<linkto "blob object">blob object</linkto>--->[[Documentation:Tutorial Section 3.1#Blob Object|blob object]])</p>
 
  
 
<pre>
 
<pre>
   #declare Blob_threshold=0.01;
+
   #declare Blob_Threshold=0.01;
  
 
   isosurface {
 
   isosurface {
 
     function {
 
     function {
       (1+Blob_threshold)
+
       (1+Blob_Threshold)
       -pow(Blob_threshold, fn_A(x,y,z))
+
       -pow(Blob_Threshold, fn_A(x,y,z))
       -pow(Blob_threshold, fn_B(x,y,z))
+
       -pow(Blob_Threshold, fn_B(x,y,z))
 
     }
 
     }
 
     max_gradient 4
 
     max_gradient 4
Line 243: Line 354:
 
</pre>
 
</pre>
  
<p>[[Image:TutImgIso_17.png|Isosurface sample (blob)]]</p>
+
<table class="centered" width="570px" cellpadding="0" cellspacing="10">
 
+
<tr>
<p>The <code>Blob_threshold</code> value influences the smoothness of
+
  <td>
the transit between the shapes.  a lower value leads to sharper edges.</p>
+
    [[Image:TutImgIso_17.png|center|220px<!--leftpanel--->]]
<p>The function for a negative blob looks like:</p>
+
  </td>
 +
  <td>
 +
    <p class="tabletext">The <code>Blob_Threshold</code> value influences the smoothness of
 +
the transit between the shapes.  A lower value leads to sharper edges, and it's function looks like:</p>
 
<pre>
 
<pre>
function{fn_A(x,y,z) + pow(Blob_threshold,(Fn_B(x,y,z) + Strength))}
+
function{fn_A(x,y,z) + pow(Blob_Threshold,(fn_B(x,y,z) + Strength))}
 
</pre>
 
</pre>
 +
  </td>
 +
</tr>
 +
<tr>
 +
  <td>
 +
    <p class="caption">smooth transitions using blob</p>
 +
  </td>
 +
</tr>
 +
</table>
  
 
=====Noise and pigment functions=====
 
=====Noise and pigment functions=====
<p>Some of the <!--<linkto "internal functions">internal functions</linkto>--->[[Documentation:Reference Section 7.1#Internal Functions|internal functions]]
+
<p>Some of the <!--<linkto "internal functions">internal functions</linkto>--->[[Reference:Function#Internal Pre-Defined Functions|internal functions]] have a random or noise-like structure</p>
have a random or noise-like structure</p>
 
  
<p>Together with the pigment functions they are one of the most powerful tools
+
<p>Together with the pigment functions they are one of the most powerful tools for designing isosurfaces. We can add real surface displacement to the objects rather than only normal perturbation known from the <!--<linkto "normal">normal</linkto>--->[[Reference:Normal|normal]] statement.</p>
for designing isosurfaces. We can add real surface displacement to the objects
 
rather than only normal perturbation known from the
 
<!--<linkto "normal">normal</linkto>--->[[Documentation:Reference Section 5#Normal|normal]] statement.</p>
 
  
 
<p>The relevant internal functions are:</p>
 
<p>The relevant internal functions are:</p>
Line 265: Line 383:
 
<ul>
 
<ul>
 
<li><code>f_noise3d(x,y,z)</code><br>
 
<li><code>f_noise3d(x,y,z)</code><br>
uses the <!--<linkto "noise generator">noise generator</linkto>--->[[Documentation:Reference Section 5.6#Noise Generators|noise generator]] specified in
+
uses the [[Reference:Pattern Modifiers#Noise Generators|noise generator]] specified in <code>global_settings</code> and generates structures like the bozo pattern.</li>
<code>global_settings{}</code> and generates structures like the bozo pattern.</li>
 
 
 
 
<li><code>f_noise_generator(x, y, z, noise_generator)</code><br>
 
<li><code>f_noise_generator(x, y, z, noise_generator)</code><br>
 
generates noise with a specified noise generator.</li>
 
generates noise with a specified noise generator.</li>
 
 
 
<li><code>f_ridged_mf(x, y, z, H, Lacunarity, Octaves, Offset, Gain, noise_generator)</code><br>
 
<li><code>f_ridged_mf(x, y, z, H, Lacunarity, Octaves, Offset, Gain, noise_generator)</code><br>
 
generates a ridged multifractal pattern.</li>
 
generates a ridged multifractal pattern.</li>
 
 
<li><code>f_ridge(x, y, z, Lambda, Octaves, Omega, Offset, Ridge, noise_generator)</code><br>
 
<li><code>f_ridge(x, y, z, Lambda, Octaves, Omega, Offset, Ridge, noise_generator)</code><br>
 
generates another noise with ridges.</li>
 
generates another noise with ridges.</li>
 
 
<li><code>f_hetero_mf(x, y, z, H, Lacunarity, Octaves, Offset, T, noise_generator)</code><br>
 
<li><code>f_hetero_mf(x, y, z, H, Lacunarity, Octaves, Offset, T, noise_generator)</code><br>
 
generates heterogenic multifractal noise.</li>
 
generates heterogenic multifractal noise.</li>
 
</ul>
 
</ul>
  
<p>Using pure noise3d as a function results in the following picture:</p>
+
<table class="centered" width="570px" cellpadding="0" cellspacing="10">
 +
<tr>
 +
  <td>
 +
    <p class="tabletext">Using this simple noise3d function results in the image on the right. The value <code>-0.5</code> matches the default <code>threshold</code> value of zero. The <code>f_noise3d</code> function returns values between 0 and 1:</p>
 +
    <p class="tabletext"><code>function&nbsp;{&nbsp;f_noise3d(x,y,z)-0.5&nbsp;}</code></p>
 +
  </td>
 +
  <td>
 +
    [[Image:TutImgIso_18.png|center|220px<!--rightpanel--->]]
 +
  </td>
 +
</tr>
 +
<tr>
 +
  <td></td>
 +
  <td>
 +
    <p class="caption">simple noise3d function</p>
 +
  </td>
 +
</tr>
 +
</table>
  
<p><code> function { f_noise3d(x, y, z)-0.5 }</code></p>
+
<table class="centered" width="570px" cellpadding="0" cellspacing="10">
 +
<tr>
 +
  <td>
 +
    <p class="tabletext">In these next two images the noise function was added to a plane function. The x-parameter was set to 0 so the noise function is constant in x-direction. This way we achieve the typical heightfield structure.</p>
 +
  </td>
 +
</tr>
 +
</table>
  
<p>[[Image:TutImgIso_18.png|Isosurface sample (noise3d)]]</p>
+
<table class="centered" width="570px" cellpadding="0" cellspacing="10">
 +
<tr>
 +
  <td>
 +
    [[Image:TutImgIso_19.png|center|220px<!--leftpanel--->]]
 +
  </td>
 +
  <td>
 +
    <p class="tabletext">With this and the other functions you can generate objects similar to heightfields, having the advantage that a high resolution can be achieved without high memory requirements:</p>
 +
    <p class="tabletext"><code>function&nbsp;{&nbsp;x&nbsp;+&nbsp;f_noise3d(0,y,z)&nbsp;}</code></p>
 +
  </td> 
 +
</tr>
 +
<tr>
 +
  <td>
 +
    <p class="caption">a noise3d heightfield</p>
 +
  </td>
 +
  <td></td>
 +
</tr>
 +
</table>
  
<p class="Note"><strong>Note:</strong> The <code>-0.5</code> is only there to make it match to the used threshold value of 0, the <code>f_noise3d</code> function returns values between 0 and 1.</p>
+
<table class="centered" width="570px" cellpadding="0" cellspacing="10">
 +
<tr>
 +
  <td>
 +
    <p class="tabletext">The noise function can of course also be subtracted which results in an <em>inverted</em> version:</p>
 +
    <p class="tabletext"><code>function&nbsp;{&nbsp;x&nbsp;-&nbsp;f_noise3d(0,y,z)&nbsp;}</code></p>
 +
  </td>
 +
  <td>
 +
    [[Image:TutImgIso_20.png|center|220px<!--rightpanel--->]]
 +
  </td>
 +
</tr>
 +
<tr>
 +
  <td></td>
 +
  <td>
 +
    <p class="caption">a noise3d heightfield - inverted</p>
 +
  </td>
 +
</tr>
 +
</table>
  
<p>With this and the other functions you can generate objects similar to
+
<table class="centered" width="570px" cellpadding="0" cellspacing="10">
heightfields, having the advantage that a high resolution can be achieved
+
<tr>
without high memory requirements.</p>
+
  <td>
 +
    [[Image:TutImgIso_21.png|center|220px<!--leftpanel--->]]
 +
  </td>
 +
  <td>
 +
    <p class="tabletext">Of course we can also add noise to any other function. If the noise function is very strong this can result in several separated surfaces.</p>
 +
    <p class="tabletext"><code>function&nbsp;{&nbsp;f_sphere(x,y,z,1.2)&nbsp;-&nbsp;f_noise3d(x,y,z)&nbsp;}</code></p>
 +
  </td>
 +
</tr>
 +
<tr>
 +
  <td>
 +
    <p class="caption">noise3d on a sphere</p>
 +
  </td>
 +
  <td>
 +
  </td>
 +
</tr>
 +
</table>
  
<p><code>  function { x + f_noise3d(0, y, z) }</code></p>
+
<table class="centered" width="570px" cellpadding="0" cellspacing="10">
 
+
<tr>
<p>[[Image:TutImgIso_19.png|Isosurface sample (noise3d 'heightfield')]]</p>
+
  <td>
 
+
    <p class="tabletext">This is a noise function applied to a sphere surface, we can influence the intensity of the noise by multiplying it with a factor and change the scale by multiplying the coordinate parameters:</p>
<p>The noise function can of course also be subtracted which results in an
+
    <p class="tabletext"><code>function&nbsp;{</code><br><code>&nbsp;&nbsp;f_sphere(x,y,z,1.6)&nbsp;-</code><br><code>&nbsp;&nbsp;&nbsp;&nbsp;f_noise3d(x*5,y*5,z* )&nbsp;*&nbsp;0.5<br>&nbsp;&nbsp;&nbsp;&nbsp;}</code></p>
'inverted' version:</p>
+
  </td>
 
+
  <td>
<p><code>  function { x - f_noise3d(0, y, z) }</code></p>
+
    [[Image:TutImgIso_22.png|center|220px<!--rightpanel--->]]
 
+
  </td>
<p>[[Image:TutImgIso_20.png|Isosurface sample (noise3d 'heightfield' inverted)]]</p>
+
</tr>
 
+
<tr>
<p>In the last two pictures we added the noise function to a plane function.
+
  <td></td>
The x-parameter was set to 0 so the noise function is constant in x-direction.
+
  <td>
This way we achieve the typical heightfield structure.</p>
+
    <p class="caption">noise3d on a sphere - scaled</p>
 
+
  </td>
<p>Of course we can also add noise to any other function.  If the noise function
+
</tr>
is very strong this can result in several separated surfaces.</p>
+
</table>
 
 
<p><code>  function { f_sphere(x, y, z, 1.2) - f_noise3d(x, y, z) }</code></p>
 
 
 
<p>[[Image:TutImgIso_21.png|Isosurface sample (noise3d on sphere)]]</p>
 
 
 
<p>This is a noise function applied to a sphere surface, we can influence
 
the intensity of the noise by multiplying it with a factor and change the
 
scale by multiplying the coordinate parameters:</p>
 
 
 
<p><code> function { f_sphere(x, y, z, 1.6) - f_noise3d(x * 5, y * 5, z * 5) * 0.5 }</code></p>
 
 
 
<p>[[Image:TutImgIso_22.png|Isosurface sample (noise3d on sphere scaled)]]</p>
 
  
 
<p>As alternative to noise functions we can also use any pigment in a function:</p>
 
<p>As alternative to noise functions we can also use any pigment in a function:</p>
Line 338: Line 507:
 
</pre>
 
</pre>
  
<p>This function is a vector function returning a (color) vector.For use in isosurface functions they <em>must</em> be declared first. When using the identifier, you have to specify which component of the color vector should be used. To do this, the dot notation is used: <code>Function(x,y,z).red</code>.</p>
+
<p>This is a vector function, it returns a color vector for use in isosurface functions. They <em>must</em> be pre-declared first. When using the identifier, you have to specify which component of the color vector should be used.</p>
 +
<p>To do this, the dot notation is used. Refer to the above example: <code>fn_Pigm(x,y,z).red</code></p>
  
<p>
+
<p>A color vector has five components, their supported dot types to access these components are:</p>
A color vector has five components. Supported dot types to access these components are:  
 
</p>
 
  
<ul>
+
<ol>
 +
  <li><code>fn_Pigm( ).x</code> | <code>fn_Pigm( ).u</code> | <code>fn_Pigm( ).red</code><br>
 +
to get the red value of the color vector </li>
 +
  <li><code>fn_Pigm( ).y</code> | <code>fn_Pigm( ).v</code> | <code>fn_Pigm( ).green</code><br>
 +
to get the green value of the color vector</li>
 +
  <li><code>fn_Pigm( ).z</code> | <code>fn_Pigm( ).blue</code><br>
 +
to get the blue value of the color vector</li>
 +
  <li><code>fn_Pigm( ).filter</code> | <code>fn_Pigm( ).f</code><br>
 +
to get the filter value of the color vector</li>
 +
  <li><code>fn_Pigm( ).transmit</code> | <code>fn_Pigm( ).t</code><br>
 +
to get the transmit value of the color vector</li>
 +
</ol>
  
<li>
+
<p>And two special purpose operators, their supported dot types to access these operators are:</p>
F( ).<code>x</code> | F( ).<code>u</code> | F( ).<code>red</code>  
+
<p class="Note"><strong>Note:</strong> The <code>.hf</code> operator is experimental and will generate a warning.</p>  
<ul>
 
  
<li>
+
<ol>
to get the red value of the color vector  
+
  <li><code>fn_Pigm( ).gray</code> to get the gray value of the color vector<br>
</li>
+
<em>gray value</em> = Red*29.7% + Green*58.9% + Blue*11.4% </li>
 +
  <li><code>fn_Pigm( ).hf</code> to get the height_field value of the color vector<br>
 +
<em>hf value</em> = (Red + Green/255)*0.996093</li>
 +
</ol>
  
</ul>
+
<table class="centered" width="570px" cellpadding="0" cellspacing="10">
 
+
<tr>
</li>
+
  <td>
 
+
    [[Image:TutImgIso_23.png|center|220px<!--leftpanel--->]]
<li>
+
  </td>
F( ).<code>y</code> | F( ).<code>v</code> | F( ).<code>green</code>  
+
  <td>
<ul>
+
    <p class="tabletext">There are quite a lot of things possible with pigment functions. However, it should be noted that, some functions can cause longer render times:</p>
 
+
    <p class="tabletext"><code>function {<br>&nbsp;&nbsp;f_sphere(x,&nbsp;y,&nbsp;z,&nbsp;1.6)&nbsp;-<br>&nbsp;&nbsp;&nbsp;&nbsp;fn_Pigm(x/2,&nbsp;y/2,&nbsp;z/2).gray*0.5<br>&nbsp;&nbsp;&nbsp;&nbsp;}</code></p>
<li>
+
  </td>
to get the green value of the color vector
+
</tr>
</li>
+
<tr>
 
+
  <td>
</ul>
+
    <p class="caption">noise using a pigment function</p>
 
+
  </td>
</li>
+
  <td></td>
 
+
</tr>
<li>
+
</table>
F( ).<code>z</code> | F( ).<code>blue</code>
 
<ul>
 
 
 
<li>
 
to get the blue value of the color vector
 
</li>
 
 
 
</ul>
 
 
 
</li>
 
 
 
<li>
 
F( ).<code>filter</code> | F( ).<code>t</code>
 
<ul>
 
 
 
<li>
 
to get the filter value of the color vector
 
</li>
 
 
 
</ul>
 
 
 
</li>
 
 
 
<li>
 
F( ).<code>transmit</code>
 
<ul>
 
 
 
<li>
 
to get the transmit value of the color vector
 
</li>
 
 
 
</ul>
 
 
 
</li>
 
 
 
<li>
 
F( ).<code>gray</code>
 
<ul>
 
 
 
<li>
 
to get the gray value of the color vector
 
</li>
 
 
 
<li>
 
gray value = Red*29.7% + Green*58.9% + Blue*11.4%
 
</li>
 
 
 
</ul>
 
 
 
</li>
 
 
 
<li>
 
F( ).<code>hf</code>
 
<ul>
 
 
 
<li>
 
to get the height_field value of the color vector
 
</li>
 
 
 
<li>
 
hf value = (Red + Green/255)*0.996093
 
</li>
 
 
 
<li>
 
the .hf operator is experimental and will generate a warning.
 
</li>
 
 
 
</ul>
 
 
 
</li>
 
 
 
</ul>
 
 
 
<p><code> function { f_sphere(x, y, z, 1.6)-fn_Pigm(x/2, y/2, z/2).gray*0.5 }</code></p>
 
 
 
<p>[[Image:TutImgIso_23.png|Isosurface sample (pigment function)]]</p>
 
 
 
<p>There are quite a lot of things possible with pigment functions, but you
 
probably have recognized that this renders quite slow.</p>
 
  
 
=====Conditional directives and loops=====
 
=====Conditional directives and loops=====
Line 491: Line 593:
  
 
=====Transformations on functions=====
 
=====Transformations on functions=====
<p>
+
<p>Transforming an isosurface object is done like transforming any POV-Ray object. Simply use the object modifiers, scale, translate, and rotate. However, when you want to transform functions within the <code>contained_by</code> object, you have to substitute parameters in the functions.</p>
Transforming an isosurface object is done like transforming any POV-Ray object. Simply use the object modifiers (scale, translate, and rotate).  
 
</p>
 
  
<p>
+
<p>The results <em>seem</em> inverted to what you would normally expect, here's why:</p>
However, when you want to transform functions within the contained_by object, you have to substitute parameters in
 
the functions.
 
</p>
 
  
<p>
+
<p>Remember the sphere function we created earlier in this tutorial: <code>Sphere(x,y,z)</code></p>
The results <em>seem</em> inverted to what you would normally expect. Here is an explanation: <br>Take a
+
<p>We know it sits at the origin because <code>x=0</code>. If we want to translate it 2 units to the right to <code>x=2</code> we need to write the second equation in the same form: <code>x-2=0</code>. Now that both equations equal zero, we can replace the parameter <code>x</code> with <code>x-2</code>, call our function as: <code>Sphere(x-2,y,z)</code> and it's translated two units to the right.</p>
Sphere(x,y,z). We know it sits at the origin because x=0. When we want it at x=2 (translating 2 units to the right) we  
 
need to write the second equation in the same form: x-2=0 <br>Now that both equations equal 0, we can replace  
 
parameter x with x-2 <br>So our Sphere(x-2, y,z) moves two units to the <em>right</em>.  
 
</p>
 
  
<p>
+
<p>Let's look at how to scale our test sphere by <code>0.5</code> in the <em>y direction</em>. Given the default value of <code>y=1</code> <em>one unit</em> we'd want <code>y=0.5</code>. To do this we need to have the equation in the same form as the first one, so we'll multiply both sides by two: <code>y*2 = 0.5*2</code> which gives <code>y*2=1</code>.</p>
Let's scale our Sphere 0.5 in the y direction. Default size is y=1 (one unit). We want y=0.5. <br>To get this  
+
<p>Now we can replace the <code>y</code> parameter in our sphere: <code>Sphere(x,y*2,z)</code>. This scales the <em>y-size</em> of the sphere by half.</p>
equation in the same form as the first one, we have to multiply both sides by two. y*2 = 0.5*2, which gives y*2=1 <br>Now  
 
we can replace the y parameter in our sphere: Sphere(x, y*2, z). This squishes the y-size of the sphere by half. <br>Well,
 
this is the general idea of substitutions.
 
</p>
 
  
<p>
+
<p>Here is an overview of some useful substitutions, we'll be using a pseudo-object designated as <code>P(x,y,z)</code> in the following examples:</p>
Here is an overview of some useful substitutions: <br>Using a declared object P(x,y,z)  
 
</p>
 
  
<p>
+
<p><strong>Scale:</strong></p>
<strong>Scale</strong><br>
+
<p>&nbsp;&nbsp;To scale <code>x</code> replace <code>x</code> with <code>x/scale</code>:<br>&nbsp;&nbsp;<code>P(x/2,y,z)</code></p>
scale x : replace &quot;<code>x</code>&quot; with &quot;<code>x/scale</code>&quot; (idem other parameters)  
 
</p>
 
  
<pre>scale x*2  gives    P(x/2,y,z)</pre>
+
<p><strong>Scale Infinitely:</strong></p>
 +
<p>&nbsp;&nbsp;To scale <code>y</code> infinitely replace <code>y</code> with <code>0</code>:<br>&nbsp;&nbsp;<code>P(x,0,z)</code></p>
  
<p>
+
<p><strong>Translate:</strong></p>
<strong>Scale Infinitely</strong><br>
+
<p>&nbsp;&nbsp;To translate <code>z</code> replace <code>z</code> with <code>z&nbsp;-&nbsp;translation</code>:<br>&nbsp;&nbsp;<code>P(x,y,z-3)</code></p>
scale x infinitely : replace &quot;<code>x</code>&quot; with &quot;<code>0</code>&quot; (idem other parameters)  
 
</p>
 
  
<pre>scale y infinitely  gives    P(x,0,z)</pre>
+
<p><strong>Shear:</strong></p>
 +
<p>&nbsp;&nbsp;To shear in <em>xy-plane</em> replace <code>x</code> with <code>x + y*tan(radians(Angle))</code>:<br>&nbsp;&nbsp;<code>P(x+y*tan(radians(Angle)),y,z)</code></p>
  
<p>
+
<p><strong>Rotate:</strong></p>
<strong>Translate</strong><br>
+
<p class="Note"><strong>Note:</strong> These rotation substitutions work like normal POV-rotations, they already compensate for the inverse behavior.</p>
translate x : replace &quot;<code>x</code>&quot; with &quot;<code>x - translation</code>&quot; (idem other
 
parameters)
 
</p>
 
  
<pre>translate z*3  gives    P(x,y,z-3)</pre>
+
<p>To rotate around the X-axis:</p>
 +
<p>&nbsp;&nbsp;replace <code>y</code> with <code>z*sin(radians(Angle)) + y*cos(radians(Angle))</code></p>
 +
<p>&nbsp;&nbsp;replace <code>z</code> with <code>z*cos(radians(Angle)) - y*sin(radians(Angle))</code></p>
  
<p>
+
<p>To rotate around the Y-axis:</p>
<strong>Shear</strong><br>
+
<p>&nbsp;&nbsp;replace <code>x</code> with <code>x*cos(radians(Angle)) - z*sin(radians(Angle))</code></p>
shear in XY-plane : replace &quot;<code>x</code>&quot; with &quot;<code>x + y*tan(radians(Angle))</code>&quot;
+
<p>&nbsp;&nbsp;replace <code>z</code> with <code>x*sin(radians(Angle)) + z*cos(radians(Angle))</code></p>
(idem other parameters)
 
</p>
 
  
<pre>shear 45 degrees left  gives    P(x+y*tan(radians(45)), y, z)</pre>
+
<p>To rotate around the Z-axis:</p>
 +
<p>&nbsp;&nbsp;replace <code>x</code> with <code>x*cos(radians(Angle)) + y*sin(radians(Angle))</code></p>
 +
<p>&nbsp;&nbsp;replace <code>y</code> with <code>-x*sin(radians(Angle)) + y*cos(radians(Angle)) </code></p>
  
<p><strong>Rotate</strong></p>
+
<p><strong>Flip:</strong></p>
<p class="Note">
+
<p>To flip X - Y:</p>
<strong>Note:</strong> These rotation substitutions work like normal POV-rotations: they already compensate for the inverse working
+
<p>&nbsp;&nbsp;replace <code>x</code> with <code>y</code> <em>AND</em> replace <code>y</code> with <code>-x</code></p>
</p>
 
 
 
<p>
 
rotate around X <br>: replace &quot;<code>y</code>&quot; with &quot;<code>z*sin(radians(Angle)) +
 
y*cos(radians(Angle))</code>&quot; <br>: replace &quot;<code>z</code>&quot; with &quot;<code>z*cos(radians(Angle)) -  
 
y*sin(radians(Angle))</code>&quot;
 
</p>
 
 
 
<p>
 
rotate around Y <br>: replace &quot;<code>x</code>&quot; with &quot;<code>x*cos(radians(Angle)) -
 
z*sin(radians(Angle))</code>&quot; <br>: replace &quot;<code>z</code>&quot; with &quot;<code>x*sin(radians(Angle)) +
 
z*cos(radians(Angle))</code>&quot;
 
</p>
 
 
 
<p>
 
rotate around Z <br>: replace &quot;<code>x</code>&quot; with &quot;<code>x*cos(radians(Angle)) +
 
y*sin(radians(Angle))</code>&quot; <br>: replace &quot;<code>y</code>&quot; with &quot;<code>-x*sin(radians(Angle)) +
 
y*cos(radians(Angle)) </code>&quot;
 
</p>
 
 
 
<pre>
 
  rotate z*75  gives:
 
  P(x*cos(radians(75)) + y*sin(radians(75)),
 
    -x*sin(radians(75)) + y*cos(radians(75)), z)
 
</pre>
 
 
 
<p>
 
<strong>Flip</strong><br>
 
flip X - Y : replace &quot;<code>x</code>&quot; with &quot;<code>y</code>&quot; and replace &quot;<code>y</code>&quot;
 
with &quot;<code>-x</code>&quot;
 
</p>
 
  
<p>
+
<p>To flip Y - Z:</p>
flip Y - Z : replace &quot;<code>y</code>&quot; with &quot;<code>z</code>&quot; and replace &quot;<code>z</code>&quot;
+
<p>&nbsp;&nbsp;replace <code>y</code> with <code>z</code> <em>AND</em> replace <code>z</code> with <code>-y</code></p>
with &quot;<code>-y</code>&quot;
 
</p>
 
 
 
<p>
 
flip X - Z : replace &quot;<code>x</code>&quot; with &quot;<code>-z</code>&quot; and replace &quot;<code>z</code>&quot;
 
with &quot;<code>x</code>&quot;
 
</p>
 
  
<pre>flip x and y  gives    P(y, -x, z)</pre>
+
<p>To flip X - Z:</p>
 +
<p>&nbsp;&nbsp;replace <code>x</code> with <code>-z</code> <em>AND</em> replace <code>z</code> with <code>x</code></p>
  
<p>
+
<p><strong>Twist:</strong></p>
<strong>Twist</strong><br> twist N turns/unit around <code>X</code> <br>: replace &quot;<code>y</code>&quot; with &quot;<code>z*sin(x*2*pi*N)  
+
<p>To twist N turns/unit around the <code>x</code> axis:</p>
+ y*cos(x*2*pi*N)</code>&quot; <br>: replace &quot;<code>z</code>&quot; with &quot;<code>z*cos(x*2*pi*N) -  
+
<p>&nbsp;&nbsp;replace <code>y</code> with <code>z*sin(x*2*pi*N) + y*cos(x*2*pi*N)</code></p>
y*sin(x*2*pi*N)</code>&quot;
+
<p>&nbsp;&nbsp;replace <code>z</code> with <code>z*cos(x*2*pi*N) - y*sin(x*2*pi*N)</code></p>
</p>
 
  
 
=====Improving Isosurface Speed=====
 
=====Improving Isosurface Speed=====
<p>To optimize the approximation of the isosurface and to get maximum
+
<p>To optimize the approximation of the isosurface and to get maximum rendering speed it is important to adapt certain values:</p>
rendering speed it is important to adapt certain values;</p>
 
  
<p><code>accuracy</code></p>
+
<p><strong><code>accuracy</code>:</strong></p>
  
<p>The accuracy value influences how accurate the surface geometry is calculated.
+
<p>The accuracy value influences how accurate the surface geometry is calculated. Lower values lead to a more precise, but slower result. The default value of <code>0.001</code> is fairly low. We used this value in all the previous samples, but often you can raise this quite a lot and thereby make things faster.</p>
Lower values lead to a more precise, but slower result.
 
The default value of <code>0.001</code> is fairly low. We used this value in all the
 
previous samples, but often you can raise this quite a
 
lot and thereby make things faster.</p>
 
  
<p><code>max_gradient</code></p>
+
<p><strong><code>max_gradient</code>:</strong></p>
  
<p>For finding the actual surface it is important for POV-Ray to know the
+
<p>For finding the actual surface it is important for POV-Ray to know the maximum gradient of the function, meaning how fast the function value changes. We can specify a value with the <code>max_gradient</code> keyword.  Lower max_gradient values lead to faster rendering, but if the specified value is below the actual maximum gradient of the function, there can be holes or other artefact's in the surface.</p>
maximum gradient of the function, meaning how fast the function value changes.
 
We can specify a value with the <code>max_gradient</code> keyword.  Lower
 
max_gradient values lead to faster rendering, but if the specified value is
 
below the actual maximum gradient of the function, there can be holes or
 
other artefacts in the surface.</p>
 
  
<p>For the same reason functions with infinite gradient should not be used.
+
<p>For the same reason functions with an infinite gradient should not be used. This applies for pigment functions with brick or checker patterns for example. You should also be careful when using <code>select()</code> in isosurface functions because of this.</p>
This applies for pigment functions with brick or checker pattern for example.
 
You should also be careful when using <code>select()</code> in isosurface
 
functions because of this.</p>
 
  
<p>If the real maximum gradient differs too much from the specified value
+
<p>If the real maximum gradient differs too much from the specified value POV-Ray issues a warning together with the found maximum gradient. It is usually sufficient to use this number for the <code>max_gradient</code> parameter to get fast and correct results.</p>
POV-Ray prints a warning together with the found maximum gradient.
 
It is usually sufficient to use this number for the <code>max_gradient</code>
 
parameter to get fast and correct results.</p>
 
  
<p>POV-Ray can also dynamically change the <code>max_gradient</code> when you
+
<p>POV-Ray can also dynamically change the <code>max_gradient</code> when you specify <code>evaluate</code> with 3 parameters in the isosurface definition. Concerning the details on this and other things see the <!--<linkto "evaluate">evaluate</linkto>--->[[Reference:Isosurface|evaluate]] keyword in the reference section.</p>
specify <code>evaluate</code> with 3 parameters the isosurface definition.
 
Concerning the details on this and other things see the
 
<!--<linkto "evaluate">evaluate</linkto>--->[[Documentation:Reference Section 4.2#Isosurface Object|evaluate]] in the reference section.</p>
 
  
<p><code>contained_by</code></p>
+
<p><strong><code>contained_by</code>:</strong></p>
  
<p>
+
<p>Make sure your <code>contained_by</code> object fits as tightly as possible. An oversized container can sky-rocket the render time. When the container has a lot of empty space around the actual isosurface, POV-Ray has to do a lot of superfluous sampling: especially with complex functions this can become very time consuming. On top of this, the <code>max_gradient</code> needed to get a proper surface will also increase rapidly, almost proportional to the oversize! You could use a transparent copy of the container (using exactly the same transformations) to check how it fits. Getting the <code>min_extent</code> and <code>max_extent</code> of the isosurface is not useful because it only gives the extent of the container and not of the actual isosurface.</p>
Make sure your <code>contained_by</code> 'object' fits as tightly as possible. An oversized container can  
 
sky-rocket the render time. <br>When the container has a lot of empty space around the actual isosurface, POV-Ray has  
 
to do a lot of superfluous sampling: especially with complex functions this can become very time consuming. On top of  
 
this, the <code>max_gradient</code> needed to get a proper surface will also increase rapidly (almost proportional to  
 
the oversize!). <br>You could use a transparent copy of the container (using exactly the same transformations) to  
 
check how it fits. Getting the <code>min_extent</code> and <code>max_extent</code> of the <code>isosurface</code>
 
is not useful because it only gives the extent of the container and not of the actual isosurface.  
 
</p>
 
  
 
====Poly Object====
 
====Poly Object====
<!--<indexentry "poly, tutorial">--->
+
{{#indexentry:poly, tutorial}}
<p>The polynomial object (and its <em>shortcut</em> versions: <!--<linkto "cubic">cubic</linkto>---><code>[[Documentation:Reference Section 4.2#Poly, Cubic and Quartic|cubic]]</code>, <!--<linkto "quartic">quartic</linkto>---><code>[[Documentation:Reference Section 4.2#Poly, Cubic and Quartic|quartic]]</code> and <!--<linkto "quadric">quadric</linkto>---><code>[[Documentation:Reference Section 4.2#Quadric|quadric]]</code>)
+
<p>The polynomial object (and its <em>shortcut</em> versions: <!--<linkto "cubic">cubic</linkto>---><code>[[Reference:Cubic|cubic]]</code>, <!--<linkto "quartic">quartic</linkto>---><code>[[Reference:Quartic|quartic]]</code> and <!--<linkto "quadric">quadric</linkto>---><code>[[Reference:Quadric|quadric]]</code>)
of POV-Ray is one of the most complex and
+
of POV-Ray is one of the most complex and mathematical primitives of the program. One could think that it is seldom
mathematical primitives of the program. One could think that it is seldom
+
used and more or less obsolete, but we have to remember that for example the torus primitive is just a shortcut for the equivalent <code>quartic</code>, which is just a shortcut for the equivalent <code>poly</code> object. Polys are, however, seldom used in scenes due to the fact that they are so difficult to define and it is far from trivial to get the desired shape with just a polynomial equation. It is mostly used by the most mathematically oriented POV-Ray users.</p>
used and more or less obsolete, but we have to remember that for example
 
the torus primitive is just a shortcut for the equivalent <code>quartic</code>, which
 
is just a shortcut for the equivalent <code>poly</code> object. Polys are, however, seldom
 
used in scenes due to the fact that they are so difficult to define and
 
it is far from trivial to get the desired shape with just a polynomial
 
equation. It is mostly used by the most mathematically oriented POV-Ray
 
users.</p>
 
  
 
<p>This tutorial explains the process of making a polynomial object
 
<p>This tutorial explains the process of making a polynomial object
Line 667: Line 679:
 
<p class="Note"><strong>Note:</strong> Since version 3.5, POV-Ray includes the new <code>isosurface</code> object
 
<p class="Note"><strong>Note:</strong> Since version 3.5, POV-Ray includes the new <code>isosurface</code> object
 
which makes the polynomial object more or less obsolete. The isosurface is more versatile (you can specify any mathematical function, not
 
which makes the polynomial object more or less obsolete. The isosurface is more versatile (you can specify any mathematical function, not
just polynomials), easier to use. You can write the function as is, without needing to put values in a gigantic vector. Isosurfaces often render considerably faster than equivalent polys.</p>
+
just polynomials) and easier to use. You can write the function as is, without needing to put values in a gigantic vector. Isosurfaces also often (although not always) render considerably faster than equivalent polys.</p>
  
 
<p>However, the most mathematically oriented still like polys because
 
<p>However, the most mathematically oriented still like polys because
Line 674: Line 686:
 
are more than good enough for most applications, though.</p>
 
are more than good enough for most applications, though.</p>
  
<p class="Note"><strong>Note:</strong> At it's maximum a 15th degree polynomial can be represented with the poly object. If a higher degree polynomial or other non-polynomial function has to be represented, then it is necessary to use the isosurface object.</p>
+
<p class="Note"><strong>Note:</strong> A maximum of 35th degree polynomial can be represented with the poly object. If a higher degree polynomial or other non-polynomial function has to be represented, then it is necessary to use the isosurface object.</p>
  
 
=====Creating the polynomial function=====
 
=====Creating the polynomial function=====
Line 680: Line 692:
 
You will need some (high-school level) mathematical knowledge for this.</p>
 
You will need some (high-school level) mathematical knowledge for this.</p>
  
<p><strong>1)</strong> Let's start with an easy example: A sphere.</p>
+
<p><strong>1)</strong> Let's start with an easy example, a sphere:</p>
  
 
<p>The sphere function is:</p>
 
<p>The sphere function is:</p>
  
<p>[[Image:TutImgPolyfunc1.png|sphere function]]</p>
+
<table class="centered" width="190px" cellpadding="0" cellspacing="10">
 +
<tr>
 +
  <td>
 +
    [[Image:TutImgPolyfunc1.png|center|170px<!--centered--->]]
 +
  </td>
 +
</tr>
 +
<tr>
 +
  <td>
 +
    <p class="caption">sphere function</p>
 +
  </td>
 +
</tr>
 +
</table>
  
<p>Now we have to convert this to polynomial form:</p>
+
<p>Now we have to convert this to polynomial form, we will need a polynomial of the 2nd degree to represent this:</p>
  
<p>[[Image:TutImgPolyfunc2.png|sphere polynomial]]</p>
+
<table class="centered" width="205px" cellpadding="0" cellspacing="10">
 
+
<tr>
<p>We will need a polynomial of the 2nd degree to represent this.</p>
+
  <td>
 +
    [[Image:TutImgPolyfunc2.png|center|185px<!--centered--->]]
 +
  </td>
 +
</tr>
 +
<tr>
 +
  <td>
 +
    <p class="caption">sphere polynomial</p>
 +
  </td>
 +
</tr>
 +
</table>
  
 
<p><strong>2)</strong> A more elaborated example:</p>
 
<p><strong>2)</strong> A more elaborated example:</p>
Line 696: Line 728:
 
<p>Let's take the function:</p>
 
<p>Let's take the function:</p>
  
<p>[[Image:TutImgPolyfunc3.png|function]]</p>
+
<table class="centered" width="130px" cellpadding="0" cellspacing="10">
 +
<tr>
 +
  <td>
 +
    [[Image:TutImgPolyfunc3.png|center|110px<!--centered--->]]
 +
  </td>
 +
</tr>
 +
<tr>
 +
  <td>
 +
    <p class="caption">function</p>
 +
  </td>
 +
</tr>
 +
</table>
  
 
<p>Converting this to polynomial form we get:</p>
 
<p>Converting this to polynomial form we get:</p>
  
<p>[[Image:TutImgPolyfunc4.png|polynomial]]</p>
+
<table class="centered" width="215px" cellpadding="0" cellspacing="10">
 +
<tr>
 +
  <td>
 +
    [[Image:TutImgPolyfunc4.png|center|195px<!--centered--->]]
 +
  </td>
 +
</tr>
 +
<tr>
 +
  <td>
 +
    <p class="caption">polynomial</p>
 +
  </td>
 +
</tr>
 +
</table>
  
 
<p>Although the highest power is 4 we will need a 5th order polynomial to
 
<p>Although the highest power is 4 we will need a 5th order polynomial to
Line 707: Line 761:
  
 
<p><strong>3)</strong> And since we talked about the torus, let's also take it as an example.</p>
 
<p><strong>3)</strong> And since we talked about the torus, let's also take it as an example.</p>
 +
<p>A torus can be represented with the function:</p>
  
<p>A torus can be represented with the function:</p>
+
<table class="centered" width="295px" cellpadding="0" cellspacing="10">
 +
<tr>
 +
  <td>
 +
    [[Image:TutImgPolyfunc5.png|center|275px<!--centered--->]]
 +
  </td>
 +
</tr>
 +
<tr>
 +
  <td>
 +
    <p class="caption">torus function</p>
 +
  </td>
 +
</tr>
 +
</table>
  
<p>[[Image:TutImgPolyfunc5.png|torus function]]</p><p>where r<sub>1</sub> is the major radius and r<sub>2</sub> is the minor radius.</p>
+
<p>where r<sub>1</sub> is the major radius and r<sub>2</sub> is the minor radius.</p>
  
 
<p>Now, this is tougher to convert to polynomial form, but finally we get:</p>
 
<p>Now, this is tougher to convert to polynomial form, but finally we get:</p>
  
<p>[[Image:TutImgPolyfunc6.png|torus polynomial]]</p>
+
<table class="centered" width="700px" cellpadding="0" cellspacing="10">
 +
<tr>
 +
  <td>
 +
    [[Image:TutImgPolyfunc6.png|center|680px<!--centered--->]]
 +
  </td>
 +
</tr>
 +
<tr>
 +
  <td>
 +
    <p class="caption">torus polynomial</p>
 +
  </td>
 +
</tr>
 +
</table>
  
 
<p>A 4th order polynomial is enough to represent this.</p>
 
<p>A 4th order polynomial is enough to represent this.</p>
Line 725: Line 802:
 
=====Writing the polynomial vector=====
 
=====Writing the polynomial vector=====
 
<p>Now that we have the function in polynomial form, we have to write it
 
<p>Now that we have the function in polynomial form, we have to write it
in POV-Ray syntax. The syntax is specified in the in the chapters <!--<linkto "Poly, Cubic and Quartic">Poly, Cubic and Quartic</linkto>--->[[Documentation:Reference Section 4.2#Poly, Cubic and Quartic|Poly, Cubic and Quartic]] and <!--<linkto "Quadric">Quadric</linkto>--->[[Documentation:Reference Section 4.2#Quadric|Quadric]] of the SDL section. There is also a  
+
in POV-Ray syntax. The syntax is specified in the sections on [[Reference:Polynomial|polynomial]] and [[Reference:Quadric|quadric]] of the reference section. There is also a table in this chapter which we will be using to make the polynomial vector. It is easier to have this table printed on paper.</p>
table in this chapter which we will be using to make the polynomial vector. It is  
 
easier to have this table printed on paper.</p>
 
  
 
<p class="Note"><strong>Note:</strong> It is also possible to make a little program with your favorite
 
<p class="Note"><strong>Note:</strong> It is also possible to make a little program with your favorite
Line 736: Line 811:
  
 
<p>Since the sphere can be represented with a polynomial of 2nd degree, we
 
<p>Since the sphere can be represented with a polynomial of 2nd degree, we
look at the row titled <em>2nd</em> in the table. We see that it has 10 items,
+
look at the column titled <em>2nd</em> in the [[Reference:Polynomial|table]]. We see that it has 10 items,
ie. we need a vector of size 10. Each item of the vector will be the factor
+
ie. we need a vector of size 10. Each item of the vector will be the factor of the term listed in the table.</p>
of the term listed in the table.</p>
 
  
 
<p>The polynomial was:</p>
 
<p>The polynomial was:</p>
  
<table class="matte" width="205px" cellpadding="0" cellspacing="10">
+
<table class="centered" width="205px" cellpadding="0" cellspacing="10">
 
<tr>
 
<tr>
 
   <td>
 
   <td>
Line 790: Line 864:
 
</pre>
 
</pre>
  
<table class="matte" width="340px" cellpadding="0" cellspacing="10">
+
<table class="centered" width="340px" cellpadding="0" cellspacing="10">
 
<tr>
 
<tr>
 
   <td>
 
   <td>
Line 804: Line 878:
  
 
<p></p>
 
<p></p>
<p class="Note"><strong>Note:</strong> There is a shortcut for 2nd degree polynomials: The  
+
<p class="Note"><strong>Note:</strong> There is a shortcut for 2nd degree polynomials: The <code>[[Reference:Quadric|quadric]]</code> primitive. Using a shortcut version, whenever possible, can lead to faster
<code><!--<linkto "quadric">quadric</linkto>--->[[Documentation:Reference Section 4.2#Quadric|quadric]]</code>
+
renderings. We can write the sphere code described above in the following way:</p>
primitive. Using a shortcut version, whenever possible, can lead to faster
 
renderings. We can write the sphere code described above in the following
 
way:</p>
 
  
 
<pre>
 
<pre>
Line 818: Line 889:
  
 
<p><strong>2)</strong> Now lets try the second one. We do it similarly, but this time we need
 
<p><strong>2)</strong> Now lets try the second one. We do it similarly, but this time we need
to look at the row titled <em>5th</em> in the table.</p>
+
to look at the column titled <em>5th</em> in the table.</p>
  
 
<p>The polynomial was:</p>
 
<p>The polynomial was:</p>
  
<table class="matte" width="215px" cellpadding="0" cellspacing="10">
+
<table class="centered" width="215px" cellpadding="0" cellspacing="10">
 
<tr>
 
<tr>
 
   <td>
 
   <td>
Line 879: Line 950:
 
</pre>
 
</pre>
  
<table class="matte" width="340px" cellpadding="0" cellspacing="10">
+
<table class="centered" width="340px" cellpadding="0" cellspacing="10">
 
<tr>
 
<tr>
 
   <td>
 
   <td>
Line 896: Line 967:
 
<p>The polynomial was:</p>
 
<p>The polynomial was:</p>
  
<table class="matte" width="700px" cellpadding="0" cellspacing="10">
+
<table class="centered" width="700px" cellpadding="0" cellspacing="10">
 
<tr>
 
<tr>
 
   <td>
 
   <td>
Line 932: Line 1,003:
 
<p>When rendered we get:</p>
 
<p>When rendered we get:</p>
  
<table class="matte" width="340px" cellpadding="0" cellspacing="10">
+
<table class="centered" width="340px" cellpadding="0" cellspacing="10">
 
<tr>
 
<tr>
 
   <td>
 
   <td>
Line 946: Line 1,017:
  
  
<p>There is a shortcut for 4th order polynomials: The  
+
<p>There is a shortcut for 4th order polynomials: The <code>[[Reference:Quartic|quartic]]</code> primitive. We can write the torus like this:</p>
<code><!--<linkto "quartic">quartic</linkto>--->[[Documentation:Reference Section 4.2#Poly, Cubic and Quartic|quartic]]</code> primitive.
 
We can write the torus like this:</p>
 
  
 
<pre>
 
<pre>
Line 964: Line 1,033:
  
 
=====Polynomial made easy=====
 
=====Polynomial made easy=====
<p>Since consulting the table in the section <!--<linkto "Poly, Cubic and Quartic">Poly, Cubic and Quartic</linkto>--->[[Documentation:Reference Section 4.2#Poly, Cubic and Quartic|Poly, Cubic and Quartic]] or writing a program to get the right poly vector can be a bit cumbersome, especially when the poly vector is not a write-once-only expression and that you want to get it back, so let's examine how those equations would be rewritten using the <em>simplified</em> syntax.</p>
+
<p>Since consulting the table in the section [[Reference:Polynomial|Polynomial]] or writing a program to get the right poly vector can be a bit cumbersome, especially when the poly vector is not a write-once-only expression and that you want to get it back, so let's examine how those equations would be rewritten using the <em>simplified</em> syntax.</p>
 
<p>You should refer to the images in the previous section, as these examples produce <em>exactly</em> the same results.</p>
 
<p>You should refer to the images in the previous section, as these examples produce <em>exactly</em> the same results.</p>
  
Line 1,006: Line 1,075:
 
<table width=100% border=0 cellspacing=0 cellpadding=5>
 
<table width=100% border=0 cellspacing=0 cellpadding=5>
 
<tr><td width=50% bgcolor=#EEEEEF>
 
<tr><td width=50% bgcolor=#EEEEEF>
[[Documentation:Tutorial Section 3.1#Isosurface Object|Isosurface Object]]</td>
+
[[Documentation:Tutorial Section 3.1#Height Field Object|Height Field Object]]</td>
 
<td width=50% bgcolor=#EEEEEF align=right>
 
<td width=50% bgcolor=#EEEEEF align=right>
 
[[Documentation:Tutorial Section 3.3#Superquadric Ellipsoid Object|Superquadric Ellipsoid Object]]</td></tr>
 
[[Documentation:Tutorial Section 3.3#Superquadric Ellipsoid Object|Superquadric Ellipsoid Object]]</td></tr>

Latest revision as of 14:20, 16 December 2016

This document is protected, so submissions, corrections and discussions should be held on this documents talk page.


Isosurface Object

Isosurfaces are shapes described by mathematical functions.

In contrast to the other mathematically based shapes in POV-Ray, isosurfaces are approximated during rendering and therefore they are sometimes more difficult to handle. However, they offer many interesting possibilities, like real deformations and surface displacements

Some knowledge about mathematical functions and geometry is useful, but not necessarily required to work with isosurfaces.

Simple functions

Let's begin with something simple. In this first series of images, let's explore the user defined function shown as function { x } that we see in the code example below. It produces the first image on the left, a simple box. The container, which is a requirement for the isosurface object, is represented by the box object and the contained_by keyword in the isosurface definition.

  isosurface {
    function { x }
    contained_by { box { -2, 2 } }
  }

You should have also noticed that in the image on the left, only half the box was produced, that's because the threshold keyword was omitted, so the default value 0 was used to evaluate the x-coordinate.

In this next code example threshold 1 was added to produce the center image.

  isosurface {
    function { x }
    threshold 1
    contained_by { box { -2, 2 } }
  }

It is also possible to remove the visible surfaces of the container by adding the open keyword to the isosurface definition.

For the final image on the right, the following code example was used. Notice that the omission of the threshold keyword causes the x-coordinate to be again evaluated to zero.

  isosurface {
    function { x }
    open
    contained_by { box { -2, 2 } }
  }
TutImgIso 01.png
TutImgIso 02.png
TutImgIso 03.png

function { x }

function { x } with threshold 1

function { x } with open

Hint: The checkered ground plane is scaled to one unit squares.

For the last series of images in this section, let's try something different. These next two code examples were used to show the results of changing the user defined function to function { x+y } and function { x+y+z } respectively. They describe planes going through the origin, the function just describes the normal vector of the plane.

  isosurface {
    function { x+y }
    max_gradient 4
    contained_by { box { -2, 2 } }
  }

Note: To properly render these examples max_gradient 4 was added to the isosurface definition, and will be explained later.

  isosurface {
    function { x+y+z }
    max_gradient 4
    contained_by { box { -2, 2 } }
  }
TutImgIso 04.png
TutImgIso 05.png

plane function { x+y }

plane function { x+y+z }

Note: When appropriate, to better visualize the difference between the isosurface and the container object, the images in this tutorial have been color coded.

Several surfaces

Now that you're starting to become familiar with isosurface syntax, there really isn't any need to show a code example for each and every image. You can always look back at the earlier examples when needed. The image captions will most often contain additional keyword hints when appropriate.

Note: The user defined function portion will always use this color coded format: function { x+y+z }

For the first image on the left, these two functions lead to identical results: function { abs(x)-1 } and function { sqrt(x*x)-1 } because both of these formulas have the same solution where the function value is 0, specifically x=-1 and x=1 in this example.

You can easily mix any of these elements in different combinations, but the results always produce planar surfaces. The last two images in this series used function { abs(x)-1+y } and function { abs(x)+abs(y)+abs(z)-2 } respectively.

TutImgIso 06.png
TutImgIso 07.png
TutImgIso 08.png

identical results with open

linear functions x & y axis

linear functions x, y & z axis

Non-linear functions
TutImgIso 09.png

Curved surfaces of many different kinds can be achieved with non-linear functions. A square function creates the parabolic shape:
function { pow(x,2)+y }

a parabolic shape

If you describe a circle in 2 dimensions with a constant in the 3rd dimension you get a cylinder:
function { sqrt(pow(x,2)+pow(z,2))-1 }

TutImgIso 10.png

the cylinder shape

TutImgIso 11.png

It's easy to change a cylinder into a cone, we just need to add a linear component in y-direction:
function { sqrt(pow(x,2)+pow(z,2))+y }

the cone shape

No worries, creating a sphere is easy too. In this example 2 specifies the radius:
function { sqrt(pow(x,2)+pow(y,2)+pow(z,2))-2 }

TutImgIso 12.png

the sphere shape

Specifying functions

Until now, we have seen, the functions used to define the isosurface were literally written in the function {...} block:

#declare Threshold = 1;

isosurface {
function {pow(x,2) + pow(y,2) + pow(z,2)}
  threshold Threshold
  ...
}

Let's expand on that concept, and add some flexibility. Remember that user defined functions (like equations), all float expressions and operators which are legal in POV-Ray can be used, and that functions should be declared first, and then used in the isosurface. See the section user defined function for more information.

This next example takes the above equation, and rewrites it as a user defined function. By default a function that takes three parameters (x,y,z) does not require you to explicitly specify the parameter names when declaring it, however when using the identifier, the parameters must be specified.

#declare Threshold = 1;

#declare Sphere = function {pow(x,2) + pow(y,2) + pow(z,2)};

isosurface {
  function { Sphere(x,y,z) }
  threshold Threshold
  ...
}

However, if you need more or less than three parameters when declaring a function, you will also have to explicitly specify the parameter names.

#declare Sphere = function (x,y,z,Radius) {pow(x,2) + pow(y,2) + pow(z,2) - pow(Radius,2)};

isosurface {
  function { Sphere(x,y,z,1) }
  ...
}
Internal functions

There are a lot of internal functions available in POV-Ray. For example a sphere could also be generated with function { f_sphere(x, y, z, 2) }, for these and other functions, see the functions.inc include file. Most of them are more complicated and it is usually faster to use them instead of a hand coded equivalent.

See the complete list for details.

The following makes a torus just like POV-Ray's torus object:

  #include "functions.inc"

  isosurface {
    function { f_torus(x, y, z, 1.6, 0.4) }
    contained_by { box { -2, 2 } }
  }
TutImgIso 13.png

The 4th and 5th parameters are the major and minor radius, just like the corresponding values in the torus{} object.

The parameters x, y and z are required, because it is a declared function. You can also declare functions yourself like it is explained in the reference section.

the torus function

Combining isosurface functions

We can also simulate some Constructive Solid Geometry with isosurface functions. If you do not know about CSG we suggest you have a look at What is CSG? or the corresponding part of the reference section first.

For this next group of images, consider the two functions for a cylinder and a rotated box:

  #declare fn_A = function { sqrt(pow(y,2) + pow(z,2)) - 0.8 }
  #declare fn_B = function { abs(x)+abs(y)-1 }
  1. If we combine them the following way, we get a merge:
    function { min(fn_A(x, y, z), fn_B(x, y, z)) }
  2. An intersection can be obtained by using max() instead of min():
    function { max(fn_A(x, y, z), fn_B(x, y, z)) }
  3. A difference is possible, by adding a minus (-) before the second function:
    function { max(fn_A(x, y, z), -fn_B(x, y, z)) }
TutImgIso 14.png
TutImgIso 15.png
TutImgIso 16.png

merge example

intersection example

difference example

Apart from basic CSG you can also obtain smooth transits between the different surfaces, for instance the blob object:

  #declare Blob_Threshold=0.01;

  isosurface {
    function {
      (1+Blob_Threshold)
      -pow(Blob_Threshold, fn_A(x,y,z))
      -pow(Blob_Threshold, fn_B(x,y,z))
    }
    max_gradient 4
    contained_by { box { -2, 2 } }
  }
TutImgIso 17.png

The Blob_Threshold value influences the smoothness of the transit between the shapes. A lower value leads to sharper edges, and it's function looks like:

function{fn_A(x,y,z) + pow(Blob_Threshold,(fn_B(x,y,z) + Strength))}

smooth transitions using blob

Noise and pigment functions

Some of the internal functions have a random or noise-like structure

Together with the pigment functions they are one of the most powerful tools for designing isosurfaces. We can add real surface displacement to the objects rather than only normal perturbation known from the normal statement.

The relevant internal functions are:

  • f_noise3d(x,y,z)
    uses the noise generator specified in global_settings and generates structures like the bozo pattern.
  • f_noise_generator(x, y, z, noise_generator)
    generates noise with a specified noise generator.
  • f_ridged_mf(x, y, z, H, Lacunarity, Octaves, Offset, Gain, noise_generator)
    generates a ridged multifractal pattern.
  • f_ridge(x, y, z, Lambda, Octaves, Omega, Offset, Ridge, noise_generator)
    generates another noise with ridges.
  • f_hetero_mf(x, y, z, H, Lacunarity, Octaves, Offset, T, noise_generator)
    generates heterogenic multifractal noise.

Using this simple noise3d function results in the image on the right. The value -0.5 matches the default threshold value of zero. The f_noise3d function returns values between 0 and 1:

function { f_noise3d(x,y,z)-0.5 }

TutImgIso 18.png

simple noise3d function

In these next two images the noise function was added to a plane function. The x-parameter was set to 0 so the noise function is constant in x-direction. This way we achieve the typical heightfield structure.

TutImgIso 19.png

With this and the other functions you can generate objects similar to heightfields, having the advantage that a high resolution can be achieved without high memory requirements:

function { x + f_noise3d(0,y,z) }

a noise3d heightfield

The noise function can of course also be subtracted which results in an inverted version:

function { x - f_noise3d(0,y,z) }

TutImgIso 20.png

a noise3d heightfield - inverted

TutImgIso 21.png

Of course we can also add noise to any other function. If the noise function is very strong this can result in several separated surfaces.

function { f_sphere(x,y,z,1.2) - f_noise3d(x,y,z) }

noise3d on a sphere

This is a noise function applied to a sphere surface, we can influence the intensity of the noise by multiplying it with a factor and change the scale by multiplying the coordinate parameters:

function {
  f_sphere(x,y,z,1.6) -
    f_noise3d(x*5,y*5,z* ) * 0.5
    }

TutImgIso 22.png

noise3d on a sphere - scaled

As alternative to noise functions we can also use any pigment in a function:

  #declare fn_Pigm=function {
    pigment {
      agate
      color_map {
        [0 color rgb 0]
        [1 color rgb 1]
      }
    }
  }

This is a vector function, it returns a color vector for use in isosurface functions. They must be pre-declared first. When using the identifier, you have to specify which component of the color vector should be used.

To do this, the dot notation is used. Refer to the above example: fn_Pigm(x,y,z).red

A color vector has five components, their supported dot types to access these components are:

  1. fn_Pigm( ).x | fn_Pigm( ).u | fn_Pigm( ).red
    to get the red value of the color vector
  2. fn_Pigm( ).y | fn_Pigm( ).v | fn_Pigm( ).green
    to get the green value of the color vector
  3. fn_Pigm( ).z | fn_Pigm( ).blue
    to get the blue value of the color vector
  4. fn_Pigm( ).filter | fn_Pigm( ).f
    to get the filter value of the color vector
  5. fn_Pigm( ).transmit | fn_Pigm( ).t
    to get the transmit value of the color vector

And two special purpose operators, their supported dot types to access these operators are:

Note: The .hf operator is experimental and will generate a warning.

  1. fn_Pigm( ).gray to get the gray value of the color vector
    gray value = Red*29.7% + Green*58.9% + Blue*11.4%
  2. fn_Pigm( ).hf to get the height_field value of the color vector
    hf value = (Red + Green/255)*0.996093
TutImgIso 23.png

There are quite a lot of things possible with pigment functions. However, it should be noted that, some functions can cause longer render times:

function {
  f_sphere(x, y, z, 1.6) -
    fn_Pigm(x/2, y/2, z/2).gray*0.5
    }

noise using a pigment function

Conditional directives and loops

Conditional directives are allowed in functions:

#declare Rough = yes;
#include "functions.inc"
isosurface {
  function { y #if(Rough=1)-f_noise3d(x/0.5,y/0.3,z/0.4)*0.8 #end }
  ...
}

Loops can also be used in functions:

#include "functions.inc"
#declare Thr = 1/1000;
#declare Ang = radians(45);
#declare Offset = 1.5;
#declare Scale = 1.2;
#declare TrSph = function { f_sphere(x-Offset,y,z,0.7*Scale) }

function {
  (1-Thr)
  #declare A = 0;
  #while (A<8)
  -pow(Thr, TrSph(x*cos(A*Ang) + y*sin(A*Ang),
                  y*cos(A*Ang) -x*sin(A*Ang), z) )
    #declare A=A+1;
  #end
}

Note: The loops and conditionals are evaluated at parse time, not at render time.

Transformations on functions

Transforming an isosurface object is done like transforming any POV-Ray object. Simply use the object modifiers, scale, translate, and rotate. However, when you want to transform functions within the contained_by object, you have to substitute parameters in the functions.

The results seem inverted to what you would normally expect, here's why:

Remember the sphere function we created earlier in this tutorial: Sphere(x,y,z)

We know it sits at the origin because x=0. If we want to translate it 2 units to the right to x=2 we need to write the second equation in the same form: x-2=0. Now that both equations equal zero, we can replace the parameter x with x-2, call our function as: Sphere(x-2,y,z) and it's translated two units to the right.

Let's look at how to scale our test sphere by 0.5 in the y direction. Given the default value of y=1 one unit we'd want y=0.5. To do this we need to have the equation in the same form as the first one, so we'll multiply both sides by two: y*2 = 0.5*2 which gives y*2=1.

Now we can replace the y parameter in our sphere: Sphere(x,y*2,z). This scales the y-size of the sphere by half.

Here is an overview of some useful substitutions, we'll be using a pseudo-object designated as P(x,y,z) in the following examples:

Scale:

  To scale x replace x with x/scale:
  P(x/2,y,z)

Scale Infinitely:

  To scale y infinitely replace y with 0:
  P(x,0,z)

Translate:

  To translate z replace z with z - translation:
  P(x,y,z-3)

Shear:

  To shear in xy-plane replace x with x + y*tan(radians(Angle)):
  P(x+y*tan(radians(Angle)),y,z)

Rotate:

Note: These rotation substitutions work like normal POV-rotations, they already compensate for the inverse behavior.

To rotate around the X-axis:

  replace y with z*sin(radians(Angle)) + y*cos(radians(Angle))

  replace z with z*cos(radians(Angle)) - y*sin(radians(Angle))

To rotate around the Y-axis:

  replace x with x*cos(radians(Angle)) - z*sin(radians(Angle))

  replace z with x*sin(radians(Angle)) + z*cos(radians(Angle))

To rotate around the Z-axis:

  replace x with x*cos(radians(Angle)) + y*sin(radians(Angle))

  replace y with -x*sin(radians(Angle)) + y*cos(radians(Angle))

Flip:

To flip X - Y:

  replace x with y AND replace y with -x

To flip Y - Z:

  replace y with z AND replace z with -y

To flip X - Z:

  replace x with -z AND replace z with x

Twist:

To twist N turns/unit around the x axis:

  replace y with z*sin(x*2*pi*N) + y*cos(x*2*pi*N)

  replace z with z*cos(x*2*pi*N) - y*sin(x*2*pi*N)

Improving Isosurface Speed

To optimize the approximation of the isosurface and to get maximum rendering speed it is important to adapt certain values:

accuracy:

The accuracy value influences how accurate the surface geometry is calculated. Lower values lead to a more precise, but slower result. The default value of 0.001 is fairly low. We used this value in all the previous samples, but often you can raise this quite a lot and thereby make things faster.

max_gradient:

For finding the actual surface it is important for POV-Ray to know the maximum gradient of the function, meaning how fast the function value changes. We can specify a value with the max_gradient keyword. Lower max_gradient values lead to faster rendering, but if the specified value is below the actual maximum gradient of the function, there can be holes or other artefact's in the surface.

For the same reason functions with an infinite gradient should not be used. This applies for pigment functions with brick or checker patterns for example. You should also be careful when using select() in isosurface functions because of this.

If the real maximum gradient differs too much from the specified value POV-Ray issues a warning together with the found maximum gradient. It is usually sufficient to use this number for the max_gradient parameter to get fast and correct results.

POV-Ray can also dynamically change the max_gradient when you specify evaluate with 3 parameters in the isosurface definition. Concerning the details on this and other things see the evaluate keyword in the reference section.

contained_by:

Make sure your contained_by object fits as tightly as possible. An oversized container can sky-rocket the render time. When the container has a lot of empty space around the actual isosurface, POV-Ray has to do a lot of superfluous sampling: especially with complex functions this can become very time consuming. On top of this, the max_gradient needed to get a proper surface will also increase rapidly, almost proportional to the oversize! You could use a transparent copy of the container (using exactly the same transformations) to check how it fits. Getting the min_extent and max_extent of the isosurface is not useful because it only gives the extent of the container and not of the actual isosurface.

Poly Object

The polynomial object (and its shortcut versions: cubic, quartic and quadric) of POV-Ray is one of the most complex and mathematical primitives of the program. One could think that it is seldom used and more or less obsolete, but we have to remember that for example the torus primitive is just a shortcut for the equivalent quartic, which is just a shortcut for the equivalent poly object. Polys are, however, seldom used in scenes due to the fact that they are so difficult to define and it is far from trivial to get the desired shape with just a polynomial equation. It is mostly used by the most mathematically oriented POV-Ray users.

This tutorial explains the process of making a polynomial object in POV-Ray.

Note: Since version 3.5, POV-Ray includes the new isosurface object which makes the polynomial object more or less obsolete. The isosurface is more versatile (you can specify any mathematical function, not just polynomials) and easier to use. You can write the function as is, without needing to put values in a gigantic vector. Isosurfaces also often (although not always) render considerably faster than equivalent polys.

However, the most mathematically oriented still like polys because isosurfaces are calculated just by approximating the right value, while the poly is calculated in a mathematically exact way. Usually isosurfaces are more than good enough for most applications, though.

Note: A maximum of 35th degree polynomial can be represented with the poly object. If a higher degree polynomial or other non-polynomial function has to be represented, then it is necessary to use the isosurface object.

Creating the polynomial function

The first step is to create the polynomial function to be represented. You will need some (high-school level) mathematical knowledge for this.

1) Let's start with an easy example, a sphere:

The sphere function is:

TutImgPolyfunc1.png

sphere function

Now we have to convert this to polynomial form, we will need a polynomial of the 2nd degree to represent this:

TutImgPolyfunc2.png

sphere polynomial

2) A more elaborated example:

Let's take the function:

TutImgPolyfunc3.png

function

Converting this to polynomial form we get:

TutImgPolyfunc4.png

polynomial

Although the highest power is 4 we will need a 5th order polynomial to represent this function (because we cannot represent y4z with a 4th order polynomial).

3) And since we talked about the torus, let's also take it as an example.

A torus can be represented with the function:

TutImgPolyfunc5.png

torus function

where r1 is the major radius and r2 is the minor radius.

Now, this is tougher to convert to polynomial form, but finally we get:

TutImgPolyfunc6.png

torus polynomial

A 4th order polynomial is enough to represent this.

Note: Not every function can be represented in polynomial form. Only functions that use addition (and substraction), multiplication (and division) and scalar powers (including rational powers, eg. the square root) can be represented. Also, the poly primitive supports only polynomials of the 35th degree at max.

Converting a function to polynomial form may be a very laborious task for certain functions. Some mathematical programs are very helpful in this matter.

Writing the polynomial vector

Now that we have the function in polynomial form, we have to write it in POV-Ray syntax. The syntax is specified in the sections on polynomial and quadric of the reference section. There is also a table in this chapter which we will be using to make the polynomial vector. It is easier to have this table printed on paper.

Note: It is also possible to make a little program with your favorite programming language which will print the poly vector from the polynomial function, but making a program like this is up to you.

1) Let's start with the easy one, ie. the sphere.

Since the sphere can be represented with a polynomial of 2nd degree, we look at the column titled 2nd in the table. We see that it has 10 items, ie. we need a vector of size 10. Each item of the vector will be the factor of the term listed in the table.

The polynomial was:

TutImgPolyfunc2.png

sphere polynomial function

Writing the poly in this way we get:

#declare Radius=1;
poly
{ 2,
  <1,0,0,0,1,
   0,0,1,0,-Radius*Radius>
}

Put each group of factors (separated with lines in the table) in their own lines.

In the table we see that the first item is the factor for x2, which is 1 in the function. The next item is xy. Since it is not in the function, its factor is 0. Likewise the next item, which is xz. And so on. The last item is the scalar term, which is in this case -r2.

If we make a proper scene and render it, we get:

camera { location y*4-z*5 look_at 0 angle 35 }
light_source { <100,200,-50> 1 }
background { rgb <0,.25,.5> }

#declare Radius=1;
poly
{ 2,
  <1,0,0,0,1,
   0,0,1,0,-Radius*Radius>
  pigment { rgb <1,.7,.3> } finish { specular .5 }
}
TutImgPolypic1.png

sphere polynomial image

Note: There is a shortcut for 2nd degree polynomials: The quadric primitive. Using a shortcut version, whenever possible, can lead to faster renderings. We can write the sphere code described above in the following way:

quadric
{ <1,1,1>, <0,0,0>, <0,0,0>, -Radius*Radius
  pigment { rgb <1,.7,.3> } finish { specular .5 }
}

2) Now lets try the second one. We do it similarly, but this time we need to look at the column titled 5th in the table.

The polynomial was:

TutImgPolyfunc4.png

5th order polynomial function

Writing the poly primitive we get:

poly
{ 5,
  <0,0,0,0,0,
   0,0,0,0,0,
   0,0,0,0,0,
   0,0,0,1,0,
   0,0,0,0,0,
   -2,0,0,0,0,
   0,0,0,0,0,
   0,1,0,0,0,
   0,0,0,0,0,
   0,0,0,0,0,
   0,0,0,0,0,0>
}

With the proper scene we get:

camera { location <8,20,-10>*.7 look_at x*.01 angle 35 }
light_source { <100,200,20> 1 }
background { rgb <0,.25,.5> }

poly
{ 5,
  <0,0,0,0,0,
   0,0,0,0,0,
   0,0,0,0,0,
   0,0,0,1,0,
   0,0,0,0,0,
   -2,0,0,0,0,
   0,0,0,0,0,
   0,1,0,0,0,
   0,0,0,0,0,
   0,0,0,0,0,
   0,0,0,0,0,0>
  clipped_by { box { <-4,-4,-1><4,4,1> } }
  bounded_by { clipped_by }
  pigment { rgb <1,.7,.3> } finish { specular .5 }
  rotate <0,90,-90>
}
TutImgPolypic2.png

5th order polynomial image

3) And finally the torus:

The polynomial was:

TutImgPolyfunc6.png

torus polynomial function

And we get the proper 4th degree poly primitive:

camera { location y*4-z*5 look_at 0 angle 35 }
light_source { <100,200,-50> 1 }
background { rgb <0,.25,.5> }

#declare r1=1;
#declare r2=.5;
poly
{ 4,
  <1,0,0,0,2,
   0,0,2,0,-2*(r1*r1+r2*r2),
   0,0,0,0,0,
   0,0,0,0,0,
   1,0,0,2,0,
   2*(r1*r1-r2*r2),0,0,0,0,
   1,0,-2*(r1*r1+r2*r2),0,pow(r1,4)+pow(r2,4)-2*r1*r1*r2*r2>
  pigment { rgb <1,.7,.3> } finish { specular .5 }
}

When rendered we get:

TutImgPolypic3.png

torus polynomial image


There is a shortcut for 4th order polynomials: The quartic primitive. We can write the torus like this:

quartic
{ <1,0,0,0,2,
   0,0,2,0,-2*(r1*r1+r2*r2),
   0,0,0,0,0,
   0,0,0,0,0,
   1,0,0,2,0,
   2*(r1*r1-r2*r2),0,0,0,0,
   1,0,-2*(r1*r1+r2*r2),0,pow(r1,4)+pow(r2,4)-2*r1*r1*r2*r2>
  pigment { rgb <1,.7,.3> } finish { specular .5 }
}
Polynomial made easy

Since consulting the table in the section Polynomial or writing a program to get the right poly vector can be a bit cumbersome, especially when the poly vector is not a write-once-only expression and that you want to get it back, so let's examine how those equations would be rewritten using the simplified syntax.

You should refer to the images in the previous section, as these examples produce exactly the same results.

1) The sphere example can be rewritten as:

#declare Radius=1;
polynomial { 2, 
  xyz(2,0,0):1, 
  xyz(0,2,0):1,
  xyz(0,0,2):1,
  xyz(0,0,0):-Radius*Radius 
}

2) Let's now see the second one:

polynomial { 5,
  xyz(2,0,1):1,
  xyz(0,4,1):1,
  xyz(1,2,0):-2
}

3) And finally the torus example:

polynomial { 4,
  xyz(4,0,0):1,
  xyz(2,2,0):2,
  xyz(2,0,2):2,
  xyz(2,0,0):-2*(r1*r1+r2*r2),
  xyz(0,4,0):1,
  xyz(0,2,2):2,
  xyz(0,2,0):2*(r1*r1-r2*r2),
  xyz(0,0,4):1,
  xyz(0,0,2):-2*(r1*r1+r2*r2),
  xyz(0,0,0):pow((r1*r1-r2*r2),2)
}


Height Field Object Superquadric Ellipsoid Object


This document is protected, so submissions, corrections and discussions should be held on this documents talk page.