Une introduction à ...



Didier Verna et Nadine Richard


  • Ce document est principalement destiné à servir de support de cours pour les élèves de l'ENST. Son but est de fournir une premier contact interactif avec VRML97. À ce titre, il ne constitue pas un manuel de référence du langage (qui serait d'ailleurs inutile puisque la spécification complète est publique), mais il amène progressivement les notions importantes en même temps que les éléments du langage à connaître en priorité.


  • Les exemples peuvent être testés directement, à condition d'avoir un visualiseur VRML97 installé. Si ce n'est pas le cas, reportez-vous aux liens fournis en bas de ce document, et lisez la section Installation.


Sommaire

Installation
Obtenir un visualiseur VRML97 :
Ce TP est conçu pour être réalisé sur des PC sous Windows car les visualiseurs disponibles sous Unix sont soit incomplets soit très peu performants. Le visualiseur utilisé sous Windows 95/98/NT4 était Cosmo Player, qui a été développé par SGI et distribué gratuitement. Le plug-in VRML actuellement installée sous XP est Cortona, développé par ParalellGraphics. Vous trouverez une liste complète d'autres visualiseurs sur le site du Web3D Consortium, éventuellement pour d'autres plates-formes.

Utiliser les exemples :
Pour réaliser les exercices, il faudra sauvegarder les fichiers VRML (extension .wrl) pour les modifier, puis les sauvegarder et les ouvrir localement.

De nombreux exemples supplémentaires sont disponibles sur les sites de ParallelGraphics (examples, showroom, 3D page of the week, ...)et du Web3D Consortium (object libraries, art galleries...), n'hésitez pas à les visiter!


Introduction
VRML (Virtual Reality Modeling Langage)
est un langage de modélisation de scènes 3D principalement destiné à être exploité sur le Web. VRML est un langage descriptif et non pas un langage de programmation. Cela signifie que vous pouvez décrire des environnements virtuels dans un fichier texte, mais que vous ne vous occupez pas de la manière dont cette description est ensuite utilisée pour générer les images à l'écran. Tout le niveau mise en oeuvre est à la charge du viewer 3D, ce qui explique les différences importantes de visualisation des mêmes applications. Tandis que la version 1.0 du langage ne permettait que la description d'environnements statiques, VRML97 (version standardisée de VRML 2.0) permet en plus de décrire dans une certaine mesure des comportements dynamiques (animations) et plus d'interaction entre l'utilisateur et la scène.
Conventions géométriques :
Par convention, les repères spatiaux sont orthonormés directs, et l'utilisateur fait face au plan (X,Y). Donc par defaut, on a l'axe X vers la droite, l'axe Y vers le haut, et l'axe Z vers soi.
Structure d'un fichier VRML97 :
Un fichier VRML 97 est un fichier texte, en général d'extension .wrl.



Primitives géométriques.
VRML97 reconnaît 4 objets de base : boîtes, cônes, cylindres et sphères.
Ces objets sont obtenus grâce à 4 noeuds du langage dont voici la syntaxe. Tous les paramètres présents dans les noeuds sont optionnels, c'est à dire que s'ils sont absents, des valeurs par défaut sont prises (ce sont les valeurs indiquées ci-dessous). Vous remarquerez que ces valeurs par default produisent des objets qui s'étendent de -1 à +1 sur les trois axes).



Exemple 1 :
Visualiser le cube, puis remplacez-le par les 3 autres primitives et visualisez le résultat. Faites ensuite la même chose en ajoutant des paramètres optionnels. Ne vous préoccupez pas du noeud Shape pour l'instant.
#VRML V2.0 utf8

Shape {
   geometry Box { }
}


Apparence des objets

Maintenant que nous avons les formes, nous allons donner une apparence aux objets. Il existe deux manières essentielles de modifier l'apparence d'un objet: définir un matériau (couleur de l'objet, manière dont il réfléchi la lumière etc.), et définir une texture (image calquée directement sur l'objet).

Définir un matériau
Il existe un noeud VRML permettant de définir un matériau:

Material { 
   ambientIntensity 0.2
   diffuseColor     0.8 0.8 0.8
   specularColor    0 0 0
   emissiveColor    0 0 0
   shininess        0.2
   transparency     0
}


Exemple 2
Amusez-vous à modifier le matériau du cube. Mais attention, chaque viewer VRML a ses limites, et tous ne supportent pas nécessairement tous les paramètres. Le rendu peut donc être partiellement infidèle.
	
#VRML V2.0 utf8

Shape {
   geometry Cone {}
}

Shape {
   appearance Appearance {
      material Material {
         diffuseColor 1 0 0.5
         ambientIntensity 0.3
         specularColor 0.2 0.6 0.2
         emissiveColor 0.2 0 0.1
         shininess 0.5
         transparency 0.3
      }
   }

   geometry Box { }
}
Définir une texture
VRML offre plusieurs noeuds permettant de définir une texture, voici les 2 principaux:




Exemple 3
Visualisez puis modifiez l'objet texturé ou le noeud de texture, et étudiez la façon dont la texture est appliquée par défaut. Notez également l'influence mutuelle des noeuds Material et ImageTexture.
#VRML V2.0 utf8

Shape {
   appearance Appearance {
      texture PixelTexture {
         image 3 3 3 
            0xffffff 0xffffff 0xffffff
            0xff0000 0x00ff00 0x0000ff
            0x111111 0x111111 0x111111
      }
	        
      material Material { 
         diffuseColor 1 0 0.5
         ambientIntensity 0.3
         specularColor 0.2 0.6 0.2
         emissiveColor 0.2 0 0.1
         shininess 0.5
         transparency 0.3
      }
  }

  geometry Box { }
}


Production des objets

Comme vous l'avez sans doute remarqué, nous n'avons jusqu'ici jamais utilisé les primitives géométriques ou les noeuds d'apparence tous seuls dans le corps du fichier VRML. Ceci s'explique par le fait qu'un objet est nécessairement défini par le couple (géométrie, apparence) qui le caractérise. VRML dispose donc des noeuds suivants:

Spécification d'apparence :
Appearance {
   material m
   texture  t
}

Les champs material et texture contiennent des noeuds tels que ceux définis précédemment. Si le champ material est nul, aucune lumière n'est prise en compte. Si le champ texture est nul, l'objet n'est pas texturé.

Spécification de forme :
Shape {
   appearance a
   geometry   g
}

Le champ appearance, si non nul, contient un noeud Appearance tel que défini précédemment. Le champ geometry contient un noeud spécifiant une géometrie d'objet, par exemple les primitives géométriques définies en première partie.

Les objets décrits en VRML ne sont en fait réellement accessibles que par l'intermédiaire d'un noeud Shape. La présence directe de primitives géométriques dans le fichier est susceptible de générer des erreurs.



Transformation et positionnement des objets

Maintenant que nous savons créer des objets et leur donner une apparence, nous allons pouvoir les transformer et les positionner dans la scène 3D.

Les objets précédents peuvent être manipulés :
par les opérations géométriques usuelles de translation, rotation et homothétie. Il existe un noeud du langage permettant de procéder simultanément au positionnement (translation, rotation) et au redimensionnement (homothétie) des formes géométriques:

Transform {
   center 0 0 0
   translation 0 0 0
   rotation 0 0 1 0
   scale 1 1 1
   scaleOrientation 0 0 1 0
   children [ ... ]
}

Ce noeud fait partie de ce qu'on appelle les noeuds de groupage. Ce sont des noeuds qui ont la propriété de pouvoir contenir d'autres noeuds du langage au niveau de leurs paramètres. Ici, le paramètre children contient d'autres noeuds (en particulier des Shape), auxquels les transformations géométriques seront appliquées.

Les transformations géométriques effectuées par ce noeud sont les suivantes:



Exemple 4
Manipulez les différents paramètres du noeud Transform, et notez comment ce noeud permet de séparer les caractéristiques assignées à chaque objet, non seulement les formes géométriques, mais aussi les apparences.
#VRML V2.0 utf8

Transform {
   translation -2 0 0
   children [ 
      Shape { 
         appearance Appearance { 
            material Material {
               diffuseColor 0 0.8 0.1
               transparency 0.2
            }
         }

         geometry Box { }
      } 
   ]
}

Transform {
   translation 2 0 0
      children [ 
         Shape { 
            appearance Appearance { 
               material Material {
                  diffuseColor 0 0.1 0.8
               }
            }

            geometry Cone { }
         }
      ]
}
Pour mieux comprendre le noeud Transform...

Considérons que les opérations géométriques effectuées se déroulent dans l'ordre suivant:

Transform {
   translation T
   rotation R
   scaleFactor S
   scaleOrientation SR
   center C
}
<==> - Translation de -C
- Rotation de -RS
- Homothétie de S
- Rotation de RS
- Rotation de R
- Translation de C
- Translation de T



Lumière !

En général, les visualiseurs s'occupent de mettre une lumière ambiante dans les scènes, et offrent souvent la possibilité de mettre une head light, c'est-à-dire une lampe collée sur votre front ! Il peut parfois être utile de définir soi-même ses propres sources de lumière. Il en existe trois :

Source ponctuelle
Ce noeud définit une source de lumière en un point donné, et qui rayonne de manière isotrope. En voici la syntaxe:
PointLight {
   intensity 1
   color 1 1 1
   location 0 0 0
   radius 100
   attenuation 1 0 0
   ambientIntensity 0
}



Projecteur
Ce noeud produit une lumière de type projecteur, dans un cône. La syntaxe est la suivante :
SpotLight {
   intensity 1
   direction 0 0 -1
   color 1 1 1
   location 0 0 0
   radius 100
   attenuation 1 0 0
   ambientIntensity 0
   beamWidth 1.570796
   cutOffAngle 0.785398
}



Source directionnelle
Ce noeud définit une source de lumière illuminant la scène selon des raies parallèles. La syntaxe suit:
DirectionalLight {
   direction x y z
   color r v b
   intensity i
   ambientIntensity ai
}
Exemple 5
Testez les différentes sources de lumières existant. Modifiez éventuellement le Material pour l'objet illuminé.
#VRML V2.0 utf8

SpotLight {
   location 2 0 0
   direction -1 0 0
}

Shape {
   appearance Appearance { 
      material Material {
         diffuseColor 0 0.1 0.8
      }  
   }

   geometry Sphere { }
}


Animation
VRML97 permet d'animer les objets de façon plus ou moins complexe:

Dans ce tutoriel, nous nous en tiendrons au mécanisme d'événements pour l'animation en VRML. Nous proposons en particulier des animations simples mettant en jeu des déplacements d'objets et des changements de couleur.

Une animation est déclenchée par un événement utilisateur récupéré par des capteurs spécifiques (détection d'un clic souris) ou par une horloge. Pour lisser une animation décrite par un ensemble de valeurs, on utilise des interpolateurs.


Les événements
Un événement est un message qui contient une valeur d'un certain type (le type du champ considéré). Chaque noeud VRML est composé de champs qui peuvent être:
On connecte un événement en sortie d'un noeud à un événement en entrée du même type d'un autre noeud par une ROUTE. Pour pouvoir créer une ROUTE, il faut avoir nommé les noeuds concernés (cf. DEF et USE).

Les horloges

Un TimeSensor permet de générer des événements correspondant à des tics d'horloge à intervalles réguliers.

Définition du TimeSensor:

TimeSensor {
   exposedField SFTime cycleInterval 1.0
   exposedField SFBool enabled TRUE
   exposedField SFBool loop FALSE
   exposedField SFTime startTime 0.0
   exposedField SFTime stopTime 0.0
   eventOut SFTime cycleTime
   eventOut SFFloat fraction_changed
   eventOut SFBool isActive
   eventOut SFTime time
}
Le champ cycleInterval détermine la durée d'un intervalle de temps en secondes (par défaut, une seconde). Le booléen enabled permet d'activer l'horloge tandis que loop permet de génèrer continuellement des événements au lieu de s'arrêter au premier intervalle. Les champs startTime et stopTime permettent de définir les dates de début et de fin de génération d'événements à partir du début de la scène. Les événements en sortie sont utilisés pour créer des animations; en particulier, l'événement fraction_changed pourra être récupéré par un interpolateur.


Les interpolateurs

Un interpolateur permet de lisser les animations en générant les valeurs intermédiaires à partir d'une liste de valeurs-clés. Les animations de ce type sont appelées keyframed animations car on utilise des instants-clés que l'on associe à des valeurs-clés pour définir l'animation, puis c'est au moteur 3D de faire l'interpolation.

Exemple: le PositionInterpolator:

PositionInterpolator {
   eventIn SFFloat set_fraction
   exposedField MFFloat key []
   exposedField MFVec3f keyValue []
   eventOut SFVec3f value_changed
}
Le champ key est la liste des instants-clés de l'animation (ici, un déplacement), et le champ keyValue est la liste correspondante des valeurs-clés (ici, des positions). L'événement en entrée set_fraction permet de récupérer les fractions de temps générées par un TimeSensor, et l'événement en sortie value_changed renvoie la valeur-clé ou la valeur interpolée correspondant à la fraction de temps reçue.


Exemple 6
Visualisez le cône animé, puis modifiez l'intervalle des horloges et les valeurs des interpolateurs pour transformer l'animation.
#VRML V2.0 utf8

DEF CONE Transform {
   children [
      Shape {
         appearance Appearance {
            material DEF CONE_COLOR Material {
               diffuseColor 1.0 1.0 0.0
            }
         }

         geometry Cone {}
     }
   ]
}

DEF POSITION_CLOCK TimeSensor {
   cycleInterval 6
   loop TRUE
   stopTime -1
}

DEF POSITIONS PositionInterpolator {
   key [ 0.0 0.25 0.5 0.75 1.0 ]
   keyValue [ -3.0 0.0 0.0,
               0.0 1.0 0.0,
               3.0 0.0 0.0, 
               0.0 1.0 0.0,
	      -3.0 0.0 0.0 ]
}

ROUTE POSITION_CLOCK.fraction_changed TO POSITIONS.set_fraction
ROUTE POSITIONS.value_changed TO CONE.translation

DEF COLOR_CLOCK TimeSensor {
   cycleInterval 2
   loop TRUE
   stopTime -1
}

DEF COLORS ColorInterpolator {
  key [ 0 0.33 0.66 1 ]
  keyValue [ 1.0 1.0 1.0,
	     1.0 0.0 0.0,
	     1.0 1.0 0.0,
	     1.0 1.0 1.0 ]
}

ROUTE COLOR_CLOCK.fraction_changed TO COLORS.set_fraction
ROUTE COLORS.value_changed TO CONE_COLOR.diffuseColor
Les capteurs

Il existe plusieurs types de capteurs des événements utilisateur, mais les plus intéressants sont le TouchSensor, qui capte en particulier les clics souris sur un objet, et le ProximitySensor, qui permet de détecter la présence de l'avatar (représentation de l'utilisateur dans la scène) à une certaine distance d'un objet. Grâce à ces capteurs, on peut par exemple cliquer sur une lampe pour l'allumer ou faire s'ouvrir une porte automatiquement dès que l'utilisateur s'en approche.


Exemple 7
Si le viewer VRML le permet, vous pourrez visualisez cette animation simple: en cliquant sur le cône correspondant à la lampe, vous verrez le cube s'éclairer.

Remarque: essayez de comprendre pourquoi la lumière s'allume quand on clique, mais s'éteint lorsqu'on relâche le bouton de la souris...
#VRML V2.0 utf8

Transform {
   children [
      Shape {
         geometry Cone { 
            bottom FALSE 
         }
         appearance Appearance {
            material Material {
               diffuseColor 1.0 0.0 0.0
            }
         }
      },

      DEF LIGHT SpotLight { 
         on FALSE 
         color 1.0 1.0 0.0
      }, 

      DEF LIGHT_SENSOR TouchSensor {}
   ]

   translation 0.0 3.0 0.0
}

Shape { 
   geometry Box {} 
   appearance Appearance { 
      material Material {
         diffuseColor 0.0 0.0 1.0 
      }
   }
}

ROUTE LIGHT_SENSOR.isActive TO LIGHT.on


Quelques autres noeuds...

Voici en vrac quelques autres noeuds intéressants. Attention, tous ne sont pas nécessairement supportés par le viewer que vous utilisez...

Ancres WWW
Anchor {
   url []
   description ""
   children []
}


Billboard
Le billboard est un noeud de groupage qui oblige chaque géométrie descendante à rester tournée vers l'utilisateur, quelle que soit sa position. Cela peut être utile pour représenter par exemple un arbre comme une surface plane texturée restant toujours visible sous le même angle.
Billboard {
   axisOfRotation 0 1 0
   children []
}


Collisions
Par défaut, l'utilisateur n'est pas censé pouvoir pénétrer à l'interieur des objets. Il entre en collision avec. Le noeud Collision permet de désactiver ce comportement.
Collision {
   collide TRUE
   children []
}


Texte
Text {
   string []
   maxExtent 0.0
}


Un exemple complet

Un bon bout de code vaut mieux qu'un long discours... voici donc en cadeau une tête de clown, avec presque tous les noeuds étudiés dans ce tutoriel. Triturez, déformez, cliquez-lui sur le nez...



Conclusions

Pour conclure ce tutoriel, nous voudrions faire les remarques suivantes :


Last modified: Wed 1 Jun 2005