Tutoriel de placement d'objets par balayage et fonction trace()

Sur ce détail d'une image faite avec POV, on peut voir de l'herbe sur la côte. L'herbe épouse les courbes du terrain et ne pousse pas en dessous d'un certain niveau ni à partir d'un certaine pente.La côte est un height_field (HF) et l'herbe est une union de quelques meshs plantée dessus en balayant toute l'étendue du HF.
Comme dans un texte tapé à la machine, on tape une ligne puis, arrivé au bout de la ligne, on passe à la suivante en effectuant un retour charriot.
De cette maniere on scanne la surface du HF (ce pourrait etre n'importe quel objet, primitive, mesh, CSG etc...)
Nous allons étudier comment réaliser cela.

 

Posons quelques préliminaires à notre scène :une caméra othographique pour garder une vision claire des choses) et une lumière.
Nous ne les répèterons pas dans les versions successives du code.

 

#include "colors.inc"
background {    color White }    
camera {    orthographic location    <-1,3,-2>    look_at 0 right    1.33*x    *2.5 up    y*2.5    } 
light_source    {0 color    rgb 2 translate    <-20,    400, 0>} 

//Créons un objet exemple. Ce pourrait être n'importe quoi, un brin d'herbe, un bonhomme, un arbre...mais restons modestes pour l'instant ;-)

#declare Mon_objet= sphere {0,0.02 pigment { Yellow}}

   
Ceux qui n'ont pas de problèmes avec les boucles peuvent passer directement à la page suivante pour étudier la fonction trace()


Etudions d'abord le balayage.

Commençons simple, essayons de voir comment répartir régulièrement
des copies d'un objet.

On pourrait faire

object{Mon_objet    translate <0,0,0>}    
object{Mon_objet    translate <1,0,0>}    
object{Mon_objet    translate <2,0,0>}   
 /... .../    
object{Mon_objet    translate <100,0,0>}    
// nouvelle ligne     
object{Mon_objet translate    <0,0,1>}    
object{Mon_objet    translate <1,0,1>}    
object{Mon_objet    translate <2,0,1>}   
 /... .../    
object{Mon_objet    translate <2,0,100>}   

 etc...

Ce serait cependant (très) fastidieux et pas (du tout) très paramétrable.
Tâchons d'automatiser un peu les choses.
Nous avons dans le SDL (Scene Description Language), le langage de POV, la possibilité de faire effectuer des boucles c'est à dire que quand POV lit (parse) le code avant de calculer (trace) l'image il lit une section du code en boucle.
Evidemment, il ne faut pas oublier de lui offrir une sortie sinon, il parse sans arrêt la boucle.
On pose donc une condition à la poursuite de la boucle.

#while (Condition) 
	// Corps de la boucle	
#end

Au début de la boucle, POV teste si la condition est toujours remplie, si oui il recommence, sinon, il continue la lecture du code après la fin de boucle (#end).
Vous pouvez consulter "Boucles while/end" dans la Référence, excellent site explicatif sur POV,en Français

Faisons une ligne.

Avant d'entamer la boucle, il faut initialiser quelques variables comme un pointeur de balayage qu'on va changer à chaque passage dans la boucle.
Il servira à recalculer une ou des variables à chaque passage dans la boucle (pour déterminer de nouvelles coordonnées par exemple).
Il servira également à déterminer s'il faut sortir de la boucle ou la recommencer.
C'est une forme tres classique de boucle.
Nous allons en profiter pour déclarer aussi quelques paramètres comme les coordonnées de départ et d'arrivée du balayage ainsi que le pas de balayage (l'intervalle entre les objets).
Chaque fois que possible, on déclarera les coordonnées sous forme de vecteur, cela permet de les regrouper dans une seule variable.
Si nécessaire, on peut extraire une composante, x, y ou z à l'aide de "
.x", ".y" ou ".z"

#declare Debut=<-1,0,-1>;// C'est un vecteur, il contient les coordonnées dans l'espace du début du balayage
// On   n'oublie pas le point virgule, comme apres toute déclaration de vecteur out de nombre, sinon "BLING!"
#declare    Fin=<1,0,1>; // Pareil pour le point de fin de balayage   
#declare Pas=0.1;//   distance entre deux copies successives de l'objet 
#declare Pointeur_x=Debut.x;  //on initialise le pointeur de balayage en lui
//assignant la composante x du vecteur de Debut  
#while (Pointeur_x<Fin.x)  //Tant que le pointeur x est plus petit  que la coordonnée x
	//de la Fin, le code revient dans la boucle et pose des copies de l'objet     
	#declare Position=<Pointeur_x,0,0>; 
	//on compose le vecteur contenant les coordonnées où poser l'objet à chaque passage dans la boucle    
	object {    Mon_objet translate Position}
	#declare Pointeur_x=Pointeur_x+Pas;  //On augmente le pointeur de la valeur de l'intervalle entre les objets
#end //Fin de la boucle

Bon c'est sympa mais ça ne fait qu'une ligne :-/.

Faisons plusieurs lignes

Comment faire pour obtenir plusieurs lignes?
Bon sang, mais c'est bien sur! Une autre boucle (qu'on va appeler "boucle Z" entre nous) qui répetera notre boucle X plusieurs fois.

Il faudra que la boucle X soit à l'intérieur de la boucle Z.

Le code donne ça

 

#declare Debut=<-1,0,-1>;
#declare Fin=<1,0,1>;   
#declare    Pas=0.1;     
#declare Pointeur_z=Debut.z;  //On met le pointeur z au depart
#while    (Pointeur_z<Fin.z) //Debut boucle z et test 
	#declare Pointeur_x=Debut.x; //remise du pointeur x au départ à chaque boucle z
	#while    (Pointeur_x<Fin.x)    //début de boucle x
		#declare    Position=<Pointeur_x,0,Pointeur_z>;     
		object {    Mon_objet translate Position}    
		#declare Pointeur_x=Pointeur_x+Pas;   
	#end    //Fin de la boucle x
	#declare Pointeur_z=Pointeur_z+Pas;   
#end    //Fin de la boucle z

Vous pouvez vous amuser avec différentes valeurs de Debut, Fin et Pas.

bon, vous allez me dire que c'est tres régulier, et vous aurez raison.

Introduisons un peu de désordre:

Il existe une fonction intéressante dans pov: vturbulence(Lambda, Omega, Octaves, A)
elle marche comme la turbulence dans les pigments: pov "dérange" un peu, par touches successives, les coordonnées d'un point à évaluer.
Les parametres Lambda, Omega et Octaves sont un peu compliqués à expliquer en 3 lignes.
Pour ceux que ça interésse allez faire un tour sur la référence, excellent site explicatif sur POV
warp et turbulence
En attendant, des valeurs typiques de Lambda=2, Omega=0.5 et Octaves =6 conviennent bien.
"A" est le vecteur d'entrée c'est à dire qu'on fournit les coordonnées d'un point dans l'espace et vturbulence calcule la turbulence à cet endroit.

Nous allons prendre le vecteur Position et lui ajouter la vturbulence en lui appliquant un taux nommé Turb (dont la composante Y restera nulle pour ne pas turbuler l'objet verticalement.)

#declare Pas=0.1;
#declare Debut=<-1,0,-1>;    
#declare Fin=<1,0,1>;    
#declare Turb=<1,0,1>*0.2;  //    taux de turbulence nul sur y pour laisser les objets au meme niveau
#declare    Pointeur_z=Debut.z;   
#while (Pointeur_z<Fin.z)    
	#declare Pointeur_x=Debut.x;    
	#while (Pointeur_x<Fin.x)   
		#declare Position=<Pointeur_x,0,Pointeur_z>;    
		#declare Position=Position    +vturbulence(2,.5,6,Position)*Turb;   //    ici vturbulence
		object {    Mon_objet translate    Position}    
		#declare Pointeur_x=Pointeur_x+Pas;   
	#end 
	#declare    Pointeur_z=Pointeur_z+Pas;    
#end 

On obtient ceci:

On a effectivement de la turbulence mais le "mouvement" semble ample, de basse fréquence.
Les objets ont des positions liées (corrélées?) à celles de leurs voisins, un peu comme sur des vagues.
On peut désirer cet effet, on peut aussi désirer des positions plus indépendantes.
Cela changerait si on multipliait le vecteur d'entrée ("A" dans le modèle de vtubulence) par une valeur appelée euh... au hasard Frequence.
On peut décrire cela en disant que la position d'un objet serait moins liée à celle de ses voisins.
En effet, vturbulence irait chercher des positions plus distantes les unes des autres pour déterminer la turbulence des objets voisins .

#declare Pas=0.1;
#declare Debut=<-1,0,-1>;    
#declare Fin=<1,0,1>;    
#declare Turb=<1,0,1>*0.2;  
#declare Frequence=4; //    Déclaration de la Frequence
#declare    Pointeur_z=Debut.z;   
#while (Pointeur_z<Fin.z)    
	#declare Pointeur_x=Debut.x;    
	#while (Pointeur_x<Fin.x)   
		#declare Position=<Pointeur_x,0,Pointeur_z>;    
		#declare Position=Position    +vturbulence(2,.5,6,Position*Frequence)*Turb;   //    ici Frequence
		object {    Mon_objet translate    Position}    
		#declare Pointeur_x=Pointeur_x+Pas;   
	#end 
	#declare    Pointeur_z=Pointeur_z+Pas;    
#end 

 
 
Voila:
Nous pouvons maintenant passer à la page suivante pour étudier la fonction trace()

Retour page d'accueil