
par Francis Lapique, SIC
VRML est l'acronyme de Virtual
Reality Modeling Language. VRML est un langage de modélisation de réalité
virtuelle qui permet le déploiement d'univers tridimensionnels sur Internet.
VRML n'est ni un langage de programmation comme C ou Java, ni un langage de
marque comme HTML. C'est un langage de modélisation qui permet d'organiser une scène sous la forme d'une collection d'objets 3D ou noeuds
disposés suivant une structure hiérarchique ayant une représentation d'arbre. Une série d'événements vont parcourir ces
arbres sautant de noeuds en noeuds et donner vie à votre scène. Cette référence à la vie et au titre de cet article, emprunté à
Mark Pesce, nous font percevoir VRML comme la brique élémentaire d'un monde virtuel qui se construit sous nos yeux
avec l'objectif de couvrir la planète.
C'est un langage plus simple qu'un langage de programmation (si on exclut les script nodes) comme C ou Java et plus difficile que l'approche HTML.
Quelques références utiles sous forme papier et électronique. Commençons par le papier:
VRML 2.0 Sourcebook, Andrea L. Ames, David R. Nadeau, John L.Moreland of the San Diego SuperComputer Center, 1997 John Wiley & Sons
The VRML 2.0 Handbook, Jed Hartman, Josie Wernecke of Silicon Graphics, 96 Addison Wesley
Pour ce qui concerne les documents électroniques:
Le centre de San Diego Supercomputer
Le site VRML de Silicon:
Si vous voulez prendre contact avec le consortium VRML:
Ici à l'EPFL la spécification de VRML2.0 sous la forme du document The Virtual Reality Modeling Language Specification
Un excellent tutorial dont je me suis inspiré :
En mai 1994, se tenait à Genève la première conférence WWW. Mark Pesce et Tony Parisi forts d'une première expérience avec Labyrinth présentent leur papier Cyberspace. Dave Raggett et Tim Berners-Lee mettaient au programme une session BOF (Birds-of-a-Feather) intitulé Compositional Graphics and Virtual Reality. Il y est décidé de décrire les spécifications d'un langage de description de scène (Virtual Reality Markup Language; le terme Markup avait été proposé au début pour sa similitude avec HyperText Markup Language).
Pesce et Brian Behlendorf du magazine Wired mettent en place une liste de discussion qui rencontre après une semaine d'existence un très vif succès. Silicon Graphics s'implique dans le projet VRML à travers un ingénieur de développement Gavin Bell (http://www.mailbag.com/users/gavin/) travaillant sur Inventor, sorte de boîte à outils orientée objet pour manipuler interactivement des objets 3D. Ce dernier demande à son manager Rikk Carey des informations sur VRML et le convainc de l'urgence de définir rapidement un langage de description de scènes pour le Web. Bell en octobre de cette même année, au cours de la seconde conférence WWW, propose à la jeune communauté VRML un projet qui n'est qu'une version corrigée (pour tenir compte de l'aspect réseau) de la forme ASCII d' Open Inventor. Silicon décide de mettre le travail de Bell dans le domaine public. La fin de cette année 94 voit la sortie d'un premier analyseur syntaxique ou parser VRML qui porte l'étrange nom de QvLib.
Le début de l'année 1995 voit la sortie de WebSpace, premier visualiseur ou browser 3D pour le monde Silicon. Template Graphics Software prend en charge l'écriture de WebSpace pour d'autres plates-formes. Les browsers fleurissent: Worldview d'Intervista, EZ3D, VRweb, etc. Les spécifications définitives de VRML1.0 sont disponibles en mai 95. Fin 95 le VRML Architecture Group (VAG) qui s'était mis en place en août, lance la discussion d'une prochaine révision. Des propositions comme Moving Worlds de Silicon, HoloWeb de Sun, ActiveVRML de Microsoft, Out of This d'Apple, Reactive Virtual Environment d'IBM Japon et d'autres sont discutées sur la place publique. En mars 96, les participants à la liste de discussion arrivent à un consensus et plébiscitent Moving Worlds (70% des votes). Le VAG adopte la proposition comme VRML 2.0 et publie en août les spécifications définitives du langage (voir références utiles).
VRML 1.0 s'est contenté d'une description statique d'un monde tridimensionnel. On y trouvait les prémisses de VRML 2.0 avec sa structure hiérarchique de noeuds, les formes géométriques de base (cube, cône, sphère), le texte, les objets à facettes polygonales, l'aspect (couleur, brillance, etc.), la texture mappée, les groupes d'objets, les transformations (translation, rotation, échelle), le contrôle du niveau de détails, l'intégration d'un objet externe, les liens vers, les instanciations d'objets et des spécificités propriétaires.
VRML 2.0 devient dynamique avec l'introduction des capteurs (de proximité, de toucher, de temps...), de la détection de collisions, d'une nouvelle structure hiérarchique, de nouveaux éléments de base (extrusion, fonds de scène, brouillard, terrain, son spatial), d'animation par interpolation, d'animations complexes par programmes (liaison vers JavaScript, Java...) capables de capter des événements et d'en émettre, et enfin une notion de prototypes pour définir de nouveaux types de données.
Dans le monde des langages de programmation il est d'usage de décrire un programme affichant Hello World sur votre écran. Comme VRML 2.0 n'est pas un langage de programmation mais de description de scène 3D pour le Web, nous allons afficher un cône, une sphère et un cube dans une fenêtre de votre browser.

Le fichier commence obligatoirement par l'en-tête suivant:
#VRML V2.0 utf8
Cet en-tête doit se présenter exactement sous cette forme (attention au V majuscule). Le signe (#) indique un commentaire, V2.0 la version et utf8 la règle d'encodage des caractères internationaux. Ensuite nous définissons un noeud Group:
GROUP {
Rappelons qu'une scène VRML est une collection de noeuds disposés suivant une arborescence. Nous introduisons ici cette arborescence sous la forme d'un noeud Group qui va nous permettre de traiter tout ce qui suit comme une seule entité. A titre indicatif voici la spécification de ce noeud.
Group {
eventIn MFNode addChildren
eventIn MFNode removeChildren
exposedField MFNode children []
field SFVec3f bboxCenter 0 0 0
field SFVec3f bboxSize -1 -1 -1
}
Ce noeud comme tous les noeuds VRML 2.0 compte un type pour l'identifier et des champs (field) statiques (ici bboxCenter, bboxSize) et des champs dynamiques (children, addChildren..). Il est inutile d'en dire plus pour le moment.
Les enfants de ce groupe sont introduits par le champ children
children [
On poursuit en introduisant le noeud Shape qui va prendre en charge le rendu de notre scène.
Shape {
La spécification de ce noeud:
Shape {
exposedField SFNode appearance NULL
exposedField SFNode geometry NULL
}
fait apparaître un champ geometry qui va nous permettre d'introduire une géométrie qui sera dans notre exemple une boîte.
geometry Box { }
Nous terminons notre programme par une première parenthèse pour indiquer la fin du noeud Shape, une accolade pour indiquer celle de children et une deuxième parenthèse pour le noeud Group.
}
]
}
Si nous essayons ce petit programme nous allons voir une boîte grise sur fond noir ou une boîte noire sur fond noir, ce qui n'est pas idéal. Il faut colorer notre scène. Le champ appearance du noeud Shape est justement fait pour cette tâche. Ce champ appearance est de type SFNode ce qui signifie Single-valued fields Node. Nous allons donc attacher à ce champ appearance un noeud Appearance (je sais que c'est un peu troublant la première fois). Voici la spécification du noeud Appearance
Appearance {
exposedField SFNode material NULL
exposedField SFNode texture NULL
exposedField SFNode textureTransform NULL
}
Mais nous ne voyons toujours pas comment nous allons colorier notre boîte. Ce que nous allons faire c'est préciser le choix d'une matière qui va diffuser une couleur donnée. Pour ce faire nous avons un noeud Material et un champ diffuseColor auquel nous affectons une valeur RGB qui est ici rouge:
Shape {
appearance Appearance {
material Material { diffuseColor 1 0 0 }
}
geometry Box { }
}
Il faut positionner notre boîte dans la scène. Par défaut, elle se trouve à l'origine. Nous allons faire appel au source d'un noeud translation pour déplacer ce sous-ensemble de 5 unités (les distances se mesurent en mètres et les angles en radians) sur la droite sur l'axe horizontal (celui des X) , et de 0 unités sur l'axe des Y et Z (en VRML l'axe des Z pointe vers vous):
translation 5 0 0
children [
Shape {
appearance Appearance {
material Material {diffuseColor 1 0 0}
}
geometry Box { }
}
]
Nous arrivons au bout de nos peines en ajoutant les deux autres géométries à savoir un cône et une sphère:
Group {
children [
Transform {
translation 5 0 0
children [
Shape { ... geometry Box { } }
]
}
Transform {
translation 0 0 0
children [
Shape { ... geometry Sphere { } }
]
}
Transform {
translation -5 0 0
children [
Shape { ... geometry Cone { } }
]
}
] # end of Group children
}
Nous devez faire très attention au jeu de parenthèses et observer une très grande discipline. Sinon vous allez perdre beaucoup de temps pour corriger les erreurs de syntaxe ou pour comprendre des effets de bord qui pourraient se présenter.
Si vous voulez rendre certaines parties de votre code réutilisables, vous pouvez définir vos propres objets en les nommant au moyen de la directive DEF comme suit:
DEF box Transform { ... }
DEF sphere Transform { ... }
DEF cone Transform { ... }
L'instantiation se fait par la suite à travers la directive USE.
Voilà pour cet exemple Hello World. Nous allons aborder dans un prochain exemple, intitulé Doorbell, un aspect un peu plus avancé de VRML 2.0. C'est celui des mécanismes de capture et d'émission d'événements entre noeuds.
Avant d'entrer dans le détail de l'exemple, je vais préciser l'idée générale. Les noeuds peuvent émettrent et recevoir des
événements. Au moyen de la commande ROUTE nous allons être capable de diriger tel événement d'un noeud A sur tel événement d'un noeud B et interagir par là avec la scène.
Il faut préciser un peu plus que nous l'avons fait jusqu'à présent l'architecture d'un noeud.
Chaque noeud possède un caractère privé et un caractère public.
La partie field du noeud fait référence à sa partie privée. Les parties eventIn, eventOut font référence à sa partie publique.

SomeNode {
field SFFloat alpha 10.0
eventIn SFFloat set_radius
eventOut SFFloat radius_changed
}
La syntaxe set_radius et radius_changed indique qu'il s'agit respectivement d'événement en entrée et en sortie (préfixe set_ et suffixe _changed sont implicites). Le champ exposedField couvre les trois situations: eventIn, field et eventOut
SomeNode {
exposedField SFFloat radius
}
# equivalent to
SomeNode {
eventIn SFFloat set_radius
field SFFloat radius
eventOut SFFloat radius_changed
}
La commande ROUTE redirige les événements d'un noeud à l'autre comme suit:
DEF NODE1 Collision { . . . }
DEF NODE2 AudioClip { . . . }
ROUTE NODE1.CollideTime TO NODE2.startTime
Notez que les noeuds participants à une commande ROUTE doivent être définis par DEF.

Quelques remarques concernant cette commande ROUTE. Ce n'est pas un noeud. Les types d'événements doivent être identiques. On ne peut pas envoyer un type SFFloat sur un type SFColor par exemple. On peut envoyer un eventOut sur plusieurs eventIn (fan-out), l'inverse (fan-in) est également possible. Regardons comment les choses se passent avec le noeud TouchSensor qui est un capteur sensible au toucher. Sa spécification est la suivante:
TouchSensor {
exposedField SFBool enabled TRUE
eventOut SFVec3f hitNormal_changed
eventOut SFVec3f hitPoint_changed
eventOut SFVec2f hitTexCoord_changed
eventOut SFBool isActive
eventOut SFBool isOver
eventOut SFTime touchTime
}
Chaque fois que vous pointez ce noeud vous émettez un événement isOver avec TRUE comme valeur. Si vous pointez et pressez le noeud vous émettez isActive dans l'état TRUE. Si vous pointez, pressez et libérez cette pression vous émettez
touchTime, avec comme valeur, la durée de la pression. Nous laisserons de côté le reste des événements. Notre exemple Doorbell est une des animations les plus simples possibles. Nous voulons déclencher une sonnerie en cliquant une géométrie que nous allons charger dynamiquement. Voici l'exemple complet:
#VRML V2.0 utf8
Group {
children [
Group {
children [
DEF PUSH TouchSensor {
}
Inline {
url 'doorbell.geom.wrl'
}
]
}
Sound {
source DEF BUZZ AudioClip {
url 'doorbell.wav' }
minFront 5
maxFront 50
}
]
}
ROUTE PUSH.touchTime TO BUZZ.set_startTime
Nous avons associé un TouchSensor, intitulé PUSH, à une géométrie qui a été chargée par la commande Inline. Nous avons également une bande son au format wav. Cette bande son fait partie d'un noeud AudioClip nommé BUZZ. Ce noeud attend un événement startTime pour lancer le clip audio. Pour réaliser l'effet on raccorde les deux événements via la commande ROUTE. Il n'est pas question dans le cadre de cet article d'aborder des animations plus avancées qu'il serait trop long de commenter. C'est avec regret que nous laissons de côté un tas d'autres capteurs (CylinderSensor, PlaneSensor, ProximitySensor, SphereSensor, TimeSensor, VisibilitySensor) tout comme les animations par interpolation (ColorInterpolator, CoordinateInterpolator, NormalInterpolator, OrientationInterpolator, PositionInterpolator, ScalarInterpolator). Poursuivons avec quelque chose d'essentiel le Script Node.
Le Script Node est la porte vers des animations et interactions complexes en allant au-delà des possibilités standards des capteurs et animation par interpolation. Vous allez pouvoir:
traiter les données des capteurs et générer des données d'animation
entrer en interaction avec le browser (demander au browser de suivre un Viewpoint Node pour animer le viewer suivant un chemin)
manipuler le graphe de la scène (ajouter ou supprimer des groupes de noeuds)
communiquer avec un serveur ou un autre monde VRML via le réseau
Comme les autres noeuds, un Script capte et émet des événements. La grande différence c'est que vous allez pouvoir faire un traitement de ces événements au moyen d'un script ou programme. Un browser ne pourra évidemment pas exécuter un script dans un langage qu'il ne connait pas. Pour un maximum de portabilité il est conseillé, pour le moment, d'écrire ces Scripts en JavaScript ou Java. Je vais montrer un exemple en JavaScript. La spécification de ce Script Node est la suivante:
Script {
exposedField MFString url []
field SFBool directOutput FALSE
field SFBool mustEvaluate FALSE
# And any number of:
eventIn eventTypeName eventName
field fieldTypeName fieldName initialValue
eventOut eventTypeName eventName
}
Le champ url est essentiel. C'est lui qui donne le chemin d'accès au Script:
Script
{
url «circle.class»
ou...
url «circle.js»
ou...
url «javascript:function ...»
}
Les champs directOutput et mustEvaluate interviennent pour améliorer la performance. La première chose à faire est de décrire l'interface du Script, c'est à dire donner la liste des événements qui stimulent notre noeud (notez que l'on ne peut pas définir d'exposedField):
Script {
field SFFloat cycles 1.0
eventIn SFFloat set_fraction
eventOut SFVec3f position_changed
}
Ensuite il faut préciser le traitement de ces événements. On va le faire en écrivant une fonction par type d'événement:
function set_fraction( f, tm ) {
ang = f*360*cycles / 180*3.14;
position_changed[0] = f;
position_changed[1] = Math.sin( ang );
position_changed[2] = 0.0;
}
Sur ce dernier exemple set_fraction est le nom de l'événement (c'est un événement de type input, set_quelquechose) et le couple (f ,tm) contient des valeurs associées à cet événement. La fonction émet un événement position qui est un vecteur de dimension trois. C'est le navigateur qui en donnant la main au Script va déclencher cette fonction pour traiter ce type d'événement.
Il existe un certain nombre de fonctions spéciales comme initialize(), eventsProcessed(), shutdown (), que le browser appelle automatiquement pour certaines opérations. Par exemple si vous définissez une fonction initialize() le navigateur appelle cette fonction au moment du chargement du Script avant tout traitement d'événements.
Analysons un exemple complet qui consiste à animer une petite bille de couleur rouge suivant une sinusoïde. Nous écrivons un Script Mover qui accepte en entrée un événement anim et qui génére en sortie un événement position. Cet événement position est un vecteur de translation dans le plan X-Y. Sur l'axe des X nous avons le temps qui s'écoule et la valeur du sinus sur l'axe des Y:
DEF Mover Script {
field SFFloat cycles 1.0
eventIn SFFloat set_anim
eventOut SFVec3f position_changed
url «vrmlscript:
function set_anim(f,tm) {
position_changed[0] = f;
position_changed[1] =
Math.sin( ((f*360)*cycles)/180.0 * 3.1415927 );
position_changed[2] = 0.0;
}»
}
Pour la génération du temps, on va se servir d'un noeud Clock qui est un TimeSensor :
DEF Clock TimeSensor {
cycleInterval 5.0
loop TRUE
startTime 1.0
stopTime 0.0
},
Sans rentrer trop dans le détail de ce noeud, il répète à l'infini des cycles de 5 secondes. Pendant chacun de ces cycles, il émet un événement fraction qui indique la fraction de cycle passée. Le dernier noeud à définir c'est notre bille rouge que nous allons déplacer dans le temps. On nomme ce noeud MoveMe et comme il est de type Transform, on va pouvoir lui appliquer une transformation qui dans notre exemple est une translation (événement exposedField SFVec3f translation)
DEF MoveMe Transform {
children Shape {
appearance Appearance {
material Material {
diffuseColor 1.0 0.0 0.0
}
}
geometry Sphere { radius 0.1 }
}
Nous arrivons au terme de notre travail en spécifiant le routage des événements:
ROUTE Clock.fraction_changed TO Mover.set_anim ROUTE Mover.position_changed TO MoveMe.set_translation
Le noeud Clock déclenche le Script Mover qui lui-même déclenche la translation du noeud MoveMe. Avant de conclure il reste encore un dernier aspect de VRML 2.0 qu'il serait dommage de passer sous silence. Il s'agit de la notion de PROTO, abrégé de prototypes.
Vous pouvez créer vos propres noeuds, combinaisons à l'infini de types existants comme:
Shapes
Sensors
Interpolators
Scripts
n'importe quoi d'autre...
Ce qui est donc offert, c'est la faculté de vous construire des bibliothèques de haut-niveau pour le domaine qui est le vôtre. L'interface d'un PROTO n'a rien de particulier. Elle compte un nom, des champs et des événements:
PROTO Robot [
field MFString name «Bill»
field SFBool enabled TRUE
eventIn SFBool set_enabled
eventOut SFVec3f position_changed
] { . . . }
Le corps du PROTO va contenir le graphe de votre scène et les ROUTE qui lui sont associées. Chaque field (et event) que vous déclarez au niveau de l'interface doit trouver son correspondant field (event) dans le corps du PROTO.
PROTO Robot [ . . . ] {
Group {
children [ . . . ]
}
DEF Animator Script { . . . }
ROUTE . . .
}
Pour ce faire vous utilisez la syntaxe IS:
PROTO Column [
field SFColor columnColor 0.5 0.5 0.5
]
{
Shape {
appearance Appearance {
material Material {
diffuseColor IS columnColor
diffuseColor 0.8 0.8 0.8
}
texture NULL
textureTransform NULL
}
geometry Cylinder {
}
}
La ligne diffuseColor IS columnColor indique que la valeur du champ columnColor du prototype est à prendre comme valeur du champ diffuseColor du noeud Material. Vous pouvez également utiliser la syntaxe IS pour passer des événements:
PROTO Robot [
eventOut SFVec3f position_changed
] {
Script {
eventOut SFVec3f position_changed
IS position_changed
}
}
L'utilisation d'un PROTO est identique à celui de n'importe quel noeud:
DEF Terminator Robot {
name «Terminator»
}
ROUTE Proximity.isActive TO Terminator.set_enabled
Vous trouverez l'explication d'un exemple complet PROTO+Script à l'adresse suivante:
Dans cet exemple vous verrez comment balayer un faisceau lumineux au-dessus d'un plan.
Différents environnements:
dans le monde PC, 3D Studio Max avec un plug-in VRML,
WorldView chez Intervista:
Internet3D chez ParaGraph:
dans le monde Silicon Graphics CosmoWorlds.
Avec ce dernier éditeur vous construisez le graphe de votre scène et vous réalisez le routage en reliant l'entrée de certain noeud sur la sortie d'autres.
un produit chez Alias/wavefront qui porte le nom Web|Animator:
vous pouvez visiter dimension X et leur produit Liquid Reality:
ainsi que celui d'OnLive:
pour faire intervenir des avatars, habitants de ces mondes virtuels.
VRML 2.0 ouvre de nouveaux horizons quant au contenu et aux applications dédiées au Web, tels que cyberville, travail à distance coopératif, gestion d'unités de production et visualisation des bases de données.
Des serveurs Web d'un second type vont réaliser la fusion des technologies VRML avec ses avatars, Java et SQL étendu. Il est clair que ces serveurs de second type seront et feront plus que la somme de ces trois technologies.Une dernière remarque concernant les fameux browsers. Les choses se sont singulièrement compliquées pour eux. On est loin du simple affichage de page HTML. L'animation d'une scène VRML2.0 comptant texture, script Java et intervention d'avatars font qu'on leur demande beaucoup (vous pouvez mettre à l'épreuve votre machine et votre browser avec l'exemple suivant (décrit en détail dans le VRML 2.0 Handbook):
Un certain nombre de questions comme, le protocole httpd doit-il évoluer?, quelle place faut-il donner à Java ? sont
posées.
A signaler: l'installation récente dans la région lausannoise d'une société spécialisée dans les développements VRML (www.artemedia.ch). tiré de son intervention à la conférence The VRML 2.0 Developer's Conférence, janv. 30-31, San Francisco
| retour au sommaire du Flash informatique no 3/97 |
| retour à la page principale des Flash informatique |
| Vos commentaires |
| © FI-3 du 23 mars1997 |