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:
- ambientColor: c'est la couleur que renverra l'objet lorsqu'il sera éclairé
par une lumière ambiante blanche. On peut préciser que,
dans notre exemple, toutes les lumières ont été choisies
blanche car cela correspond le mieux au modèle réel des lumières
comme le soleil ou n'importe quelle lampe. Si vous optez pour une autre couleur
il va falloir que vous sachiez certaines choses d'abord. Nous y reviendrons
un peu plus loin.
- emissiveColor: c'est la lumière que l'objet emet. On l'utilise pour
les lampes ... Elle ne nous intéresse que très peu ici.
- diffuseColor: il s'agit de la couleur la plus importante pour nous car c'est
la plus proche de notre définition "classique" de la couleur
d'un objet. Elle indique la teinte d'un objet lorsqu'il est éclairé.
- specularColor: cette couleur est utilisée pour les effets de brillance
sur les objets. Elle décrit en fait les "tâches" de
lumières qui apparaissent sur du métal éclairé
par exemple.
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:
- AmbientLight: ce sont des lumières ambiantes qui éclairent
indifférement toute la scène sans direction ou point d'émission.
Elle permette un éclairage globale mais ne donne pas vraiment de volume
à vos géometries car toutes les faces sont illuminées
identiquement. Son constructeur est simplement : "public AmbientLight(Color3f
color)".
- DirectionnalLight: cette lumière possède un vecteur (un objet
Vector3f) qui indique son sens et sa direction. Il faut savoir que l'influence
d'une lumière appliquée sur une face est calculée en
fonction du vecteur direction de la lumière et du vecteur normal à
cette surface. Par conséquent, tous les points d'un plan orthogonal
à ce vecteur seront éclairés de la même manière
par une DirectionnalLight car son vecteur direction est uniforme. Son constructeur
le plus utilisé est: "public DirectionalLight(Color3f color,
Vector3f direction)".
- PointLight: c'est une lumière plus complexe mais qui permet de sympathiques
effets lumineux. Cependant, elle génère plus de calcul que les
lumières précedentes, il faut donc les utiliser avec parcimonie.
Comme son nom l'indique, cet objet simule l'éclairage à partir
d'un point: les rayons lumineux ont donc une direction radiale autour de ce
point. Son principal constructeur est :"public PointLight(Color3f
color, Point3f position, Point3f attenuation)". En plus du point
d'émission et de la couleur, cette méthode spécifie un
Point3f pour l'atténuation. En fait, il ne s'agit pas réellement
d'un point mais d'une forme quadratique de type a+bx+cx^2 (pour un point (a,b,c))
qui représente une fonction d'atténuation de l'influence de
cette lumière en fonction de la distance de l'objet à éclairer.
Rien de mieux pour bien comprendre l'utilité de cette fonction, que
de faire quelques tests. On peut remarquer que cette classe possède
une sous-classe, SpotLight, qui en plus permet de restreindre les rayons lumineux
dans un cône de rayon donné (le résultat serait celui
d'une lampe torche).
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 |
- 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.
- 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.
- 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.
- 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
.