【问题标题】:Why is the diffuse map not applied to my MeshView?为什么漫反射贴图没有应用到我的 MeshView?
【发布时间】:2015-06-29 07:42:32
【问题描述】:

问题

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

问题

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

代码

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

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);
    }
}

截图

非常感谢您的帮助!

【问题讨论】:

    标签: javafx javafx-3d


    【解决方案1】:

    问题

    这个形状由 6 个单一的形状组成。四个三角形都在 A 点相遇。 底部有一个矩形,因为它们是三角形,你需要两个三角形来创建一个矩形。下图显示了该形状的俯视图。

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

    纹理

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

    面对

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

    面是六个值的元组。因为我们正在绘制三角形,所以它总是类似于:从点到点,再到点。所以元组由 6 个值组成。 p0,t0,p1,t1,p2,t2。这些值是点和纹理数组的索引。 p0指向点数组的第一个树元组,t0指向纹理坐标数组的前两个元组。

    (逆时针)顺时针

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

    JavaFX 的默认摄像头是逆时针方向的,因此如果您将脸部按逆时针方向放置,您的脸部正面将对摄像头可见。面不靠背,这是在 JavaFX 内部针对性能问题完成的。只要不将剔除设置为背面,背面就不会使用任何材质进行渲染。

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

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

    ABC 是我的第一张脸,它是点 A、B 和 C 之间的三角形。这些点从材质图像坐标中获取它们的纹理。

    有关这些事情的更多信息,请访问 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);
        }
    }
    

    【讨论】:

    • 哇,我认为这很容易。非常感谢您的帮助!
    • 欢迎您!顺便说一句,有一本好书可以获得更多关于 JavaFX 3D 的信息:Vos et 的 Pro JavaFX 8。人。 apress.com/pro-javafx-8
    • 谢谢你的书提示,我去看看。还有一个问题:顺时针和逆时针方向是什么?我想这点无关紧要,但对于纹理和面孔来说,它似乎很重要。至少这是我在创建chart 样本时收集到的信息。没有找到任何文档。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-26
    • 1970-01-01
    相关资源
    最近更新 更多