Saturday, July 28, 2012

Tutorial: Refraction in PovRay

I'm been experimenting with PovRay, a 3D ray tracing program based on constructive geometry.  Creating an image requires declaring a scene in a text file, not that dissimilar from writing a computer program.  When I was a kid, I remember my father teaching me about refraction by putting a pencil in a glass of water.  The pencil appears to bend because the path taken by photons reflected by the pencil bends via refraction - governed by Snell's law - then the pencil meets the water surface.  The water has an index of refraction (IOR) of about 1.3, while air is about 1.0.

Today, I'll show you how to simulate this effect in PovRay.  Please feel free to adapt the ideas present here into your own work (I'd appreciate it if you'd like to my blog though - but you don't have to).

If you don't already have PovRay installed you can get it here: http://www.povray.org/download/  Actually, as of this writing (2012-07-28), I recommend the version 3.7 release candidate: http://www.povray.org/beta/

Here is the final scene we will render (3840x2160 with 0.3 anti-aliasing; click to view full resolution):



Another view of the scene, showing the refraction more clearly from the side (click to view full resolution):



Our scene will include four objects:
  1. A partially transparent glass (index of refraction 1.5)
  2. Water (index of refraction 1.3)
  3. A straw
  4. A partially reflective surface for the glass to stand on

First, though, some setup parameters:


// Daniel J. Parente
// Licensed under CC 3.0-BY-SA

//Always use a version directive! 
//You might need to change this is you are using PovRay 3.6
#version 3.7;  

//Include some files
#include "colors.inc"
#include "textures.inc"
#include "glass.inc"

global_settings
{
   assumed_gamma 1.0   //For PovRay 3.7 (ver. 3.6 used 1.7, I think)
}
        
//Declare a constant to control the size of our glass
#declare BEAKER_HEIGHT=15;


//Declare a light source off to the left of the camera
light_source
{
   <0,20,-30>*3 color 1  //Same vector as the camera
   rotate 50*y           //Rotate off to the left
}   

camera
{
   location <0, 20, -30>*1.2         //Define a camera location
   up y                              //As usual, y is the up direction
   right image_width/image_height*x  //Configure the aspect ratio
   look_at <0, BEAKER_HEIGHT/3.5, 0> //Look at the middle of the beaker
}        

The glass


Then, we'll create the glass by constructing the difference of two cylinders: one with a slightly smaller radius and offset upward (the y direction) from the other.  We'll make the constructed object partially transparent, give it a glass finish and - importantly - configure the index of refraction:

//Define the glass beaker
difference
{
   cylinder
   {      
      //Main body of the cylinder
      <0,0,0>, <0, BEAKER_HEIGHT, 0>, 5
   }
   cylinder
   {      
      //Make it hollow (but offset in the y direction to give a bottom)
      //Hollow by creating another cylinder, a little thinner than the
      // outer cylinder
      <0,-5,0>, <0, BEAKER_HEIGHT+.1, 0>, 4.9
   }
   pigment
   {
      //Set the degree of transparency
      //(I think it ignores the R,G,B fields)
      rgbt .7
   }
   finish
   {
      //Look like glass
      Glass_Finish
   }      
   interior
   {
      ior 1.5  //Sets an index of refraction (1.5 is good for glass)
   }     
}

The water

The water is simply a partially transparent blue cylinder, with a radius that is slightly smaller than the glass's inner radius and with an index of refraction of 1.3

//Water            
cylinder
{
   //Define a cylinder just barely smaller (.05 units) than the beaker
   <0,1.0001,0>, <0, BEAKER_HEIGHT*.9, 0>, 4.85
   pigment
   {
      rgbt<0, 0, 1, .8>   //Blue, partially transparent
   }

   interior
   {
      ior 1.3 //Sets an index of refraction for water
   }
}  

The straw

To showcase the refraction, place a straw (a thin, hollowed-out cylinder) inside the glass of water:

//Create a straw
difference
{
   cylinder
   {      
      //Cylinder
      <0,0,0>, <0,BEAKER_HEIGHT*1.2,0>, .5
   }
  
   cylinder
   {
      //Slightly thinner, but longer cylinder to hollow it out
      <0,-1,0>, <0,BEAKER_HEIGHT*1.6,0>, .4
   }                
   finish
   {
      //Make it look a little like plastic
      phong .3
   }
   //Yellow color
   pigment{ rgb<255,240, 113>/255 }
  
   //Rotate it so it tilts a bit (and we can see the IOR effect)
   rotate 20*z
   rotate -20*x
  
   //Shift the straw into the water cylinder
   translate <2,BEAKER_HEIGHT/5, 2.5>
}  

The mirror plane


Lastly, we'll add a partially reflective "mirror" surface for the glass to sit on:

//Construct a mirror plane for the glass to sit on
plane
{
   <0,1,0>, -.001
   pigment
   {
      color White
   }            
      
   finish
   {      
      reflection .7
   }   
}

Rendering

Render the scene at 1920x1080 (or, whichever setting you choose, the aspect ratio should auto-adapt).  This took about 2 seconds on a hyper-threaded quad-core (14 CPU-seconds) using PovRay 3.7.0-RC6 for Windows 64-bit.

If you want to get a better look at the refraction, you can change the camera position to:

location <0, 10, -30>*1.2

This will produce the second, side-view image shown above.

I encourage you to vary some parameters in this scene (especially IOR values) to get a feel for how refraction works in PovRay.

You can download the full scene here: http://biophysengr.net/files/blog/povray/water_refraction.pov

Standard disclaimers and statements
I am not affiliated PovRay. I have not accepted compensation in any form from any organization in support of this project. Use this information at your own risk. I am not responsible for any adverse effects your use of this information causes.