Programmation JavaFX/Apparence 3D
L'apparence par défaut des objets de la scène construite au chapitre précédent est plutôt monotone :
Le but de ce chapitre est de montrer comment améliorer l'apparence des objets en leur donnant une couleur ou une texture.
L'apparence d'un objet (couleur, reflets, texture, ...) est défini par une instance de la classe abstraite Material
.
JavaFX fournit une classe concrète nommée PhongMaterial
, nommée d'après le modèle d'ombrage de Phong.
Couleur
modifierLes méthodes de la classe PhongMaterial
sont les suivantes :
setDiffuseColor(Color)
- Définit la couleur principale.
setSpecularColor(Color)
- Définit la couleur des reflets des sources lumineuses.
setSpecularPower(float)
- Définit la puissance de réflexion.
Exemple
modifierPhongMaterial mat_blue = new PhongMaterial();
mat_blue.setDiffuseColor(Color.BLUE);
mat_blue.setSpecularColor(Color.LIGHTBLUE);
mat_blue.setSpecularPower(10.0);
On peut ensuite assigner l'apparence aux objets :
sphere.setMaterial(mat_blue);
Texture
modifierUne texture est définie par une image pouvant définir la couleur de diffusion, de réflexion, d'illumination de l'objet.
Les méthodes de la classe PhongMaterial
concernant les textures sont les suivantes :
setDiffuseMap(Image)
- Définit la texture pour la couleur de diffusion.
setSpecularMap(Image)
- Définit la texture pour la couleur de reflet.
setSelfIlluminationMap(Image)
- Définit la texture pour l'intensité d'illumination.
setBumpMap(Image)
- Définit la texture pour la granularité de l'objet.
Exemple
modifierL'exemple ci-dessous utilise l'image Red-brick-wall-texture-1.jpg disponible sur commons.
PhongMaterial mat_wall = new PhongMaterial();
//Image tex_im = new Image("file://///E:/Image/wall.png"); // Chemin local
Image tex_im = new Image("https://upload.wikimedia.org/wikipedia/commons/thumb/a/ac/Red-brick-wall-texture-1.jpg/640px-Red-brick-wall-texture-1.jpg");
PhongMaterial mat_wall = new PhongMaterial();
mat_wall.setDiffuseColor(Color.GRAY);
mat_wall.setSpecularColor(Color.WHITE);
mat_wall.setSpecularPower(8.0);
mat_wall.setDiffuseMap(tex_im);
Code complet
modifierpackage org.wikibooks.fr.javafx;
import javafx.application.*;
import javafx.scene.*;
import javafx.scene.image.*;
import javafx.scene.paint.*;
import javafx.scene.shape.*;
import javafx.scene.transform.*;
import javafx.stage.*;
public class TestApparence3D extends Application
{
private final Rotate rotate_x = new Rotate(0, Rotate.X_AXIS);
private final Rotate rotate_y = new Rotate(0, Rotate.Y_AXIS);
private final Translate translate = new Translate(0, 0, -1000);
@Override
public void start(Stage stage)
{
Image tex_im = new Image("https://upload.wikimedia.org/wikipedia/commons/thumb/a/ac/Red-brick-wall-texture-1.jpg/640px-Red-brick-wall-texture-1.jpg");
PhongMaterial mat_wall = new PhongMaterial();
mat_wall.setDiffuseColor(Color.GRAY);
mat_wall.setSpecularColor(Color.WHITE);
mat_wall.setSpecularPower(8.0);
mat_wall.setDiffuseMap(tex_im);
PhongMaterial mat_blue = new PhongMaterial();
mat_blue.setDiffuseColor(Color.BLUE);
mat_blue.setSpecularColor(Color.LIGHTBLUE);
mat_blue.setSpecularPower(10.0);
PhongMaterial mat_red = new PhongMaterial();
mat_red.setDiffuseColor(Color.RED);
mat_red.setSpecularColor(Color.ORANGERED);
mat_red.setSpecularPower(10.0);
int dx = 0, dy = 0, dz = 0;
Sphere sphere = new Sphere(100);
sphere.setTranslateX(dx);
sphere.setTranslateY(dy);
sphere.setTranslateZ(dz);
sphere.setCursor(Cursor.OPEN_HAND);
sphere.setMaterial(mat_blue);
Box box = new Box(100, 200, 100);
box.setTranslateX(dx + 250);
box.setTranslateY(dy + 220);
box.setTranslateZ(dz);
box.setCursor(Cursor.OPEN_HAND);
box.setMaterial(mat_wall);
Cylinder cyl = new Cylinder(40, 220);
cyl.setTranslateX(dx);
cyl.setTranslateY(dy + 220);
cyl.setTranslateZ(dz);
cyl.setCursor(Cursor.OPEN_HAND);
cyl.setMaterial(mat_red);
TriangleMesh pyramid_mesh = new TriangleMesh(); // VertexFormat.POINT_TEXCOORD => P,T
// Texture : coordonnées indépendantes de la taille de la texture
// 0.5 = 50%, 1.0 = 100%, 2.0 = 200% (texture répétée 2 fois), ...
pyramid_mesh.getTexCoords().addAll( 0 , 0 ); // --> T[0]
pyramid_mesh.getTexCoords().addAll( 1.0F, 0 ); // --> T[1]
pyramid_mesh.getTexCoords().addAll( 0.5F, 0.0F ); // --> T[2]
pyramid_mesh.getTexCoords().addAll( 1.0F, 1.0F ); // --> T[3]
pyramid_mesh.getTexCoords().addAll( 0 , 1.0F ); // --> T[4]
float h = 150; // Height
float s = 300; // Side
pyramid_mesh.getPoints().addAll( // --> P[]
// X Y Z
0, 0, 0, // Point 0 - Top
0, h, -s/2, // Point 1 - Front
-s/2, h, 0, // Point 2 - Left
0, h, s/2, // Point 3 - Back
s/2, h, 0 ); // Point 4 - Right
pyramid_mesh.getFaces().addAll( // --> F[] = index in P[],T[]
// P,T P,T P,T
0,2, 2,4, 1,3, // Front left face
0,2, 1,4, 4,3, // Front right face
0,2, 4,4, 3,3, // Back right face
0,2, 3,4, 2,3, // Back left face
3,2, 1,3, 2,0, // Bottom rear face
3,2, 4,1, 1,4 ); // Bottom front face
pyramid_mesh.getFaceSmoothingGroups().addAll(0,1,2,3,4,4);
MeshView pyramid = new MeshView(pyramid_mesh);
pyramid.setDrawMode(DrawMode.FILL);
pyramid.setMaterial(mat_wall);
pyramid.setTranslateX(dx);
pyramid.setTranslateY(dy-150-h);
pyramid.setTranslateZ(dz);
Node[] allnodes = { sphere, cyl, pyramid, box };
Color light_color = Color.LIGHTGREY;
AmbientLight light = new AmbientLight(light_color);
light.setTranslateX(dx - 180);
light.setTranslateY(dy - 90);
light.setTranslateZ(dz - 120);
light.getScope().addAll(allnodes);
PointLight light2 = new PointLight(light_color);
light2.setTranslateX(dx + 180);
light2.setTranslateY(dy + 190);
light2.setTranslateZ(dz + 180);
light2.getScope().addAll(allnodes);
Group root = new Group(allnodes);
root.getChildren().addAll(light, light2);
Scene scene = new Scene(root, 600, 600, true);
scene.setFill(Color.BLACK);
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setNearClip(0.1);
camera.setFarClip(10000.0);
camera.getTransforms().addAll(rotate_x, rotate_y, translate);
scene.setCamera(camera);
stage.setTitle("3D JavaFX");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args)
{
Application.launch(args);
}
}
Ajustements
modifierLa texture de la base carrée fait apparaître les deux triangles car les 2 faces adjacentes sont associées à deux triangles qui ne sont pas adjacent dans la texture :
En ajustant la texture comme dans le code ci-dessous, on obtient une texture uniforme pour la base carrée :
pyramid_mesh.getFaces().addAll( // --> F[] = index in P[],T[]
// P,T P,T P,T
0,2, 2,4, 1,3, // Front left face
0,2, 1,4, 4,3, // Front right face
0,2, 4,4, 3,3, // Back right face
0,2, 3,4, 2,3, // Back left face
3,1, 1,4, 2,0, // Bottom rear face
3,1, 4,3, 1,4 ); // Bottom front face
|
Dans cette version, la caméra est fixe. Le chapitre suivant résout le problème en ajoutant la possibilité d'interagir avec la souris pour déplacer la caméra.