Java3D Tutoriel:

Chapitre 5: Introduction aux lumières

Pré-requis : Chapitre 4: Textures

Nous contacter 

Voici un point qui ne manquera pas d'intéresser les plus curieux d'entre vous. En effet, quoi de plus beau qu'une scène illuminée avec gôut? Vous pourrez enfin utiliser des effets d'ombrage métant en valeur les formes de vos géometries.


Exemple 1, lumières et textures sur une Box

Télécharger le code complet

Cet exemple a le mérite de présenter simplement les différentes lumières ainsi que leurs effets sur différents types de formes (avec ou sans texture, avec différentes propriétes pour l'objet Material ...). Vous apprendrez donc ici les bases de la gestion des sources lumineuses en Java3D. Cependant, les combinaisons possibles sont multiples et toutes les présenter serait fastidieux et inutile, la meilleure manière de travailler cet exemple sera donc de le paramétrer suivants vos choix et de constater le résultat. Nous essayerons donc de vous fournir les points clefs à connaître pour comprendre le rendu de Java3D en fonction de vos réglages.

Extraits de code

Extrait 1, configurer l'objet :


//material -> uniquement si il y a des lumières
Material mat=new Material
(new Color3f(1f,1f,0f)
,new Color3f(0f,0f,0f)
,new Color3f(1f,1f,0f)
,new Color3f(1f,1f,1f)
,64);
boxApp.setMaterial(mat);

Extrait 2, le code d'implémentation des différentes lumières:


//lumières
if(ambientBool)
{
AmbientLight ambLi=new AmbientLight(new Color3f(1f,1f,1f));
ambLi.setInfluencingBounds(new BoundingSphere(new Point3d(),150d));
myScene.addChild(ambLi);
}
if(directBool)
{

DirectionalLight diLi=new DirectionalLight();
diLi.setDirection(new Vector3f(0f,0f,-1f));
diLi.setInfluencingBounds(new BoundingSphere(new Point3d(),150d));
diLi.setColor(new Color3f(1f,1f,1f));
myScene.addChild(diLi);

}
if(pointBool)
{

PointLight pointLi=new PointLight
(new Color3f(1f,1f,1f)
,new Point3f(2f,2f,2f)
,new Point3f(1f,0f,0f));
pointLi.setInfluencingBounds(new BoundingSphere(new Point3d(),150d));
myScene.addChild(pointLi);

}


Extrait 3, adjonction de la texture à l'Appearance:
void addTexture(Appearance app,String textureName)
{

TextureLoader loader=new TextureLoader(textureName,this);
ImageComponent2D image=loader.getImage();

Texture2D texture=new Texture2D(Texture.BASE_LEVEL,Texture.RGBA,image.getWidth(),image.getHeight());
texture.setImage(0, image);
texture.setEnable(true);
texture.setMagFilter(Texture.BASE_LEVEL_LINEAR);
texture.setMinFilter(Texture.BASE_LEVEL_LINEAR);
app.setTexture(texture);
TextureAttributes textureAt=new TextureAttributes();
textureAt.setTextureMode(TextureAttributes.MODULATE);
app.setTextureAttributes(textureAt);

}


Commentaire du code

Extrait 1, comment paramétrer l'objet pour le rendre réactif aux lumières :

Première chose très importante, il y a un paramètre supplémentaire à activer pour que la Box, ou tout autre objets primitifs du Java3D d'ailleurs, soit sensible aux rayons lumineux: il faut ajouter la génération sur chaque face des normals par rapport auxquelles seront effectuées les calculs. D'où le constructeur: "Box box=new Box (0.4f, 0.4f ,0.4f , Box. GENERATE_ TEXTURE_OORDSBox.GENERATE_NORMALS,boxApp);".

Il va nous falloir maintenant comprendre comment sont gérées les lumières au niveau de nos objets 3D. Pour cela, introduisons la classe Material. A chaque Shape3D, on peut associer par le biais de la méthode setMaterial(Material mat) un objet de type Material qui définit comment va réagir l'objet s'il est éclairé. Le constructeur de la classe Material est :"public Material(Color3f ambientColor, Color3f emissiveColor, Color3f diffuseColor, Color3f specularColor, float shininess)". On peut donc voir qu'à chaque objet nous allons associer non pas une mais quatres couleurs:

Le dernier paramètre du constructeur de Material définit simplement la brillance de l'objet avec un nombre entre 1, objet mat, et 128, objet très brillant. Nous l'avons placé à 64, ce qui d'ailleurs la valeur par défaut.

Pour notre exemple, nous avons choisi de créer l'objet avec un objet Material configuré pour que la "couleur" de l'objet box soit jaune (1f,1f,0f en RGB). Nous placons donc du jaune pour les lumières ambiantes et diffuses. On remarquera également que la couleur émissive est choisie noire, ce qui correspond à une non-émission de lumière. De plus, la lumière spéculaire est choisie blanche (1f,1f,1f), car dans la réalité c'est fréquement la cas. Par exemple, si vous éclairez un morceau de plastique avec une lumière blanche, les zones les plus exposées deviendront blanches.

Extrait 2, les différentes lumières :

Il existe plusieurs types de lumières:

Etant donné le nombre l'immense nombre de combinaisons possibles pouvant modifier le rendu d'une scène illuminée (différentes lumières, couleurs des lumières et de l'objet, caractéristiques de l'objet Appearance, ...), nous avons mis en place une méthode createScene, createScene(boolean ambientBool,boolean directBool,boolean pointBool,boolean texBool), qui accepte donc 4 paramètres booléens: dans l'ordre, un booléen pour l'absence/présence d'une lumière ambiante, deux autres pour les lumières directionnelles et ponctuelles et enfin un dernier pour charger ou non une texture sur l'objet. Ainsi, on peut rapidement voir l'effet de chaque type de lampe. Nous présentons ci-dessous quelques combinaisons que l'on peut ainsi obtenir:

1-createScene(false,false,false,false) 2-createScene(true,false,false,false), AmbiantLight
3-createScene(false,true,false,false), DirectionalLight 4-createScene(false,false,true,false), PointLight

 

  1. createScene(false,false,false,false) : contrairement à ce que l'on aurait pu penser, absolument rien n'apparait. Pourquoi ? C'est simplement dû au fait que nous avons associé à notre Box un objet Material et à partir de ce moment Java3D considère que cette primitive appartient à une scène éclairée, et comme aucune lumière n'est activée, alors la Box est dans le noir! Il faut simplement savoir que chaque objet avec une matière doit être éclairé pour être visible.
  2. createScene(true,false,false,false) : on charge maintenant une lumière ambiante blanche. L'objet apparaît alors avec sa couleur ambiante choisie jaune. Avec ce type de lumière, on ne met pas du tout en évidence le volume et les formes de notre objet car toutes les faces sont identiquement éclairée.
  3. createScene(false,true,false,false) : la Box est maintenant attaquée par un lumière directionnelle blanche qui lui fait face. La face FRONT est donc celle qui subit le plus l'effet de la lumière. On remarquera également (cela ne se voit pas sur la capture d'écran mais est très visible avec l'exemple) que quand une face du cube est totalement orientée face à la lumière, c'est alors une lumière blanche qui nous est renvoyée. Quelle en est la raison? C'est simplement que c'est alors la lumière spéculaire de notre Material, que nous avons choisi blanche, qui nous est renvoyée.
  4. createScene(false,false,true,false) : finalement, nous pouvons observer l'effet d'une lumière ponctuelle positionnée sur la droite de notre Box.

Petite remarque: nous avons choisi toutes les lumières de couleurs blanche, mais on peut bien sûr les choisir autrement. Mais il y a quelques subtilités à connaître: si vous éclairer un Shape3D de lumière ambiante bleu (0f,0f,1f) avec seulement une lumière ambiante jaune (1f,1f,0f), alors rien n'apparaîtra car le bleu ne fait pas du tout partie de la couleur de la lumière incidente. Par contre si vous éclairez un objet jaune(1f,1f,0f) avec une lumière violette (1f,0f,1f) alors l'objet semblera rouge car c'est la seule couleur émise par la lumière que peut percevoir l'objet! Nous vous laissons maintenant tester, sur cet exemple, la multitude de réglages possibles en ésperant que les explications données vous permettront de comprendre le résultat de vos expériences.

Extrait 3, lumières et texture

Seules quelques nouvelles lignes nous intéressent particulièrement: les trois dernières lignes de l'extrait. Vous pouvez essayer de lancer l'application avec le paramètre true pour texBool mais sans ces trois lignes. Le résultat est alors net: pas de jeu de lumière apparent sur notre texture ! En effet, par défaut cela n'est pas prévu. Il suffit juste de paramétrer un objet TextureAttributes qui sera associé à un objet Appearance. C'est la méthode "public void setTextureMode(int textureMode)" de TextureAttributes qui permet de combiner texture et lumières une fois placée en MODULATE.

Il me semble intéressant que vous vous renseignez dans l'Api reference au sujet de la classe TextureAttributes qui regorgent de méthodes pour affiner les réglages (toutes ne peuvent pas être abordée ici).

Voici une bonne chose de faite. Complétons juste cet exemple avec des effets de lumières sur un objet élementaire telle que notre fameuse barrière du Chapitre 4 (exemple 2).


Exemple 2, lumières sur des formes élementaires

Télécharger le code complet 

Je vous rassure tout de suite, cette partie sera plus brève et moins théorique que la précédente, les nouveautés seront également moins nombreuses.

Extraits de code

Extrait 1, les lumières au niveau de la géométrie


//vecteurs normaux pour face 1
Vector3f vect0=new Vector3f();
Point3f point0=new Point3f();
Point3f point2=new Point3f();
Point3f point1=new Point3f();
objGeom.getCoordinate(0,point0);
objGeom.getCoordinate(2,point2);
objGeom.getCoordinate(1,point1);
vect0.cross(
new Vector3f(point2.x-point0.x,point2.y-point0.y,point2.z-point0.z),
new Vector3f(point1.x-point0.x,point1.y-point0.y,point1.z-point0.z));
vect0.normalize();
objGeom.setNormal(0,vect0);
for (int i=1;i<5;i++) objGeom.setNormal(i,new Vector3f(0f,0f,1f));

//vecteurs normaux pour face 2, 3 et 4
for (int i=6;i<10;i++) objGeom.setNormal(i,new Vector3f(0f,0f,-1f));
for (int i=10;i<15;i++) objGeom.setNormal(i,new Vector3f(-1f,0f,0f));
for (int i=15;i<20;i++) objGeom.setNormal(i,new Vector3f(1f,0f,0f));

Extrait 2, les lumières au niveau de l'apparence


app.setMaterial
(new Material
(new Color3f(0.63f,0.42f,0.21f)
,new Color3f(0f,0f,0f)
,new Color3f(0.63f,0.42f,0.21f)
,new Color3f(0.63f,0.42f,0.21f)
,64));

Extrait 3, implémentation des lumières
//lumières
PointLight pointLi=new PointLight
(new Color3f(1f,1f,1f)
,new Point3f(0f,0f,0.5f)
,new Point3f(1f,0f,0f));
pointLi.setInfluencingBounds(new BoundingSphere(new Point3d(),150d));
scene.addChild(pointLi);

AmbientLight ambLi=new AmbientLight(new Color3f(1f,1f,1f));
ambLi.setInfluencingBounds(new BoundingSphere(new Point3d(),150d));
scene.addChild(ambLi);

Commentaire du code

Extrait 1, génération des normales

Comme nous l'avons précisé dans le premier exemple, les calculs pour l'éclairage d'une scène sont effectués par rapport aux vecteurs normaux des faces et aux vecteurs incidents des rayons lumineux. Seulement, dans l'exemple un, il suffisait de régler un paramètre pour que les vecteurs normaux de chaque face soient automatiquement générés; cette époque est révolue! En effet, il va maintenant vous falloir définir vous mêmes manuellement chacun de ces vecteurs. Cette tâche peut réellement être très fastidieuse, mais dans notre exemple un minimum calcul vectoriel sera nécessaire.

Les vecteurs normaux ne sont en réalité pas associés face par face mais sommet par sommet. Donc, en plus des méthodes "setCoordinate()" ,"setColor()" et "set
TextureCoordinate()
" vues précedement, il va falloir ajouter la méthode "setNormal(int vertex,Vector3f vect)".

Extrait 2, réglale du Material de l'Appearance

Extrait 3, mise en place des lumières

 

.