// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // // Persistence of Vision Ray Tracer Scene Description File // // File: LandscapeTutorial.pov // // Vers: 3.6 // // Desc: Sample landscape scene with detailed comments. If you use my code, it'd be nice to be credited. Thanks! // // Auth: Kirk Andrews www.tektonart.com // // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // #version 3.6; #include "stdinc.inc" // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% CONTROLS #declare TerrainScale = 20; // Global Terrain scale #declare HFResolution = 1000; // Generated Resolution of the HeightField #declare RadiosityOK = on; #declare AreaLightOK = off; #declare TreesOK = on; #declare r = seed(10); #declare TS = TerrainScale; global_settings { assumed_gamma 1.0 max_trace_level 12 #if (RadiosityOK) radiosity { count 1 //30 error_bound .15 recursion_limit 1 //3 normal off media off brightness 1 } #end } // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% CAMERA // This is just the default camera. Later, we'll use the trace function to put it directly // on the terrain. camera { location <20.0, 9.5, -20.0> direction 1.5*z right x*image_width/image_height look_at <0.0, 6.0, 0.0> } // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% LIGHTS AND EFFECTS sky_sphere { pigment { gradient y color_map { [0.0 rgb <0.6,0.7,1.0>] [0.7 rgb <0.0,0.1,0.8>] } // This warp makes sure there's no ugly line on the horizon. warp {repeat y flip y} } } light_source { <0, 0, 0> // Light Color: // I Like to use a very bright light with an yellow-orange tint color rgb <1.3, 1, .7>*3.5 // An Area Light helps the feel of the scene quite a bit. #if (AreaLightOK) area_light <400, 0, 0> <0, 400, 400> 8, 8 adaptive 1 jitter circular orient #end // This transparent sphere creates a quick and easy visible sun. looks_like { sphere {0, TS*10 pigment {rgbt 1} finish {specular 1} } } translate <40, 15, 20>*TS } fog { fog_type 2 distance 5*TS color rgb <1,.9,.8> // A fog color that roughly approximates the light color works well for sunset/sunrise scenes. fog_offset 0.1 fog_alt TS/3 turbulence 0.0 } // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% TEXTURES // sets the default texture that objects get when they have no texture specified #default { texture { pigment {color rgb 1} #if (RadiosityOK) finish{ambient 0.0} #else finish{ambient 0.1} #end } } // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% The Terrain // HF functions: // Often, a combination of functions produces a better result. #declare gx = function { pattern { // The wrinkles pattern is the basis for most terrains I create. When used // with poly_wave 3 or 4, it produces mountain ridges with valleys between. wrinkles poly_wave 3 scale .15 translate 3.5*z // Don't quite like the lay of the land? Just move the function a bit. } } #include "transforms.inc" #debug "Build Terrain \n" // Here's the Heightfield #declare Terrain = height_field { // Instead of loading a pre-made image heightfield, this uses // a function to generate the image. function HFResolution, HFResolution //This sets the resolution of the generated image //The value is set above in "Controls" { ( gx(x,y,z) ) } smooth // I center the HF: translate <-.5,0,-.5> // I scale it to fill the space between <-1,0,-1> and <1,2,1> scale 2 // To add a little interest, I often rotate the whole terrain a bit rotate 2*x // If you want to add water, adjust the water level by translating the terrain down. // That way, you can create a water plane at y=0, and it makes it easier to create // altitude-dependant texture patterns. translate .1*y // I now scale it to the global TerrainScale // Adjust the y scale to your liking. scale // The Shear_Trans function from transforms.inc can create some interesting results // on landscapes, allowing heightfields to even have overhangs on one side. Just tweak // the "y" value slightly. Shear_Trans(x, <0,1,.05>, z) } // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Textures // This is a simple Proximity Function. I use it for many things in a landscape scene. #declare ProxF = function { pigment { average pigment_map { #local i = 0; #while (i < 50) [ // The object pattern, using the terrain. object { object{ Terrain translate *rand(r)} color rgb 0, // outside object color rgb 1 // inside object } ] #local i = i+1; #end } } } // ----------------------------- Rocks --- // The color of the rocks #declare RockPig = pigment { // The cells pattern with some turbulence has many uses. I love // the cells pattern because its randomness allows for a more natural // selection of colors for things grass or rocks. Turbulence is key, // however. I use a high lambda value to break up and scatter the edges. cells turbulence .5 lambda 8 color_map { [0.0 rgb <.8,.6,.4>] [0.5 rgb <.7,.6,.5>] [1.0 rgb <.5,.4,.1>] } scale <1,.1,1>*.25 warp {turbulence .2} rotate 30*x } #declare Rock1 = texture { pigment { // Using the Proximity function here allows the more exposed // rock to have the above Rock Pigment, while more sheltered // areas will have a darker color more like dirt. function {ProxF(x,y,z).red} pigment_map { [0.0 RockPig ] [1.0 rgb <.3,.05,.0>] } } normal { // The slope pattern is essential for landscapes. Here I use it // to apply a strong normal pattern to vertical surfaces while flat // areas will be smoother. slope y normal_map { [0.0 granite 0.8] [0.5 granite -0.8] // Sometimes, a negative granite pattern produces better results. [1.0 granite 0.1] } scale .4 scale .5*y // Notice: These transformations match those on RockPig. warp {turbulence .2} rotate 30*x } } // ----------------------------- Grass --- #declare GrassPig = pigment { // I am using the Proximity Function again, this time to put darker grass // in sheltered areas and lighter, browner grass in exposed areas. function {ProxF(x,y,z).red} // Why '.red?' It needs to be converted from a color vector to a float value, and one color is faster than '.gray'. color_map { [0.0 rgbt <.4,.5,.2,1> ] [0.2 rgbt <.4,.5,.2,.5> ] [0.5 rgb <.15,.3,.0> ] [0.9 rgb <.1,.2,.0> ] } } #declare Grass1 = texture { pigment { // The slope map will put grass on flat areas and avoid steep slopes. slope y // Instead of having a gradual transition of grass opacity or a flat line between // areas of grass and non-grass, I use high-lambda turbulence for a more natural effect. // By scaling it before hand, and then "un"-scaling afterward, I can adjust the size of the // turbulence. Although, to be honest, I'm not sure it works on a slope pattern... scale 10 warp {turbulence .3 lambda 12} scale 1/10 pigment_map { [.77 rgbt <.1,.2,.0,1> ] [.77 GrassPig ] } } } // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Plant Vegetation #if (TreesOK) // Planting Trees takes a while. I like to let myself know how its coming: #debug "Planting Trees \n" #debug "Load Tree \n" // Load the tree file. I use POV-Tree to make my trees. If I'm making a whole forest, I try to make // the tree very lightweight. Also, after the POV .inc file is created, a go in and make two important // changes: I add "double_illuminate" to the FOILAGE mesh, and I delete it's texture attribution (do a search for "WOOD" // to find the bottom of the FOILAGE mesh). This allows me to add my own textures below. #include "DistantTree3.inc" /* //If you don't have a tree file, this'll do for now: #declare WOOD = cone { 1*y, 0.0, -1*y, 0.1 // open texture { pigment {rgb <.3,.2,.1>} } } #declare FOILAGE = cone { 1*y, 0.0, .2*y, .7 } #declare TREE = union { object {WOOD} object {FOILAGE} } */ #declare NumTrees = 20000; #declare i = 0; #declare TreeF = function { pattern { bumps turbulence .1 scale 3 } } #declare MaxY = max_extent(Terrain).y; // Find the highest altitude of the terrain #debug "Plant Trees \n" #while (i < NumTrees) #declare Obj = Terrain #declare Norm = <0, 0, 0>; // Pick a random place within the scope of the terrain #declare Start = <(rand(r)-.5)*2*TS, 500, (rand(r)-.5)*2*TS>; // Find out where that random spot is on the ground #declare Pos = trace ( Obj, // object to test Start, // starting point -y, // direction Norm ); // normal // We've got our random spot, but should a tree grow there? #if ( Pos.y > .1*MaxY // Altitude controls: Minimum & Pos.y < (.5+rand(r)*.4)*MaxY // " " : Maximum, with a little feathering & vdot(Norm, y) > .8 // Limit the slope on which the tree grows. vdot is a dot product function. & TreeF(Pos.x,Pos.y,Pos.z) < .5+rand(r)*.1 // I use a function to gather trees togther, while some bare spots remain. The rand() helps feather the edges. & ProxF(Pos.x,Pos.y,Pos.z).red > .45 // Here's that proximty function again. Trees shouldn't grow on the peaks of rocky ridges. ) object { // POV-Tree separates the resulting tree into "WOOD" and "FOILAGE". I like to plant some occasional dead trees, // without the foilage. #if (rand(r) > .9) WOOD #else TREE // If you go into the generated POV-Tree .inc file, you can delete the texture attribution on the FOILAGE mesh, // allowing to add it here with some variation from tree to tree. texture { pigment { rgb <.1 + rand(r)*.05, .2 + rand(r)*.15, 0> } } #end // These tranformations are all about increasing the perceived variety of trees in the forest. translate -rand(r)*.3*y // This trick will work as long as your camera isn't too close. It adds to the perceived variety of trees. scale .2 // POV-Trees are 1 unit tall. You'll have to figure out how tall your trees should be in your scene. rotate rand(r)*4*x rotate rand(r)*360*y scale <.5+rand(r), .5+TreeF(Pos.x,Pos.y,Pos.z)*2, .5+rand(r)> // More variation #if (rand(r) > .5) scale -x #end // Flip soome trees scale <1, 1+ProxF(Pos.x,Pos.y,Pos.z).red, 1> // Trees in ditches should be taller. The ProxF function should provide a decent approximation. translate Pos // Plant it! } #local i = i + 1; // The counter only ticks if a tree is actually planted. // Just a little code to let me know how the planting is coming: #if (mod(i,NumTrees/10) = 0) #debug concat("\n Trees: ", str(i,3,0) ) #end #end #end #end // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Place the Camera // I use the trace function to place the camera on the terrain. // The trace function finds the first intersection of a ray from // "Start" toward "direction" with the specified object. #declare Obj = Terrain #declare Norm = <0, 0, 0>; #declare Start = <.05*TS, 500, .95*TS>; #declare Pos = trace ( Obj, // object to test Start, // starting point -y, // direction Norm ); // normal #if (Norm.x != 0 | Norm.y != 0 | Norm.z != 0) // The trace again, this time to determine what the camera is looking at. #declare Start = <.05*TS, 500, .05*TS>; #declare LPos = trace ( Obj, // object to test Start, // starting point -y, // direction Norm ); // normal camera { //The camera is placed at the intersection, then moved off the ground a little. location Pos + <0,.2*TS,0> direction 1.5*z right x*image_width/image_height look_at LPos + <0*TS,0,0*TS> } #end // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% SCENE object {Terrain texture{Rock1} texture{Grass1} }