为什么漫反射贴图未应用于我的 MeshView?

Why is the diffuse map not applied to my MeshView?

问题

我想将漫反射贴图应用到 MeshView。当我将带有漫反射贴图的 Material 应用到 MeshView 时,它是不可见的。但是,应用于 Box 的相同 material 是可见的。

问题

如何将漫反射贴图应用到 MeshView?

代码

代码生成带有随机噪声的图像。该图像在 PhongMaterial 中用作漫反射贴图。显示图像,其上方是应用了 material 的框,框上方是应用了 material 的 MeshView(金字塔)。 material 在金字塔上不可见。可以使用鼠标拖动旋转。

import java.util.Random;

import javafx.application.Application;
import javafx.geometry.Point3D;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.DrawMode;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.TriangleMesh;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;

public class Test extends Application {

    private double mousePosX, mousePosY;
    private double mouseOldX, mouseOldY;
    private final Rotate rotateX = new Rotate(20, Rotate.X_AXIS);
    private final Rotate rotateY = new Rotate(-45, Rotate.Y_AXIS);

    @Override
    public void start(Stage primaryStage) {

        // cube
        Group group = new Group();

        // size of the cube
        double size = 400;
        group.getTransforms().addAll(rotateX, rotateY);

        Image diffuseMap = createImage( size);

        // show noise image
        ImageView iv = new ImageView( diffuseMap);
        iv.setTranslateX(-0.5*size);
        iv.setTranslateY(-0.20*size);
        iv.setRotate(90);
        iv.setRotationAxis(new Point3D(1,0,0));
        group.getChildren().add( iv);

        // create material out of the noise image
        PhongMaterial material = new PhongMaterial();
        material.setDiffuseMap(diffuseMap);

        // create box with noise diffuse map
        Box box = new Box( 100,100,100);
        box.setMaterial(material);
        group.getChildren().add( box);

        // create pyramid with diffuse map
        float h = 150;                    // Height
        float s = 150;                    // Side

        TriangleMesh pyramidMesh = new TriangleMesh();

        pyramidMesh.getTexCoords().addAll(1,1,1,0,0,1,0,0);

        pyramidMesh.getPoints().addAll(
                0,    0,    0,            // Point 0 - Top
                0,    h,    -s/2,         // Point 1 - Front
                -s/2, h,    0,            // Point 2 - Left
                s/2,  h,    0,            // Point 3 - Back
                0,    h,    s/2           // Point 4 - Right
            );

        pyramidMesh.getFaces().addAll(
          0,0,  2,0,  1,0,          // Front left face
          0,0,  1,0,  3,0,          // Front right face
          0,0,  3,0,  4,0,          // Back right face
          0,0,  4,0,  2,0,          // Back left face
          4,0,  1,0,  2,0,          // Bottom rear face
          4,0,  3,0,  1,0           // Bottom front face
        ); 


        MeshView pyramid = new MeshView(pyramidMesh);
        pyramid.setDrawMode(DrawMode.FILL);
        pyramid.setTranslateY(-250);

        // apply material
        // TODO: why is the diffuse map not displayed?
        pyramid.setMaterial(material);

        group.getChildren().add(pyramid);

        // scene
        StackPane root = new StackPane();
        root.getChildren().add(group);
        Scene scene = new Scene(root, 1600, 900, true, SceneAntialiasing.BALANCED);
        scene.setCamera(new PerspectiveCamera());

        // interaction listeners
        scene.setOnMousePressed(me -> {
            mouseOldX = me.getSceneX();
            mouseOldY = me.getSceneY();
        });
        scene.setOnMouseDragged(me -> {
            mousePosX = me.getSceneX();
            mousePosY = me.getSceneY();
            rotateX.setAngle(rotateX.getAngle()-(mousePosY - mouseOldY));
            rotateY.setAngle(rotateY.getAngle()+(mousePosX - mouseOldX));
            mouseOldX = mousePosX;
            mouseOldY = mousePosY;

        });

        primaryStage.setResizable(false);
        primaryStage.setScene(scene);
        primaryStage.show();

    }

    /**
     * Create image with random noise
     */
    public Image createImage( double size) {

        Random rnd = new Random();

        int width = (int) size;
        int height = (int) size;

        WritableImage wr = new WritableImage(width, height);
        PixelWriter pw = wr.getPixelWriter();
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {

                Color color = Color.rgb(rnd.nextInt( 256), rnd.nextInt( 256), rnd.nextInt( 256));
                pw.setColor(x, y, color);

            }
        }

        return wr;

    }

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

截图

非常感谢您的帮助!

问题

这个形状由 6 个单一的形状组成。四个三角形都交于A点。 底部有一个矩形,因为它们是三角形,所以您需要两个三角形来创建一个矩形。下图是那个形状的俯视图

如您所见,我们有 5 分。因此必须将它们添加到 TriangleMesh 的点中。一个点由三个浮点值 (x,y,z) 组成的元组组成。所以这个数组总是由 3、6、9、...、15 等大小组成。

纹理

如果您希望将图像作为网格的 material(就像您所做的那样),则必须获取纹理坐标并将图像坐标添加到它们。但是你在这里添加什么?这些是您要设置为 material 的图像的二维坐标。它从 (0.0, 0.0) (u0, v0) 开始,一直到 (1.0, 1.0) (u1, v1)。它是您要在 3D 网格上显示的图像片段的 2D 坐标。因为你只有一个嘈杂的图像,你可以做 0,0 到 1,1,但你需要 3 个三角形点。

面对

现在你必须做饰面了。这就像您想在形状的线条之间的 space 中放置一些盘子。正如您已经看到的,您必须为三角形添加四块,为底部添加两块。

面是一个包含六个值的元组。因为我们在画三角形,所以它总是这样的:从一点,到一点,再到一点。所以元组由 6 个值组成。 p0, t0, p1, t1, p2, t2。这些值是点和纹理数组的索引。 p0指向点数组的第一个树元组,t0指向纹理坐标数组的第一个二元组。

(逆时针)

也许我的解释不完全正确,但我是这样理解的:

JavaFX 的默认摄像头是逆时针方向的,所以如果你逆时针方向放置面部,摄像头就会看到你的面部正面。背面不是,这是在 JavaFX 内部完成的,以解决性能问题。背面不会被任何material渲染,只要你不设置剔除到背面。

要查看这个三角形的底部,相机必须改变它的视角,所以它是顺时针的。正面和背面同样如此。

在您的示例中,我已经命名了这些点,因为现在您可以看到我首先渲染了哪些面孔,最后渲染了哪些面孔。例如:

ABC 是我的第一张脸,它是 A、B 和 C 点之间的三角形。这些点的纹理来自 material 图像坐标。

有关这些内容的更多信息,请访问 José Pereda 的博客:http://jperedadnr.blogspot.de/2015/01/creating-and-texturing-javafx-3d-shapes.html

希望你已经明白了一切,然后在下面你会找到我的解决方案。

解决方案

import java.util.Random;

import javafx.application.Application;
import javafx.geometry.Point3D;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.DrawMode;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.TriangleMesh;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;

public class Test extends Application {

    private double mousePosX, mousePosY;
    private double mouseOldX, mouseOldY;
    private final Rotate rotateX = new Rotate(20, Rotate.X_AXIS);
    private final Rotate rotateY = new Rotate(-45, Rotate.Y_AXIS);

    @Override
    public void start(Stage primaryStage) {

        // cube
        Group group = new Group();

        // size of the cube
        double size = 400;
        group.getTransforms().addAll(rotateX, rotateY);

        Image diffuseMap = createImage(size);

        // show noise image
        ImageView iv = new ImageView(diffuseMap);
        iv.setTranslateX(-0.5 * size);
        iv.setTranslateY(-0.20 * size);
        iv.setRotate(90);
        iv.setRotationAxis(new Point3D(1, 0, 0));
        group.getChildren().add(iv);

        // create material out of the noise image
        PhongMaterial material = new PhongMaterial();
        material.setDiffuseMap(diffuseMap);

        // create box with noise diffuse map
        Box box = new Box(100, 100, 100);
        box.setMaterial(material);
        group.getChildren().add(box);

        // create pyramid with diffuse map
        float h = 150; // Height
        float s = 150; // Side
        float hs = s / 2;

        // coordinates of the mapped image
        float x0 = 0.0f;
        float y0 = 0.0f;
        float x1 = 1.0f;
        float y1 = 1.0f;

        TriangleMesh pyramidMesh = new TriangleMesh();

        pyramidMesh.getPoints().addAll( //
                0.0f, 0.0f, 0.0f, // A 0 Top of Pyramid
                hs, h, -hs, // B 1
                hs, h, hs, // C 2
                -hs, h, hs, // D 3
                -hs, h, -hs // E 4
        );

        pyramidMesh.getTexCoords().addAll( //
                x0, y0, // 0
                x0, y1, // 1
                x1, y0, // 2
                x1, y1 // 3
        );

        pyramidMesh.getFaces().addAll(// index of point, index of texture, index of point, index of texture, index of point, index of texture
                0, 0, 1, 1, 2, 3, // ABC (counter clockwise)
                0, 0, 2, 1, 3, 3, // ACD (counter clockwise)
                0, 0, 3, 1, 4, 3, // ADE (counter clockwise)
                0, 0, 4, 1, 1, 3, // AEB (counter clockwise)
                4, 0, 3, 1, 2, 3, // EDC (Bottom first triangle clock wise)
                2, 0, 1, 1, 4, 3 // CBE (Bottom second triangle clock wise)
        );

        MeshView pyramid = new MeshView();
        pyramid.setMesh(pyramidMesh);
        pyramid.setDrawMode(DrawMode.FILL);
        pyramid.setTranslateY(-250);

        // apply material
        // TODO: why is the diffuse map not displayed?
        pyramid.setMaterial(material);
        group.getChildren().add(pyramid);

        // scene
        StackPane root = new StackPane();
        root.getChildren().add(group);
        Scene scene = new Scene(root, 1600, 900, true, SceneAntialiasing.BALANCED);
        scene.setCamera(new PerspectiveCamera());

        // interaction listeners
        scene.setOnMousePressed(me -> {
            mouseOldX = me.getSceneX();
            mouseOldY = me.getSceneY();
        });
        scene.setOnMouseDragged(me -> {
            mousePosX = me.getSceneX();
            mousePosY = me.getSceneY();
            rotateX.setAngle(rotateX.getAngle() - (mousePosY - mouseOldY));
            rotateY.setAngle(rotateY.getAngle() + (mousePosX - mouseOldX));
            mouseOldX = mousePosX;
            mouseOldY = mousePosY;

        });

        primaryStage.setResizable(false);
        primaryStage.setScene(scene);
        primaryStage.show();

    }

    /**
     * Create image with random noise
     */
    public Image createImage(double size) {

        Random rnd = new Random();

        int width = (int) size;
        int height = (int) size;

        WritableImage wr = new WritableImage(width, height);
        PixelWriter pw = wr.getPixelWriter();
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                Color color = Color.rgb(rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256));
                pw.setColor(x, y, color);
            }
        }
        return wr;
    }

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