问题描述
我想用不同的颜色给TriangleMesh的一些三角形上色。
执行此操作的最简单方法是什么,甚至在fxml文件中也是可能的?
Java代码:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.*;
import javafx.scene.layout.Pane;
import javafx.scene.shape.MeshView;
import javafx.stage.Stage;
import java.io.IOException;
public class ColoredMesh extends Application {
@Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(
ColoredMesh.class.getResource(
"mesh.fxml"
)
);
MeshView meshView = fxmlLoader.load();
// mesh.setDrawMode(DrawMode.LINE);
meshView.setTranslateX(-200);
meshView.setTranslateY(400);
meshView.setRotate(90);
Camera camera = new PerspectiveCamera();
camera.setRotate(90);
Scene scene = new Scene(new Pane(meshView), 800, 400);
scene.setCamera(camera);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
多边形/棱锥体/三角网格文件:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.shape.MeshView?>
<?import javafx.scene.shape.TriangleMesh?>
<MeshView>
<mesh>
<TriangleMesh>
<points>
0 100 100
100 100 0
0 100 -100
-100 100 0
0 0 0
</points>
<texCoords>
0 0
</texCoords>
<faces>
0 0 4 0 1 0
1 0 4 0 2 0
2 0 4 0 3 0
3 0 4 0 0 0
0 0 1 0 2 0
0 0 2 0 3 0
</faces>
</TriangleMesh>
</mesh>
</MeshView>
推荐答案
我猜这可能已作为重复项关闭(有关潜在重复项的引用,请参阅参考资料部分)。
然而,我认为这个问题的框架很有趣,而且非常独特,它使用FXML定义了大部分模型,所以我想我应该根据答案进行调整。
高级步骤
- 您需要提供一个具有扩散映射的PhongMaterial,该图像将成为模型的纹理。
- 您需要在模型中定义映射到扩散映射图像(它是0到1范围内的比例映射)中的位置的纹理坐标。
- 定义面时,需要指定纹理贴图中的索引,该索引将用于对面的顶点进行着色。
完整的解释超出了我现在准备写的范围,但我建议您参考其他资源,如果您需要,可以在那里找到更多信息。
输出
这被渲染为gif,所以保真度不高,PC上的实际输出看起来更好,gif输出的速度大大加快,但它确实表明了解决方案的作用。
texture.png
ColoredMesh.java
加载带纹理的模型并围绕X和Y轴为其设置动画。
import javafx.animation.*;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Point3D;
import javafx.scene.*;
import javafx.scene.image.Image;
import javafx.scene.paint.*;
import javafx.scene.shape.MeshView;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.io.IOException;
public class ColoredMesh extends Application {
private static final Color INDIA_INK = Color.rgb(60,61,76);
private static final Color AMBIENT_GRAY = Color.rgb(100, 100, 100);
private static final Duration ROTATION_STEP_TIME = Duration.seconds(5);
@Override
public void start(Stage stage) throws IOException {
MeshView meshView = loadModel();
Scene scene = createScene(meshView);
stage.setScene(scene);
stage.show();
animateNode(meshView);
}
private Scene createScene(MeshView meshView) {
PerspectiveCamera camera = new PerspectiveCamera();
AmbientLight ambientLight = new AmbientLight(AMBIENT_GRAY);
Scene scene = new Scene(
new Group(
ambientLight,
meshView
),
200, 200,
INDIA_INK
);
scene.setCamera(camera);
return scene;
}
private MeshView loadModel() throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(
ColoredMesh.class.getResource(
"pyramid-mesh.fxml"
)
);
MeshView meshView = fxmlLoader.load();
meshView.setTranslateX(100);
meshView.setTranslateY(40);
meshView.setTranslateZ(100);
// We have defined the material in the fxml which creates the MeshView.
// However, I leave this commented code here to show how the material
// can be defined in Java rather than FXML, if that were preferable.
// texture(meshView);
return meshView;
}
private void texture(MeshView meshView) {
PhongMaterial texturedMaterial = new PhongMaterial();
texturedMaterial.setDiffuseMap(
new Image(
ColoredMesh.class.getResource(
"texture.png"
).toExternalForm()
)
);
meshView.setMaterial(texturedMaterial);
}
private void animateNode(MeshView meshView) {
Animation rotateY = createRotationAnimation(Rotate.Y_AXIS, meshView);
Animation rotateX = createRotationAnimation(Rotate.X_AXIS, meshView);
rotateY.setOnFinished(e -> rotateX.play());
rotateX.setOnFinished(e -> rotateY.play());
rotateY.play();
}
private Animation createRotationAnimation(Point3D axis, Node node) {
RotateTransition animation = new RotateTransition(
ROTATION_STEP_TIME,
node
);
animation.setAxis(axis);
animation.setFromAngle(0);
animation.setToAngle(360);
return animation;
}
public static void main(String[] args) {
launch(args);
}
}
金字塔-Mesh.fxml
纹理是定义为为网格节点MeshView指定的PhongMaterial的漫反射贴图的图像。
在本例中,材料是在FXML中定义的,但如果您愿意,也可以在代码中进行定义(示例Java代码包含一个注释掉的部分来执行此操作)。
网格中的纹理坐标定义了纹理图像中每个颜色样本的中点。
对于基于三角形模型的金字塔整体,使用了六个三角形。对于底面,有两个三角形,但它们的颜色都相同,因此只需要五个纹理坐标就可以为每个面实现纯色。
在定义面时,每个对顶点的引用后跟一个对纹理坐标的引用。每个三角面被定义为对所有顶点使用相同的纹理坐标,这将为面生成与纹理图像中该纹理坐标处的颜色相匹配的统一纯色。<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.shape.MeshView?>
<?import javafx.scene.shape.TriangleMesh?>
<?import javafx.scene.paint.PhongMaterial?>
<?import javafx.scene.image.Image?>
<MeshView>
<material>
<PhongMaterial>
<diffuseMap>
<Image url="@texture.png"/>
</diffuseMap>
</PhongMaterial>
</material>
<mesh>
<TriangleMesh>
<points>
0 100 100
100 100 0
0 100 -100
-100 100 0
0 0 0
</points>
<texCoords>
0.1 0.5
0.3 0.5
0.5 0.5
0.7 0.5
0.9 0.5
</texCoords>
<faces>
0 0 4 0 1 0
1 1 4 1 2 1
2 2 4 2 3 2
3 3 4 3 0 3
0 4 1 4 2 4
0 4 2 4 3 4
</faces>
</TriangleMesh>
</mesh>
</MeshView>
TextureMaker.java
您可以以任何您想要的方式生成纹理图像。
对于本演示,我编写了一个小程序,该程序从在JavaFX中创建的快照创建图像。
如果有兴趣,这是创建纹理的代码,但不一定要使用它来运行演示。
该概念称为TextureAtlas。
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.layout.FlowPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
public class TextureMaker extends Application {
private static final int SWATCH_SIZE = 10;
private static final Color colors[] = {
Color.RED,
Color.GREEN,
Color.BLUE,
Color.MAGENTA,
Color.CYAN
};
@Override
public void start(Stage stage) throws IOException {
Rectangle[] colorSwatches = Arrays.stream(colors)
.map(this::createColoredRect)
.toArray(Rectangle[]::new);
FlowPane flowPane = new FlowPane(colorSwatches);
flowPane.setPrefSize(colors.length * SWATCH_SIZE, SWATCH_SIZE);
Scene scene = new Scene(
flowPane,
Color.rgb(60,61,76)
);
Image textureImage = scene.snapshot(null);
File textureFile = new File("texture.png");
ImageIO.write(
SwingFXUtils.fromFXImage(textureImage, null),
"png",
textureFile
);
System.out.println("Wrote: " + textureFile.getAbsolutePath());
stage.setScene(scene);
stage.show();
}
public Rectangle createColoredRect(Color color) {
return new Rectangle(SWATCH_SIZE,SWATCH_SIZE, color);
}
public static void main(String[] args) {
launch(args);
}
}
此实现创建一个图像并将其保存到磁盘,但如果需要,您可以在主应用程序中动态创建该图像,而无需将其保存到磁盘。链接的类似问题中有这种方法的演示。
关于此解决方案
上述解决方案不是任意给任何给定网格上色的方法的通用答案。
相反,它更专注于回答特定问题:
- 如何为您的问题中定义的金字塔表面上色,在fxml中定义尽可能多的数据,并且不使用外部库?
关于复杂模型和重复使用预制模型的建议
对于复杂模型,我建议使用其他软件支持的标准3D格式(例如.obj或.stl),而不是用FXML定义网格数据(面/顶点/纹理坐标)。
Web上有以常见格式提供的预先创建的模型(但不包括fxml)。此类模型可以附带必要的图像贴图和纹理坐标定义来对其进行着色。
您可以使用用于各种3D模型格式的第三方导入器库将这些模型导入到JavaFX中。如果您进行搜索,可以在Web上找到这些JavaFX导入器库,例如Interactive Mesh和f(X)yz库。相关问题
- 正如Jose所指出的:这实际上是启发FXyz3D中TexturedMesh的答案。
- Jose对这个关于魔方覆盖的问题的回答非常适合您的应用程序,尽管问题标题是通用的。
- 使用第三方库FXyz3D回答,其功能类似于TexturedMesh。
强烈推荐理解纹理3D网格的教程
有一个非常好的教程(比我能想到的任何教程都好)就是Create 3D Shapes using MeshView。
本教程将图像映射到棱锥体的表面,就像您问题中的网格。它使用照片图像,但在您的情况下,您将使用不同的纯色区域。请特别查看纹理坐标部分。
点击教程中的图像,它将显示一个将纹理坐标映射到图像上的整齐覆盖。这确实有助于可视化正在发生的事情。
这篇关于如何给三角网格中的一些三角形上色?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!