需要有关从高度图创建三角形网格的帮助(JavaFX)

Ant*_*ras 3 javafx mesh heightmap javafx-3d

我有一个程序,该程序生成一个高度图(0到255之间的2D整数数组),并使用Shape3D“ Box”对象为每个“像素”建立一个3D视图,其高度与其在高度图中的值成比例。这将创建一个看上去很酷的四方形外观。我的程序还创建了一个相应的“颜色图”,以映射地形中每个框应具有的颜色。

我还希望能够将此高度图转换为可以使用颜色图进行纹理处理的网格。

2D高度和颜色图 2D高度和颜色图

从高度图和颜色图创建的彩色三角形网格 所需的颜色网格

(这些是我从Google抓取的图片)

Jos*_*eda 5

如果我做对了,您想HeightMapMesh基于2D点的网格构建一个,并为每个点{x, y}指定给定的高度或z值。该值将与给定2D图像相同位置处的像素颜色直接相关。

获取顶点相对容易:创建2D网格,然后使用PixelReader获得颜色。

构建网格并不是那么容易,但是您可以仅基于矩形2D图像构建常规网格。

还有另一种选择:给定多个顶点,您可以生成带有Delaunay三角剖分的网格。

This is already implemented in the FXyz library: Surface3DMesh.

To use it, just add the dependency to your project:

dependencies {
    implementation "org.fxyz3d:fxyz3d:0.5.0"
}
Run Code Online (Sandbox Code Playgroud)

The following application will do a rough approximation at the HeighMapMesh you are looking for.

It uses the image you have posted to create List<Point3D> data based on a PixelReader every 5 pixels on x and y, with just a small sample of the colors of that image.

With this list, two surfaces are created, one will be filled and rendered with a texture map based on the height of each vertex, using the same color list. The other one will be used as a wireframe to be rendered on top.

public class HeighMapMeshTest extends Application {

    private static final int PIXEL_SIZE = 5;

    private static final List<Color> COLOR_LIST = Arrays.asList(Color.web("#3b6eca"),
            Color.web("#d7d588"), Color.web("#60a318"), Color.web("#457517"), Color.web("#467610"),
            Color.web("#654f44"), Color.web("#56453d"), Color.web("#fdfefc"), Color.web("#ffffff"));

    private final Rotate rotateX = new Rotate(-10, Rotate.X_AXIS);
    private final Rotate rotateY = new Rotate(5, Rotate.Y_AXIS);

    private double mousePosX;
    private double mousePosY;
    private double mouseOldX;
    private double mouseOldY;

    @Override
    public void start(Stage primaryStage) {
        Group sceneRoot = new Group();

        PerspectiveCamera camera = new PerspectiveCamera(true);
        camera.setNearClip(0.1);
        camera.setFarClip(10000.0);
        camera.getTransforms().addAll (rotateX, rotateY, new Translate(0, 0, -800));

        Scene scene = new Scene(sceneRoot, 1000, 600, true, SceneAntialiasing.BALANCED);
        scene.setCamera(camera);

        List<Point3D> data = processImage();

        Surface3DMesh heightMapMesh = new Surface3DMesh(data);
        heightMapMesh.setDrawMode(DrawMode.FILL);
        heightMapMesh.setTextureModeVertices3D(new Palette.ListColorPalette(COLOR_LIST), p -> -p.y);

        Surface3DMesh wireframe = new Surface3DMesh(data);
        wireframe.setTextureModeNone(Color.BLACK);

        Group mapGroup = new Group(heightMapMesh, wireframe);
        mapGroup.getTransforms().add(new Translate(-500, 100, 0));
        sceneRoot.getChildren().addAll(mapGroup, new AmbientLight());

        scene.setOnMousePressed(event -> {
            mousePosX = event.getSceneX();
            mousePosY = event.getSceneY();
        });

        scene.setOnMouseDragged(event -> {
            mousePosX = event.getSceneX();
            mousePosY = event.getSceneY();
            rotateX.setAngle(rotateX.getAngle() - (mousePosY - mouseOldY));
            rotateY.setAngle(rotateY.getAngle() + (mousePosX - mouseOldX));
            mouseOldX = mousePosX;
            mouseOldY = mousePosY;
        });

        primaryStage.setTitle("F(X)yz - HeightMapMesh");
        primaryStage.setScene(scene);
        primaryStage.show();

    }

    private List<Point3D> processImage() {
        Image image = new Image(VoxelTest.class.getResourceAsStream("/8rF9BXu.png"));
        PixelReader pixelReader = image.getPixelReader();
        int width = (int) image.getWidth();
        int height = (int) image.getHeight();

        List<Point3D> data = new ArrayList<>();
        for (int y = 0; y < height - PIXEL_SIZE / 2; y += PIXEL_SIZE){
            for (int x = 0; x < width - PIXEL_SIZE / 2; x += PIXEL_SIZE){
                Color color = pixelReader.getColor(x + PIXEL_SIZE / 2, y + PIXEL_SIZE / 2);
                float h = Math.max(COLOR_LIST.indexOf(color) * 10, 0);
                data.add(new Point3D((float) x, -h, (float) (height - y)));
            }
        }
        return data;
    }

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

}
Run Code Online (Sandbox Code Playgroud)

As a result:

MapHeightMesh

Of course, this can be improved in many different ways.

EDIT

Once the 3D mesh has been created, it can be exported to an .OBJ file, including the texture applied.

FXyz already includes OBJWriter for this purpose.

This code:

OBJWriter writer = new OBJWriter((TriangleMesh) heightMapMesh.getMesh(), "mapHeight");
writer.setTextureColors(9);
writer.exportMesh();
Run Code Online (Sandbox Code Playgroud)

will generate mapHeight.obj and mapHeight.mtl, where a diffuse image named palette_9.png is used.

However, this palette image doesn't use the custom palette we have defined.

In order to export the custom colorPalette, we need to create a Palette, and save it to disk:

OBJWriter writer = new OBJWriter((TriangleMesh) heightMapMesh.getMesh(), "mapHeight");
writer.setTextureColors(9);
Palette.ListColorPalette colorPalette = 
        new Palette.ListColorPalette(COLOR_LIST);
Palette palette = new Palette(9, colorPalette);
palette.createPalette(true);
writer.exportMesh();
Run Code Online (Sandbox Code Playgroud)

Verify that the palette file is a 3x3 image with the colors from COLOR_LIST.

Now you can open the obj file with 3DViewer to check that it was exported correctly.

3D浏览器