Now that we're familiar with using #declare, creating #include files, and building simple logic into our scripts with #if...#else...#end statements, and have had a very basic introduction to #switch...#case...#break...#end statements as well, we're ready to move on to what I think are two of the most powerful scripting commands...my own personal favorites, the #while...#end statement and the rand() function.
We're going to use these statements to create a scene where we define the scene elements, and POV takes care of the actual placement of them. We'll begin by looking at how the #while...#end loop works.
All of the example files used throughout this lesson are contained in this file. Please note that the examples listed here do not show the light or camera or background code, though it is included within the actual example files.
#declare X1=1
object {
union {
#while (X1<=10)
sphere {<X1,0,0>,.25}
#declare X1=X1+1
#end
}
pigment {color Red}
}

What this first example does is to create a line of 10 spheres at 1-unit intervals in the +x direction. We declared a counting variable, X1, and then by saying #while (X1<=10) we told POV to keeping doing the steps up to the #end. Let's manually work through the first couple of parses. POV sets a variable, X1, to a value of 1. The #while statement then asks "is X1 less than or equal to 10?" The answer is yes, so POV looks at the next statement, and sees that it's to draw a sphere at a point equal to <1,0,0>. Then it looks at the next statement, and sees that X1 is to be redefined as itself plus 1.... also known as "2." Then it reaches an #end statement, which sends it back to the question "is X1 less than or equal to 10?". The answer is still yes, so it again parses the two lines of code contained within #while...#end statement...and keeps looping like this, until X1=11, at which point the answer to the question becomes "no." It now skips to the first command after the #end statement, and continues on from there.
From this, we can see that command can easily be used to fill an area with a regular set of objects. We're mainly going to be working with rectangular arrays in this lesson, so let's look at the looping structure for those as well.
#declare X1=1
object {
union {
#while (X1<=10)
#declare Z1=1
#while (Z1<=10)
sphere {<X1,0,Z1>,.25}
#declare Z1=Z1+1
#end
#declare X1=X1+1
#end
}
pigment {color Red}
}

Here we see the result of nesting two different #while...#end loops. The first loop increments the object placement in the X direction. The second increments it in the Z direction. If we wanted to make a cubical array, it would be very easy to add another loop that increments in the Y direction. And expanding or repositioning our array is simple, it's just a matter of changing a couple of variables or adding another line or two of code. But suppose we don't want a regular array? Suppose we want our objects to be scattered about? Or suppose we don't want them to be all the same object? Here's where the rand() function comes in useful.
The random number generator used by POV is what is known as a "pseudo-random function." The numbers it returns are not actually truely random. Instead they are generated in series based on a seed value, and a given seed value will always return the same sequence of pseudo-random numbers, regardless of what computer or operating system POV is working on. This has two advantages.
First, the numbers are sufficiently "random" that you can use them to do things like affect the placement of objects in your scene, the colours and textures used by them, etc., and not see any obvious repetitive patterns as a result.
Second, if I use a random number in a scene, and send it to you, and we both render it, we will both end up with the exact same image, because the function will return the same series of "random" numbers to both of us. Repeatable random numbers... these are VERY, very, very useful.
To use a random number, you must begin by #declareing the seed value the random number generator is to use, for example:
#declare SD1=seed(1)
Then, wherever you want to use a random number, you call the rand() function, telling it which seed it's to create the number from. Let's try this out: suppose I want the spheres used in our last looping example to be randomly coloured.
#declare SD1=seed(1)
#declare X1=1
object {
union {
#while (X1<=10)
#declare Z1=1
#while (Z1<=10)
sphere {
<X1,0,Z1>,.25
pigment {color rgb <rand(SD1),rand(SD1),rand(SD1)>}
}
#declare Z1=Z1+1
#end
#declare X1=X1+1
#end
}
}

The rand() function returns a value from "0" to "1," so it'll create a nice bunch of random colours for me with this code. Suppose, on the other hand, that I also want to have the sphere be a random height above ground, from 0 to 5 units? Then I have to do some math on the random function to increase the size of the number it gives out:
#declare SD1=seed(1)
#declare X1=1
object {
union {
#while (X1<=10)
#declare Z1=1
#while (Z1<=10)
sphere {
<X1,rand(SD1)*5,Z1>,.25
pigment {color rgb <rand(SD1),rand(SD1),rand(SD1)>}
}
#declare Z1=Z1+1
#end
#declare X1=X1+1
#end
}
}

Doesn't look much like a regular rectangular array any more, does it? And yet, it we moved our camera to be pointing straight down at it from a height, we'd see the spheres were still falling at regular intervals in the X and Z axiis. And if we compared it to our previous picture, we'd notice something else, too - the distribution of the colours would have changed.
Why? Because the height of the sphere is now using up the first pseudo-random number, the one that the "red" rgb slot was getting before. So all the numbers in the random number queue are being redistributed...instead of being chopped up in groups of three, used for RGB values only, they're now in groups of four, with the first number being used up for the height value, and the next three being used for the RGB values.
If there was some reason why I wanted the set of colours to remain exactly as they were, rather than changing, what I would need to do is #declare a seperate seed value... SD2, for example... that would be used for the height.
Next section of the "#WHILE...#END and RAND()" tutorial
The Rendering
Times: Design and Copyright © 1997 -- DCS
& WorkForce Graphics. All rights reserved.
Reproduction in whole or in part in any form or medium without
the express written permission of DCS and/or WorkForce Graphics
is prohibited.