POV-ray - ImagInc
Start
Screensavers
POV-ray
Flash
Games
Educational
Download






insideout

ImagInc turns an image into an INC file so you can use the RGB-data for meshes, blobs or many other things (like curved heightfields).

This is a short tutorial on how to make an image like the ring above.

First you need an image to work from, much like a heightfield. For this explanation I used PaintShopPro to create this image:

boeimap1
With ImagInc I turned this image into an inc-file. For the first drafts I set the resolution at 5. This means that, while the image is 1000x200 pixels, it is converted as if it were a 200x40 image. Obviously this will result in a not very smooth image, but it has a great impact on the parsing time (25 times faster).

An inc-file generated by ImagInc contains two values for the width and height of the image (xcount, ycount) and three two-dimensional arrays for the red, green and blue values of the pixels in the image. Since in this case we're using it as a heightfield and we're working with a greyscale image we only need the data for one of the colourchannels.

I have created a POV file with the necessary statements like camera and light and a quick preview of the 'heightfield'.

camera{location <0,0,-1> look_at<0,0,0>}

light_source{<-100,200,-150> color rgb <1,1,1>}
light_source{<200,100,-150> color rgb <1,1,1>*.7 shadowless}
light_source{<100,-200,-150> color rgb <1,1,1>*.4 shadowless}
light_source{<-200,-100,-150> color rgb <1,1,1>*.1 shadowless}
light_source{<-100,200,-150> color rgb <1,.9,.2>*.25 shadowless}
light_source{<100,-200,-150> color rgb <.2,.4,1>*.25 shadowless}

#include "boei.inc"

#declare dd=255;
#declare mx=xcount/2;
#declare my=ycount/2;
Here dd is used to set the depth/height of the heightfield. The values from the heightfield-array in the inc-file are divided by dd. Since these values range from 0 to 255, a value of 255 reduces the range to 0-1.
Xcount and ycount are the width and height of the heightfield-image. Mx and my are calculated to center the image.

mesh{
  #declare yy=0;
  #while(yy<ycount)
    #declare yy2=yy+1;
    #if (yy2=ycount) #declare yy2=0;#end
    #debug concat(str((yy+1)*100/ycount,0,2)+"\n")
    #declare xx=0;#while (xx<xcount)
      #declare xx2=xx+1;
      #if (xx2=xcount) #declare xx2=0;#end
      #declare p1=<xx -mx,my-yy, -rdat[yy ][xx ]/dd>;
      #declare p2=<xx2-mx,my-yy, -rdat[yy ][xx2]/dd>;
      #declare p3=<xx -mx,my-yy2,-rdat[yy2][xx ]/dd>;
      #declare p4=<xx2-mx,my-yy2,-rdat[yy2][xx2]/dd>;
      triangle{p1,p2,p3}
      triangle{p2,p3,p4}
    #declare xx=xx+1;#end
  #declare yy=yy+1;#end
  pigment{rgb 1}
  scale 1/xcount
}
Looping through the array with xx and yy as counters, a vector is calculated with xx and yy as the x and y coordinates and the value from the rdat-array as z-component. Using mx and my the image is centered.
Vectors are also calculated for the three next points (x+1,y), (x,y+1), (x+1,y+1). The calculation of xx2 and yy2 makes sure the values don't go beyond the range of the array and that the image will nicely 'wrap'.

Here's the result:
draft1
Not much yet really, the original map-image is hardly recognizable.

Now we're going to wrap this around a tube:

#declare rad1=ycount/pi;

mesh{
  #declare yy=0;#while(yy<ycount)
    #declare yy2=yy+1;
    #if (yy2=ycount) #declare yy2=0;#end
    #debug concat(str((yy+1)*100/ycount,0,2)+"\n")
    #declare xx=0;#while (xx<xcount)
      #declare xx2=xx+1;
      #if (xx2=xcount) #declare xx2=0;#end
      #declare p1=vrotate(<xx -mx,0,-rad1-rdat[yy ][xx ]/dd>,<90-yy *360/ycount,0,0>);
      #declare p2=vrotate(<xx2-mx,0,-rad1-rdat[yy ][xx2]/dd>,<90-yy *360/ycount,0,0>);
      #declare p3=vrotate(<xx -mx,0,-rad1-rdat[yy2][xx ]/dd>,<90-yy2*360/ycount,0,0>);
      #declare p4=vrotate(<xx2-mx,0,-rad1-rdat[yy2][xx2]/dd>,<90-yy2*360/ycount,0,0>);
      triangle{p1,p2,p3}
      triangle{p2,p3,p4}
    #declare xx=xx+1;#end
  #declare yy=yy+1;#end
  pigment{rgb 1}
  scale 1/xcount
}
A new variable is introduced: rad1 which is the radius of the tube. I have made this a function of the height of the map image but that's not really necessary.
The vectors are created by rotating a vector around the x-axis. Here is how:
  • first create a vector as in the previous scene and take out the y-component
  • then translate this the value of rad1 in the z-direction (towards the camera) - this
  • can be written as:
  • then rotate this vector around the x-axis depending on its yy-value
Since you want to get a closed tube the rotation should be a full 360, so you calculate the step as yy*360/ycount.
To turn the text up to the front add another 90.
draft1
Again, the results aren't really breathtaking.
Before bothering us with this let's first create a real ring. Therefore we have to bend the tube around the y-axis:

#declare rad2=xcount/pi;

mesh{
  #declare yy=0;#while(yy<ycount)
    #declare yy2=yy+1;
    #if (yy2=ycount) #declare yy2=0;#end
    #debug concat(str((yy+1)*100/ycount,0,2)+"\n")
    #declare xx=0;#while (xx<xcount)
      #declare xx2=xx+1;
      #if (xx2=xcount) #declare xx2=0;#end
      #declare p1=vrotate(<0,0,-rad1-rdat[yy ][xx ]/dd>,<90-yy *360/ycount,0,0>);
      #declare p2=vrotate(<0,0,-rad1-rdat[yy ][xx2]/dd>,<90-yy *360/ycount,0,0>);
      #declare p3=vrotate(<0,0,-rad1-rdat[yy2][xx ]/dd>,<90-yy2*360/ycount,0,0>);
      #declare p4=vrotate(<0,0,-rad1-rdat[yy2][xx2]/dd>,<90-yy2*360/ycount,0,0>);
      #declare p1=vrotate(p1+<0,0,-rad2>,<0,90-xx *360/xcount,0>);
      #declare p2=vrotate(p2+<0,0,-rad2>,<0,90-xx2*360/xcount,0>);
      #declare p3=vrotate(p3+<0,0,-rad2>,<0,90-xx *360/xcount,0>);
      #declare p4=vrotate(p4+<0,0,-rad2>,<0,90-xx2*360/xcount,0>);
      triangle{p1,p2,p3}
      triangle{p2,p3,p4}
    #declare xx=xx+1;#end
  #declare yy=yy+1;#end
  pigment{rgb 1}
  scale 1/xcount    
  rotate x*-15
}
Another new variable: rad2 is the radius of the ring. Again I have calculated it from the width of the map-image but it can be any value (almost).
The vectors are calculated as in the previous scene, but now also the x-component is omitted. So you start with a vector with just the z-component, based on the rdat-array, translated (rdat1) for the tube-radius. This is rotated around the x-axis, then translated (rdat2) in the z-direction for the ring-radius and rotated around the y-axis. Again the rotation-angle is calculated based on xx with a 90 added to get the right part in front.

And here are the results:
draft1
For the final stage I want the text in the back, in the inside of the ring, to be engraved, so I have used a new map image:
boeimap2
I create a new inc-file, now with a resolution of 1. Suddenly it's a multi-Mb file.
Since the difference between text and background is halved (white or black on grey compared to white on black) the value of dd should be changed to get the correct depth.

Here is the full source:

camera{location <0,0,-1> look_at<0,0,0>}

light_source{<-100,200,-150> color rgb <1,1,1>}
light_source{<200,100,-150> color rgb <1,1,1>*.7 shadowless}
light_source{<100,-200,-150> color rgb <1,1,1>*.4 shadowless}
light_source{<-200,-100,-150> color rgb <1,1,1>*.1 shadowless}
light_source{<-100,200,-150> color rgb <1,.9,.2>*.25 shadowless}
light_source{<100,-200,-150> color rgb <.2,.4,1>*.25 shadowless}

#include "boei.inc"

#declare dd=64;
#declare mx=xcount/2;
#declare my=ycount/2;
#declare rad1=ycount/pi;
#declare rad2=xcount/pi;

mesh{
  #declare yy=0;#while(yy<ycount)
    #declare yy2=yy+1;
    #if (yy2=ycount) #declare yy2=0;#end
    #debug concat(str((yy+1)*100/ycount,0,2)+"\n")
    #declare xx=0;#while (xx<xcount)
      #declare xx2=xx+1;
      #if (xx2=xcount) #declare xx2=0;#end
      #declare p1=vrotate(<0,0,-rad1-rdat[yy ][xx ]/dd>,<90-yy *360/ycount,0,0>);
      #declare p2=vrotate(<0,0,-rad1-rdat[yy ][xx2]/dd>,<90-yy *360/ycount,0,0>);
      #declare p3=vrotate(<0,0,-rad1-rdat[yy2][xx ]/dd>,<90-yy2*360/ycount,0,0>);
      #declare p4=vrotate(<0,0,-rad1-rdat[yy2][xx2]/dd>,<90-yy2*360/ycount,0,0>);
      #declare p1=vrotate(p1+<0,0,-rad2>,<0,90-xx *360/xcount,0>);
      #declare p2=vrotate(p2+<0,0,-rad2>,<0,90-xx2*360/xcount,0>);
      #declare p3=vrotate(p3+<0,0,-rad2>,<0,90-xx *360/xcount,0>);
      #declare p4=vrotate(p4+<0,0,-rad2>,<0,90-xx2*360/xcount,0>);
      triangle{p1,p2,p3}
      triangle{p2,p3,p4}
    #declare xx=xx+1;#end
  #declare yy=yy+1;#end
  pigment{rgb 1}
  scale 1/xcount    
  rotate x*-15
}
draft1 Hopefully that's something you can work with.

This is a basic explanation. There are several ways to make variations. For instance you can scale the vectors after the first rotation to get a flatter ring like in the image at the top. Another idea is to spiral the heightfield around the tube or ring. The sky is (not) the limit.

If you have any questions or remarks regarding this page please let me know .

Work
Fun
Animation
Code
Tools

1996-2008 Remco de Korte
* my first name at onwijs.com