JavaFX Gráficos 3D

JavaFX nos ofrece la posibilidad de agregar gráficos tridimensionales en la aplicación que estemos desarrollando, JavaFX cuenta con un grupo de figuras prediseñadas como: cubo, esfera, cilindro, etc., además podemos crear nuestras propias figuras, contamos también con las clases correspondientes para el manejo de luces y cámaras.

Verificar Soporte Gráfico 3D


Antes de hacer algo primero debemos verificar si nuestra plataforma soporta JavaFX 3D, el código de verificación es sencillo y se muestra a continuación:

package sample;

import javafx.application.Application;
import javafx.application.ConditionalFeature;
import javafx.application.Platform;
import javafx.stage.Stage;

public class Main extends Application {
    
    @Override
    public void start(Stage primaryStage) throws Exception {

        boolean supported = Platform.isSupported(ConditionalFeature.SCENE3D);

        if (supported) System.out.println("JavaFX 3D Listo");
        else System.out.println("Esta plataforma no soporta JavaFX 3D");
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Sistema de Coordenadas


Un punto en el espacio 3D está representado por sus coordenadas en las componentes (x, y, z), el sistema de coordenadas usado por JavaFX 3D ubica el punto (0, 0, 0) en la parte superior izquierda de la ventana, del siguiente modo:

javafx3d
Las clases Box, Sphere y Cylinder representan las figuras prediseñadas que podemos agregar a la escena, para visualizar correctamente la escena debemos agregar una fuente de luz, la clase PointLight nos crea una luz puntual, requerimos también agregar una cámara, para ello tenemos la clase PerspectiveCamera, cada una de estas clases cuenta con los métodos setTranslate(X/Y/Z) que nos permiten cambiar su posición.

private void createScene3D(Stage stage) {

    // crear un cubo 3D, anchura, altura y profundidad
    Box box = new Box(100, 100, 100);

    // crear una luz puntual
    PointLight light = new PointLight();
    light.setTranslateX(-350);
    light.setTranslateY(-180);
    light.setTranslateZ(-500);
    
    Group root = new Group(box, light);

    // crear la escena, true para activar el buffer de profindidad 
    Scene scene = new Scene(root, 1280, 768, true, SceneAntialiasing.BALANCED);

    // crear una camara en perspectiva
    PerspectiveCamera camera = new PerspectiveCamera();
    camera.setTranslateX(scene.getWidth() / -2.0);
    camera.setTranslateY((scene.getHeight() / -2.0) - 150);

    scene.setCamera(camera);
    stage.setTitle("JavaFX Graficos 3D");
    stage.setScene(scene);
    stage.show();
}

javafx 3d cube
Para obtener este resultado se han aplicado rotaciones en X e Y, usando en método box.setRotate(45); para indicar el ángulo de rotación y box.setRotationAxis(new Point3D(1, 1, 0)); para indicar los ejes sobre los que se hará la rotación.

Especificar Materiales


Podemos indicar que tipo de material conforma nuestro objeto 3D, para ello contamos con la clase PhongMaterial usada para establecer los materiales de un objeto utilizando el modelo de iluminación Phong podemos establecer las siguientes propiedades: diffuseColor, diffuseMap, specularColor, specularMap, selfIlluminationMap, specularPower, bumpMap, con los correspondientes métodos setter.

PhongMaterial mat = new PhongMaterial(Color.RED);
mat.setSpecularColor(Color.BLUE);
mat.setSpecularPower(128);

// crear un cubo 3D, anchura, altura y profundidad
Box box = new Box(300, 300, 300);
box.setRotate(45);
box.setRotationAxis(new Point3D(1, 1, 0));
box.setMaterial(mat);

specular color
En lugar de aplicar un simple color veamos cómo podemos utilizar mapas de texturas para establecer las componentes difusa y especular, aplicamos solo la primera en este ejemplo, además veremos cómo utilizar normal map o bump map.

Image image_diffuse = new Image(getClass().getResource("muro_diffuse.jpg").toExternalForm());
Image image_normal = new Image(getClass().getResource("muro_normal.jpg").toExternalForm());

PhongMaterial mat = new PhongMaterial();
mat.setSpecularColor(color_luz);
mat.setSpecularPower(64);
mat.setDiffuseMap(image_diffuse);
mat.setBumpMap(image_normal);

diffuse y normal maps

Creación de Objetos 3D


La clase MeshView nos permite definir formas personalizas, esta clase requiere un objeto TriangleMesh para definir como se forma la figura, debemos indicar cada uno de los vértices usando triangulación, las coordenada de texturas y las caras del modelo.

Normalmente no definimos manualmente esta información, por lo general nos apoyamos en software de diseño 3D como: Blender, Maya, 3D Max, etc., generamos la figura y la almacenamos en un archivo en formato: .obj, .fbx, .dae, etc,.

Utilizaremos la librería creada para la aplicación de ejemplo JavaFX 3D Viewer, cargaremos un archivo .obj con sus materiales.

private void loadScene3D(Stage stage) throws IOException {
        PointLight light1 = new PointLight();
        light1.setTranslateZ(-500);

        Node model = Importer3D.load(getClass().getResource("obj-model/cyborg.obj").toExternalForm());
        model.setScaleX(150.0);
        model.setScaleY(150.0);
        model.setScaleZ(150.0);

        Group root = new Group(model, light1);

        Scene scene = new Scene(root, 1280, 768, true, SceneAntialiasing.BALANCED);

        PerspectiveCamera camera = new PerspectiveCamera();
        camera.setTranslateX(scene.getWidth() / -2.0);
        camera.setTranslateY(scene.getHeight() / -2.0);

        RotateTransition rt = new RotateTransition(Duration.seconds(10), model);
        rt.setCycleCount(Animation.INDEFINITE);
        rt.setFromAngle(0);
        rt.setToAngle(360);
        rt.setAxis(new Point3D(0, 1, 0));
        rt.play();

        scene.setFill(Color.CORNFLOWERBLUE);
        scene.setCamera(camera);
        stage.setTitle("JavaFX Graficos 3D - Cargar Modelo");
        stage.setScene(scene);
        stage.show();
}

Este código genera la siguiente salida:

obj model javafx
Puedes descargar este proyecto en su versión para IntelliJ IDE en: JavaFX Gráficos 3D.

Comentarios

  1. Muchas gracias por el post!!
    muy bien explicado

    ResponderEliminar
  2. puedes darme alguna información, sobre como cambiar la ubicación de un gráfico, es decir si cargas por defecto un cubo o un objeto .obj con mtl y .jpg este sale por defecto en el centro del frame, como puedo cambiar estas posiciones en X, Y, Z.

    De antemano gracias

    ResponderEliminar
    Respuestas
    1. Hola, con esta instrucción después de cargar el obj

      cubo.setTranslateX(valor);
      cubo.setTranslateY(valor);
      cubo.setTranslateZ(valor);

      Eliminar
  3. ¿Qué librería es la que utilizas para importar los modelos 3D? Estuve buscando y no encontré sendas librerías.

    ResponderEliminar

Publicar un comentario

Temas relacionados

Entradas populares de este blog

tkinter Grid

tkinter Canvas

Histogramas OpenCV Python

Python Binance API