Раскраска отдельных треугольников в сетке треугольников на javafx

У меня есть объект сетки треугольника в JAVAFX и я хотел бы либо

  1. цвет отдельных треугольников треугольной сетки

или же

  1. раскрасьте отдельные вершины каждого треугольника и сделайте так, чтобы треугольник был раскрашен в соответствии с интерполяцией цветов каждой вершины (например, с затенением Гуро).

Конкретный объект треугольной сетки - это икосфера с миллионами граней (поэтому я использую треугольную сетку: мне нужна скорость).

Я НЕ использовал координаты текстуры, так как не смог найти четкого объяснения этому с помощью JAVAFX, плюс я надеюсь, что есть более простой способ.

2 ответа

Решение

Метод раскраски в сетках JavaFX 3D определяется материалом, который вы им назначаете. Для одной сетки есть один материал, и невозможно объединить разные материалы в разные треугольники одной и той же сетки.

Поэтому, если вы хотите избежать текстур, я боюсь, что единственный способ - это сгруппировать треугольники одного цвета в одну и ту же сетку и создать столько сеток, сколько цветов.

Напротив, с текстурами сравнительно легко..., поскольку у вас есть только одна сетка, один материал и одно изображение со всеми цветами.

Я сделал пример икосаэдра, построил для него треугольную сетку и добавил одну текстуру, чтобы закрасить все грани.

Для этого нам нужно:

  • трехмерные координаты 12 вершины,
  • 2D нормализованные координаты ультрафиолетового отображения для текстур.
  • и 20 лиц. Каждая грань определяется 6 индексами p0, t0, p1, t1, p3, t3, где p0, p1, p2 и p3 являются индексами в массиве точек, а t0, t1, t2 и t3 являются индексами в массиве texCoords.

    открытый класс IcosahedronMesh расширяет MeshView {

    public IcosahedronMesh(){
        setMesh(createCube());
    }
    private TriangleMesh createCube() {
        TriangleMesh m = new TriangleMesh();
    
        // POINTS
        m.getPoints().addAll(
            0f, 0f, -0.951057f, 
            0f, 0f, 0.951057f, 
            -0.850651f, 0f, -0.425325f, 
            0.850651f, 0f, 0.425325f, 
            0.688191f, -0.5f, -0.425325f, 
            0.688191f, 0.5f, -0.425325f, 
            -0.688191f, -0.5f, 0.425325f, 
            -0.688191f, 0.5f, 0.425325f, 
            -0.262866f, -0.809017f, -0.425325f, 
            -0.262866f, 0.809017f, -0.425325f, 
            0.262866f, -0.809017f, 0.425325f, 
            0.262866f, 0.809017f, 0.425325f
        );
    
        // TEXTURES
        m.getTexCoords().addAll(
                0.181818f, 0f, 
                0.363636f, 0f, 
                0.545455f, 0f, 
                0.727273f, 0f, 
                0.909091f, 0f,
                0.0909091f, 0.333333f,
                0.272727f, 0.333333f, 
                0.454545f, 0.333333f, 
                0.636364f, 0.333333f, 
                0.818182f, 0.333333f, 
                1f, 0.333333f, 
                0f, 0.666667f, 
                0.181818f, 0.666667f, 
                0.363636f, 0.666667f, 
                0.545455f, 0.666667f, 
                0.727273f, 0.666667f, 
                0.909091f, 0.666667f, 
                0.0909091f, 1f, 
                0.272727f, 1f, 
                0.454545f, 1f, 
                0.636364f, 1f, 
                0.818182f, 1f
        );
    
        // FACES
        m.getFaces().addAll(
                1, 6, 11, 5, 7, 0, 
                1, 12, 7, 11, 6, 5, 
                1, 7, 6, 6, 10, 1, 
                1, 13, 10, 12, 3, 6, 
                1, 8, 3, 7, 11, 2,
                4, 14, 8, 13, 0, 7, 
                5, 9, 4, 8, 0, 3, 
                9, 15, 5, 14, 0, 8, 
                2, 10, 9, 9, 0, 4, 
                8, 16, 2, 15, 0, 9,
                11, 5, 9, 6, 7, 12,
                7, 11, 2, 12, 6, 17, 
                6, 6, 8, 7, 10, 13, 
                10, 12, 4, 13, 3, 18, 
                3, 7, 5, 8, 11, 14,
                4, 13, 10, 14, 8, 19, 
                5, 8, 3, 9, 4, 15, 
                9, 14, 11, 15, 5, 20, 
                2, 9, 7, 10, 9, 16, 
                8, 15, 6, 16, 2, 21
        );
        return m;
    }
    

    }

Теперь нам нужно изображение с раскраской для каждого лица на основе сети икосаэдра, например:

Сеть икосаэдра

(Изображение найдено здесь)

Обратите внимание, что сопоставление выполняется от нормализованных координат (0,0) до (1,1) к пикселям изображения (слева, сверху) и (справа, снизу).

Давайте, наконец, создадим сцену, загрузим сетку и добавим текстуру к ее материалу:

@Override
public void start(Stage primaryStage) throws Exception {
    Group sceneRoot = new Group();
    Scene scene = new Scene(sceneRoot, 600, 600, true, SceneAntialiasing.BALANCED);
    scene.setFill(Color.BLACK);
    PerspectiveCamera camera = new PerspectiveCamera(true);
    camera.setNearClip(0.1);
    camera.setFarClip(10000.0);
    camera.setTranslateZ(-4);
    scene.setCamera(camera);

    IcosahedronMesh mesh = new IcosahedronMesh();
    mesh.setCullFace(CullFace.FRONT);
    PhongMaterial mat = new PhongMaterial();
    mat.setDiffuseMap(new Image(getClass().getResourceAsStream("icosah_net.png")));
    mesh.setMaterial(mat);
    Rotate rotateY = new Rotate(0, 0, 0, 0, Rotate.Y_AXIS);
    mesh.getTransforms().addAll(new Rotate(30,Rotate.X_AXIS),rotateY);

    sceneRoot.getChildren().addAll(mesh, new AmbientLight(Color.WHITE));

    primaryStage.setTitle("JavaFX 3D - Icosahedron");
    primaryStage.setScene(scene);
    primaryStage.show();        
}

Вот как это выглядит:

Икосаэдр

РЕДАКТИРОВАТЬ

Теперь, если вы думаете о том, как применяется текстура, вы можете упростить изображение до нескольких квадратов с нужной вам палитрой цветов:

Палитра цветов

И координаты текстуры могут быть действительно упрощены:

m.getTexCoords().addAll(
        0.1f, 0.5f, // 0 red
        0.3f, 0.5f, // 1 green
        0.5f, 0.5f, // 2 blue
        0.7f, 0.5f, // 3 yellow
        0.9f, 0.5f  // 4 orange
);

Наконец, мы должны отобразить эти точки на наших лицах. Следуя той же схеме, что и изображение сети:

m.getFaces().addAll(
        1, 0, 11, 0, 7, 0, 
        1, 4, 7, 4, 6, 4, 
        1, 4, 6, 4, 10, 4, 
        1, 2, 10, 2, 3, 2, 
        1, 2, 3, 2, 11, 2,                
        4, 3, 8, 3, 0, 3, 
        5, 3, 4, 3, 0, 3, 
        9, 1, 5, 1, 0, 1, 
        2, 1, 9, 1, 0, 1, 
        8, 0, 2, 0, 0, 0, 

        11, 3, 9, 3, 7, 3,
        7, 1, 2, 1, 6, 1, 
        6, 1, 8, 1, 10, 1, 
        10, 0, 4, 0, 3, 0, 
        3, 0, 5, 0, 11, 0,

        4, 4, 10, 4, 8, 4, 
        5, 4, 3, 4, 4, 4, 
        9, 2, 11, 2, 5, 2, 
        2, 2, 7, 2, 9, 2, 
        8, 3, 6, 3, 2, 3
);

Теперь у нас будет очень аккуратный икосаэдр, так как мы избавляемся от границ и плохого разрешения изображения:

Улучшенный икосаэдр

Это можно распространить на любую треугольную сетку или использовать любой алгоритм для уточнения треугольников.

Спасибо Хосе за это действительно полезное введение и намек на его реализацию, основанную на ряде цветов.

Чтобы лучше понять, что нужно сделать, чтобы создать цветной материал без изображения с диска, я модифицировал приведенный здесь пример Хосе.

Основной класс приложения:

      public class IcosahedronApp extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        Group sceneRoot = new Group();
        Scene scene = new Scene(sceneRoot, 600, 600, true, SceneAntialiasing.BALANCED);
        scene.setFill(Color.BLACK);
        PerspectiveCamera camera = new PerspectiveCamera(true);
        camera.setNearClip(0.1);
        camera.setFarClip(10000.0);
        camera.setTranslateZ(-4);
        scene.setCamera(camera);
        IcosahedronMesh mesh = new IcosahedronMesh();
        mesh.setCullFace(CullFace.FRONT);
        Rotate rotateY = new Rotate(0, 0, 0, 0, Rotate.Y_AXIS);
        mesh.getTransforms().addAll(new Rotate(30,Rotate.X_AXIS),rotateY);
        sceneRoot.getChildren().addAll(mesh, new AmbientLight(Color.WHITE));
        primaryStage.setTitle("JavaFX 3D - Icosahedron");
        primaryStage.setScene(scene);
        primaryStage.show();        
    }
    
    public static void main(String[] args) {
        launch(args);
    }
}

и сетка икосаэдра

      public class IcosahedronMesh extends MeshView {
    private final static Color[] COLORS = {
        Color.RED,
        Color.GREEN,
        Color.BLUE,
        Color.YELLOW,
        Color.ORANGE,
    };

    public IcosahedronMesh() {
        setMesh(createCube());
    }

    private TriangleMesh createCube() {
        TriangleMesh m = new TriangleMesh();
        // coloring
        PhongMaterial mat = new PhongMaterial();
        WritableImage image = new WritableImage(COLORS.length, 1);
        PixelWriter writer = image.getPixelWriter();
        for (int i = 0; i < COLORS.length; i++)
            writer.setColor(i, 0, COLORS[i]);
        mat.setDiffuseMap(image);
        setMaterial(mat);
        // POINTS
        m.getPoints().addAll(
                // Point 1
                0f, 0f, -0.951057f,
                // Point 2
                0f, 0f, 0.951057f,
                // Point 3
                -0.850651f, 0f, -0.425325f,
                // Point 4
                0.850651f, 0f, 0.425325f,
                // Point 5
                0.688191f, -0.5f, -0.425325f,
                // Point 6
                0.688191f, 0.5f, -0.425325f,
                // Point 7
                -0.688191f, -0.5f, 0.425325f,
                // Point 8
                -0.688191f, 0.5f, 0.425325f,
                // Point 9
                -0.262866f, -0.809017f, -0.425325f,
                // Point 10
                -0.262866f, 0.809017f, -0.425325f,
                // Point 11
                0.262866f, -0.809017f, 0.425325f,
                // Point 12
                0.262866f, 0.809017f, 0.425325f);
        // TEXTURES
        m.getTexCoords().addAll(
                // 0 red
                0.1f, 0.5f,
                // 1 green
                0.3f, 0.5f,
                // 2 blue
                0.5f, 0.5f,
                // 3 yellow
                0.7f, 0.5f,
                // 4 orange
                0.9f, 0.5f);
        // FACES
        m.getFaces().addAll(
                // Face 1
                1, 0, 11, 0, 7, 0,
                // Face 2
                1, 4, 7, 4, 6, 4,
                // Face 3
                1, 4, 6, 4, 10, 4,
                // Face 4
                1, 2, 10, 2, 3, 2,
                // Face 5
                1, 2, 3, 2, 11, 2,
                // Face 6
                4, 3, 8, 3, 0, 3,
                // Face 7
                5, 3, 4, 3, 0, 3,
                // Face 8
                9, 1, 5, 1, 0, 1,
                // Face 9
                2, 1, 9, 1, 0, 1,
                // Face 10
                8, 0, 2, 0, 0, 0,
                // Face 11
                11, 3, 9, 3, 7, 3,
                // Face 12
                7, 1, 2, 1, 6, 1,
                // Face 13
                6, 1, 8, 1, 10, 1,
                // Face 14
                10, 0, 4, 0, 3, 0,
                // Face 15
                3, 0, 5, 0, 11, 0,
                // Face 16
                4, 4, 10, 4, 8, 4,
                // Face 17
                5, 4, 3, 4, 4, 4,
                // Face 18
                9, 2, 11, 2, 5, 2,
                // Face 19
                2, 2, 7, 2, 9, 2,
                // Face 20
                8, 3, 6, 3, 2, 3);
        return m;
    }
}

Более или менее это последний пример от Хосе. Я только заменил диффузную карту на WritableImage.

Вроде хорошо работает...

Другие вопросы по тегам