Stained Glass Texture

It is possible to get a nice leaded stained-glass effect using only the crackle texture and a bit of other native POV-Ray goodness.

At the moment, only this image is available at 1280×1024. The rest are at 320×240.

Demo Scene

First, start with a simple scene to show how this works. Then, modify the portion between the bars of asterisks to redefine Stained_Glass_texture until it looks right.

The images are linked to higher resolution versions.

 1: /*stainedglass_.pov
 2: (c)2007 David Wagner
 3: */
 4: #include "colors.inc"
 5: #include "skies.inc"
 6:
 7: global_settings {assumed_gamma 2.2}
 8:
 9: camera { up z sky z
10: // Positive x is right, y is foward, z is up.
11:    location<0,-1.5,1.5> look_at <0,0,1.5>
12:    }
13: light_source{<0,0,0> color Gray80
14: // Light is coming from behind and to the right.
15:    fade_distance 200 fade_power 3
16:    looks_like {sphere {<0,0,0>1
17:       pigment {BrightGold} finish{ambient .8} }}
18:    translate <20,-30,40>
19:    }
20: sky_sphere {S_Cloud5 rotate 90*x }
21: disc{<0,-1,0>,z,10 pigment{color Gray50} finish{reflection 0.3} }
22: sphere{<0,0,0>,.1 pigment{Green} }
23: cone{<0,0,0>.03 <0,0,1>.01 pigment {White} }
24: /*
25:   Everything prior to this sets up the basic scene
26:   and will remain unchanged.
27:   ***********************************************/
28:
29: #declare Stained_Glass_texture=texture{
30:          pigment{color 0.4*Red   +Gray40 filter 1}
31:          finish{specular .8 } }
32:
33: /***********************************************
34:   The following object applies the Stained_Glass_texture
35:   to a single surface, a square section of the x/z plane.
36:   */
37: intersection{
38:    box{<0,0,0><1,1,1> pigment{Clear} }
39:    plane{y,0.001
40:       texture{Stained_Glass_texture}
41:       }
42:    translate<-1/2,0,1>
43:    }

The Crackle

The crackle texture creates cells about one unit in size, obviously too big for a 1-unit square. The crackle parameters shown here are this pattern's defaults.

  ***********************************************/
#declare StainedGlass_texture=texture{
         pigment{crackle form <-1,1,0> metric 2 solid
            }
         finish{specular .8}
         }
/***********************************************

Smaller Crackle

I like to use irrational numbers for transformations such as this one to scale down the crackle texture.

  ***********************************************/
#declare Phi=sqrt(5)/2+.5;
#declare StainedGlass_texture=texture{
         pigment{crackle form <-1,1,0> metric 2 solid
            scale pow(Phi,-5) }
         finish{specular .8} }
/***********************************************

Crackle Solid Color Range

Using a linear color_map results in the colors all bunched up in the middle around green and blue.

  ***********************************************/
#declare Phi=sqrt(5)/2+.5;
#declare StainedGlass_texture=texture{
         pigment{crackle form <-1,1,0> metric 2 solid
            scale pow(Phi,-5) color_map{
               [0   color 0.4*Red   +Gray50 filter 1]
               [1/3 color 0.4*Green +Gray50 filter 1]
               [2/3 color 0.4*Blue  +Gray50 filter 1]
               [1   color 0.4*Red   +Gray50 filter 1]
               } }
         finish{specular .8} }
/***********************************************

Crackle Solid Sine Wave Color Range

Changing the crackle pattern's waveform to sine_wave seems to distribute the colors evenly.

  ***********************************************/
#declare Phi=sqrt(5)/2+.5;
#declare StainedGlass_texture=texture{
         pigment{crackle form <-1,1,0> metric 2 solid sine_wave
            scale pow(Phi,-5) color_map{
               [0   color 0.4*Red   +Gray50 filter 1]
               [1/3 color 0.4*Green +Gray50 filter 1]
               [2/3 color 0.4*Blue  +Gray50 filter 1]
               [1   color 0.4*Red   +Gray50 filter 1]
               } }
         finish{specular .8} }
/***********************************************

Crackle for Lead

The regular (not solid) crackle has the same pattern, with values near zero (black when used as a pigment) being where the lead is between glass pieces.

  ***********************************************/
#declare Phi=sqrt(5)/2+.5;
#declare StainedGlass_texture=texture{         
         pigment{crackle form <-1,1,0> metric 2
            scale pow(Phi,-5)
            }
         finish{ambient .2 phong .2 diffuse .5 reflection .3 metallic }
         }
/***********************************************

Combining the Pigments

Assigning separate glass and lead textures, both based on the same crackle pattern, then mapping these using this pattern yet again achieves the overall effect in two dimensions.

  • Use the variable i to slice out different parts of the pattern.
  • Experiment with Lead_Width to get the lead thickness you like.
  • Scaling and other transformations and warps are applied indirectly in a separate macro, StainedGlass_crackle_transformed(i). This is so the transformations are only applied once.
  ***********************************************/
#declare Phi=sqrt(5)/2+.5;
#declare i=0;
#declare Lead_width=0.05;
 
#macro StainedGlass_crackle(i)  
       crackle form <-1,1,0> metric 2
       #end
 
#macro StainedGlass_crackle_transformed(i)  
       StainedGlass_crackle(i)
       translate i rotate ((360/Phi)*i)
       scale pow(Phi,-5)
       #end
 
#declare Glass_texture=texture{
         pigment{StainedGlass_crackle(i) solid sine_wave
            color_map{
               [0   color 0.4*Red   +Gray50 filter 1]
               [1/3 color 0.4*Green +Gray50 filter 1]
               [2/3 color 0.4*Blue  +Gray50 filter 1]
               [1   color 0.4*Red   +Gray50 filter 1]
               } }
         finish{specular .8} 
         }
#declare Lead_texture=texture{         
         pigment{StainedGlass_crackle(i)
            }
         finish{ambient .2 phong .2 diffuse .5 reflection .3 metallic }
         }
#declare StainedGlass_texture=texture{
         StainedGlass_crackle_transformed(i)
         texture_map{
            [0          Lead_texture]
            [Lead_width Lead_texture]
            [Lead_width Glass_texture]
             }   }
/***********************************************

Final Scene

There are a few things to note.

  • Two nearly identical macros are needed, one for most uses, and one just for normals. Do not apply transformations to these.
  • The first argument of StainedGlass_crackle_bump_size(_Bump_Size,i) controls the overall three-dimensional effect.
  • Part of the StainedGlass_normal is used for the Lead_texture, and the rest is used for the Glass_texture.







  • The black hole warp makes it look as if larger glass pieces were used for the center and smaller pieces near the edges.
/*stainedglass_.pov
Copyright 2007 David Wagner
This software is licensed under the CC-GNU LGPL.
http://creativecommons.org/licenses/LGPL/2.1
*/
#include "colors.inc"
#include "skies.inc"
 
global_settings {assumed_gamma 2.2}
 
camera { up z sky z
// Positive x is right, y is foward, z is up.
   location<0,-1.5,1.5> look_at <0,0,1.5>
   }
light_source{<0,0,0> color Gray80
// Light is coming from behind and to the right.
   fade_distance 200 fade_power 3
   looks_like {sphere {<0,0,0>1
      pigment {BrightGold} finish{ambient .8} }}
   translate <20,-30,40>
   }                                                         
sky_sphere {S_Cloud5 rotate 90*x }
disc{<0,-1,0>,z,10 pigment{color Gray50} finish{reflection 0.3} }
sphere{<0,0,0>,.1 pigment{Green} }
cone{<0,0,0>.03 <0,0,1>.01 pigment {White} }
/*
  Everything prior to this sets up the basic scene
  and will remain unchanged.
  ***********************************************/
#declare Phi=sqrt(5)/2+.5;
#declare i=1;
#declare Lead_width=0.05;
 
#macro StainedGlass_crackle(i)  
       crackle form <-1,1,0> metric 2
       #end
 
#macro StainedGlass_crackle_bump_size(_Bump_Size,i)  
       crackle _Bump_Size form <-1,1,0> metric 2
       #end
 
#macro StainedGlass_crackle_transformed(i)  
       StainedGlass_crackle(i)
       translate i rotate ((360/Phi)*i)
       warp{turbulence pow(Phi,-4)*<1,0,1>
          octaves 3 lambda 1.5 omega 0.5}
       scale pow(Phi,-5)
       warp{black_hole <0.5,0,0.5>,1 strength -.4 }
       #end
 
#declare StainedGlass_normal=normal{
         StainedGlass_crackle_bump_size(1,i)
            slope_map {
               [0              <1    , 0>] //start flat
               [Lead_width/3   <1    , 0>] //stay flat
               [Lead_width     <0.25 ,-1>] //slope down to edge of lead
               [Lead_width+0.001<0.25 , 1>]
               [Lead_width+0.25<Lead_width+0.5, 0>]
                  //use greater final height for thicker glass
               [1              <Lead_width+0.5, 0>] 
               } }
#declare Glass_texture=texture{
         pigment{StainedGlass_crackle(i) solid sine_wave
            color_map{
               [0   color 0.4*Red   +Gray50 filter 1]
               [1/3 color 0.4*Green +Gray50 filter 1]
               [2/3 color 0.4*Blue  +Gray50 filter 1]
               [1   color 0.4*Red   +Gray50 filter 1]
               } }
         normal{average normal_map{
            [1 waves 0.10 scale pow(Phi,4)] 
            [9 StainedGlass_normal]
            } }
         finish{specular .8}
         }
#declare Lead_texture=texture{         
         pigment{Gray60}
         normal{average normal_map{
            [1 waves 0.10 scale pow(Phi,4)]
            [4 dents 2 scale pow(Phi,-4)]
            [4 wrinkles 1 scale pow(Phi,-5)]
            [4 StainedGlass_normal ]
            } }
         finish{ambient .2 diffuse .5 phong .2 reflection .3 metallic}
         }
#declare StainedGlass_texture=texture{
         StainedGlass_crackle_transformed(i)
         texture_map{
            [0          Lead_texture]
            [Lead_width Lead_texture]
            [Lead_width Glass_texture]
            } }
/***********************************************
  The following object applies the StainedGlass_texture
  to a single surface, a square section of the x/z plane.
  */
intersection{
   box{<0,0,0><1,1,1> pigment{Clear} }
   plane{y,0.001
      texture{StainedGlass_texture}
      }      
   translate<-1/2,0,1>
   }

A Better Slice

The slice using i=0 has some large lead-filled areas. Using i=1 gives a better result.

At the moment, only this image is available at 1280×1024.

Use, Improvements, and Ideas

Here are a few ideas on using this texture.

  • The macro and micro textures of both the leading and the glass can be improved.
  • FIXME The glass finish is not convincing; try flattening it and using some reflection.
  • Use facets to vary the orientation of each piece of glass.
  • Depending on your lighting, the lead may be too bright or the glass too saturated. Try decreasing the Lead_texture pigment to Gray40 or so, and increase the +Gray50 components of the Glass_texture.
  • Instead of a full rainbow of glass colors, use discrete, limited color ranges around the colors appropriate for your composition. You may wish to use the color ranges common to historical glass pigments.
  • Adjust the glass texture to produce good fake caustics.
  • When applying to a three-dimensional object (instead of a single plane), mirror it but use slightly different normals on front and back. Then see how well it works with photons.
  • Repeat and randomize the black hole warp for large sheets of stained glass.
  • See what happens with strong black hole warps, both positive and negative. (There are some nice effects.)
  • Try to make a rose window effect by combining crackle and radial textures. Please let me know how you do it.
  • Extend this technique for making broken and rounded glass pebble mosaics, fruitcakes, and other things.
  • FIXME Try to remove the dependency on that i value.
  • Scale the pattern normal to the surface to reduce large lead fills.

And, please don't forget to share your experience.

References

Although this technique is mentioned in the manual, I could not find a good reference for using only textures to create a reasonable stained-glass effect. At first I used a height_field to make the lead, similar to the isosurface technique used in this competition entry, POVCOMP 2004: Viewing Page for "A Bat in the Belfry". Although the height_field technique works fine, it is a pain to keep everything aligned correctly. I wanted to simplify the usage of this effect and to minimize its render time so it can be used in animation.

I think I succeeded.

David Wagner 2007/01/21 13:51

License

Copyright 2007 David Wagner
This software is licensed under the CC-GNU LGPL.

Personal Tools