PovRay Reference
This post will cover many things that I have learned over the years using povray. The goal is to keep things simple and generate nice looking renders without too much complexity. I will also cover how to load simulation data for rendering.
Radiosity
When using radiosity I do not put in any extra light sources, povray describes this as Radiosity without conventional lighting. I find that this creates cleaner looking renders. In some cases where hard shadows are required or you want to have fine control over lighting you can add lights as needed.
Radiosity References
I usually start with the flowing settings and tweak from there. For most scenes I use a brightness value of 2.0 or 2.5 as there are no extra lights. Tweaking gray_threshold can be useful if you have bright objects and are getting too much color bleed.
#include "rad_def.inc"
// radiosity (global illumination) settings
global_settings {
radiosity{
Rad_Settings(Radiosity_OutdoorHQ, off, off)
//Increase object brightness (default 1.0)
brightness 2.0
// values < 1.0 reduce the influence of colors on indirect light (aka color bleed)
gray_threshold 1.0
}
}
//Create a white background to add env lighting
background { color rgb<1,1,1> }
A more complete radiosity example looks like this:
global_settings {
radiosity {
pretrace_start 0.08
pretrace_end 0.01
count 50
nearest_count 10
error_bound 0.15
recursion_limit 1
low_error_factor 0.2
gray_threshold 0
minimum_reuse 0.015
brightness 1.0
adc_bailout 0.01
}
}
Max Trace Level
max_trace_level
is a global settings that controls how many bounces each ray experiences. Another way to think about it is that if you had 100 glass spheres in a line, setting the max_trace_level
to 10 would not allow light to pass through all of the objects because the ray didn’t make it all of the way through and stopped after 10 layers.
global_settings {
max_trace_level 3
// ...
}
Quaternions!
Povray doesn’t come with default support for quaternions but several years ago Alain Ducharme wrote a very useful set of macros. The include file is here.
I usually don’t include the full quaternions.inc file, the following macros are generally enough
#macro QToMatrix(Q)
// Convert a quaternion to a Povray transformation matrix (4x3)
// Use: matrix <M[0].x,M[0].y,M[0].z,M[1].x,M[1].y,M[1].z,M[2].x,M[2].y,M[2].z,M[3].x,M[3].y,M[3].z>
#local X2 = Q.x + Q.x;
#local Y2 = Q.y + Q.y;
#local Z2 = Q.z + Q.z;
#local XX = Q.x * X2;
#local XY = Q.x * Y2;
#local XZ = Q.x * Z2;
#local YY = Q.y * Y2;
#local YZ = Q.y * Z2;
#local ZZ = Q.z * Z2;
#local TX = Q.t * X2;
#local TY = Q.t * Y2;
#local TZ = Q.t * Z2;
array[4] {<1.0 - (YY + ZZ),XY + TZ,XZ - TY>,<XY - TZ,1.0 - (XX + ZZ),YZ + TX>,<XZ + TY,YZ - TX,1.0 - (XX + YY)>,<0,0,0>}
#end
#macro QToEuler(Q)
#local Q2 = Q*Q;
<atan2 (2*(Q.y*Q.z+Q.x*Q.t), (-Q2.x-Q2.y+Q2.z+Q2.t)),
asin (-2*(Q.x*Q.z-Q.y*Q.t)),
atan2 (2*(Q.x*Q.y+Q.z*Q.t), (Q2.x-Q2.y-Q2.z+Q2.t))>
#end
#macro QMatrix(Q)
#local M = QToMatrix(Q)
transform { matrix <M[0].x,M[0].y,M[0].z,M[1].x,M[1].y,M[1].z,M[2].x,M[2].y,M[2].z,M[3].x,M[3].y,M[3].z> }
#end
To use the macros:
//where e1,e2,e3,e0 represent the quaternion, order is important!
QMatrix(<e1,e2,e3,e0>)
INI Files
There are two main ways to specify information in povray like the image resolution or antialiasing settings. The first way is through the command line or through an INI file. In either case, every option in povray has a command line version and an INI version.
Full Command Line/INI Reference
Below is the .ini file that I usually use. Antialiasing is turned on, the image resolution is 1920x1080
, images are rendered as .png
(Output_File_Type=N) and saved to output_folder
.
Antialias=On
Antialias_Threshold=0.1
Antialias_Depth=2
Width =1920
Height=1080
Input_File_Name=povrayfile.pov
Output_File_Name=output_folder
Output_File_Type=N
Initial_Clock=0
Final_Clock=1
Pause_when_Done=off
Then I call povray with povray +WT2 +KFI0 +KFF2000 test.ini
, which will render from frame 0 to frame 2000 using 2 threads. Note that output file names are padded with zeros depending on the start and final frame numbers. For example povray +WT2 +KFI0 +KFF2000 test.ini
will pad zeros so that the file name is 4 numbers long 0001.png
… 0200.png
… 1900.png
etc.
If you want to specify the start and end frame numbers in the ini add the following lines
Initial_Frame=0
Final_Frame=2000
Subset of frames can also be rendered, this is useful if you want to render a set of frames but keep the numbering consistent in terms of zero padding. For example povray +WT2 +KFI0 +KFF2000 +SF100 +EF120 test.ini
will render from frame 0100.png
to 0120.png
. Note that the number of zeros padded is based on the +KFF2000
or Final_Frame=2000
setting and not the +SF +EF
or equivalently the Subset_Start_Frame Subset_End_Frame
options. More details on options can be found here
Initial_Frame=0
Final_Frame=2000
Subset_Start_Frame=100
Subset_End_Frame=120
Note that for all of these frame number related settings there exists a frame_number
variable in PovRay that changes with each frame. I will cover this a little bit later.
File I/O
File I/O is important if you want to load in a ton of simulation data without writing a custom pov file. This way the simulation data can be used in other postprocessing applications while still being able to visualize it.
Say we have the following data file stored as “simdata.txt”
-0.0249999,-0.32501,25.475,
-0.0249999,-0.32501,25.525,
-0.0249999,-0.32501,25.575,
-0.0249999,-0.32501,25.625,
-0.0249999,-0.32501,25.675,
-0.0249999,-0.32501,25.725,
We can open this file for reading and read the first three entries and then do something with it.
#fopen MyPosFile "simdata.txt" read
#declare ax=0.0;
#declare ay=0.0;
#declare az=0.0;
#read (MyPosFile,ax, ay, az)
//Do something cool here
If we wanted to read multiple lines automatically we can do the following
#fopen MyPosFile "simdata.txt" read
#declare ax=0.0;
#declare ay=0.0;
#declare az=0.0;
#while(defined(MyPosFile))
#read (MyPosFile,ax, ay, az)
//Do something cool here
#end
We can also generate a filename from an integer, this will come in use in the next section when we can to render an animation. Here the concat function is used to build up the file name using the number and the file extension. More complicated strings can be gerenated depending on your needs.
#declare fnum=10;
#declare data_file = concat( str(fnum,-1,0), ".txt")
#warning concat("---- LOADING DATA FILE : ", data_file, "\n")
#fopen MyPosFile data_file read
// Read file as usual
Rendering Animations
In PovRay there is a frame_number
variable that is incremented with each frame based on settings in the .ini or settings set from the command line.
In many cases writing a single .pov file that handles all of your rendering rather than creating a new pov file for each frame is really useful. If we combine the file I/O and the frame number together we can read in data that is specific to a frame and write our pov file so that it can make decisions based on the data that is has for that frame.
#declare fnum=abs(frame_number);
#declare data_file = concat( str(fnum,-1,0), ".txt")
#warning concat("---- LOADING DATA FILE : ", data_file, "\n")
#fopen MyPosFile data_file read
Rendering Generic Data
I typically organize my data so that I have some information on the type of object I am rendering. The following convention works pretty well for me:
posx,posy,posz,e0,e1,e2,e3,vx,vy,vz,typ,.....,
The first three entries are the position, followed by the rotation as a quaternion, and then the velocity of the shape I want to render. The next piece of information is the type of shape I want to render
sphere=0
ellipsoid = 1
box = 2
cylinder=3
...
in practice this looks like the following:
-0.025,-0.3,25.5,1,0,0,0,-0.00019285,-0.00980167,-0.000386857,0,0.025,
-0.025,-0.3,25.7,1,0,0,0,-0.000193601,-0.00980151,0.000386788,1,0.025,0.045,0.055,
-0.025,-0.3,25.5,1,0,0,0,-0.000193805,-0.00980153,-0.000753941,2,0.025,0.03,0.05,
-0.025,-0.3,25.7,1,0,0,0,-0.000193439,-0.0098012,-4.10879e-07,3,0.025,0.03,
-0.025,-0.3,25.6,1,0,0,0,-0.000194167,-0.00980114,0.000753471,0,0.025,
-0.025,-0.3,25.8,1,0,0,0,-0.000194564,-0.00980103,-0.000386503,0,0.025,
What we want to do is to read up till the type information and based on the type, read the next set of data. The pov file to read something like this is as follows:
#declare ax=0.0;
#declare ay=0.0;
#declare az=0.0;
#declare e0=0.0;
#declare e1=0.0;
#declare e2=0.0;
#declare e3=0.0;
#declare vx=0.0;
#declare vy=0.0;
#declare vz=0.0;
#declare tx=0.0;
#declare ty=0.0;
#declare tz=0.0;
#declare typ=0;
#declare color1=<1, 1, 1,0>;
#declare color2=<0, 1, 0,0>;
#declare color3=<0, 0, 1,0>;
#declare color4=<0, 1, 1,0>;
#while(defined(MyPosFile) )
#read (MyPosFile, ax, ay, az, e0,e1,e2,e3,vx,vy,vz,typ)
#if(defined(MyPosFile) & typ=0)
#read(MyPosFile, tx)
sphere {<0,0,0>, 1 scale <tx,tx,tx> QMatrix(<e1,e2,e3,e0>) translate<ax, ay, az > pigment {color rgbt color1 }finish {diffuse 1 ambient .5 specular .05 } }
#end
//ellipsoid is a scaled sphere
#if(defined(MyPosFile) & typ=1)
#read(MyPosFile, tx,ty,tz)
sphere {<0,0,0>, 1 scale <tx,ty,tz> QMatrix(<e1,e2,e3,e0>) translate<ax, ay, az > pigment {color rgbt color2 }finish {diffuse 1 ambient .5 specular .05 } }
#end
#if(defined(MyPosFile) & typ=2)
#read(MyPosFile, tx,ty,tz)
box {<-1, -1, -1>,< 1, 1, 1> scale <tx,ty,tz> QMatrix(<e1,e2,e3,e0>) translate<ax, ay, az > pigment {color rgbt color3 }finish {diffuse 1 ambient .5 specular .05 } }
#end
#if(defined(MyPosFile) & typ=3)
#read(MyPosFile, tx,ty)
cylinder {<0, -1, 0>,< 0, 1, 0>,1 scale <tx,ty,tx> QMatrix(<e1,e2,e3,e0>) translate<ax, ay, az > pigment {color rgbt color4}finish {diffuse 1 ambient .5 specular .05 } }
#end
#end
Rendering Dirt or Water
Here what we are trying to do is to render a set of spheres but each sphere has a different color based on a random seed. This is useful to create a “dirt” or textured water look.
First find a set of colors, I used the following palette. To render a set of spheres loaded in from a file using four colors we do the following
//Our 4 colors
#declare color1=<35, 24, 17,0>/255;
#declare color2=<64, 47, 24,0>/255;
#declare color3=<110, 85, 56,0>/255;
#declare color4=<35,24,17,0>/255;
//Then create a random seed
#declare Random_1 = seed (1153);
//Read data line normal
#while(defined(MyPosFile))
#read (MyPosFile,ax, ay, az, e0,e1,e2,e3,typ,rad)
//Get a random number
#declare id = int( 100000*rand( Random_1) );
//Modulo it to pick one of the 4 choices
#if(mod(id,4)=0)
sphere {<0,0,0>, rad scale <1,1,1> QMatrix(<e1,e2,e3,e0>) translate<ax, ay, az > pigment {color rgbt color1 }finish {diffuse 1 ambient .5 specular .05 } }
#end
#if(mod(id,4)=1)
sphere {<0,0,0>, rad scale <1,1,1> QMatrix(<e1,e2,e3,e0>) translate<ax, ay, az > pigment {color rgbt color2 }finish {diffuse 1 ambient .5 specular .05 } }
#end
#if(mod(id,4)=2)
sphere {<0,0,0>, rad scale <1,1,1> QMatrix(<e1,e2,e3,e0>) translate<ax, ay, az > pigment {color rgbt color3 }finish {diffuse 1 ambient .5 specular .05 } }
#end
#if(mod(id,4)=3)
sphere {<0,0,0>, rad scale <1,1,1> QMatrix(<e1,e2,e3,e0>) translate<ax, ay, az > pigment {color rgbt color4 }finish {diffuse 1 ambient .5 specular .05 } }
#end
#end
Rendering velocity
If we store velocity data from the simulation and load it in we can render it as a color, details here