我猜这可能已作为重复项关闭(有关潜在重复项的引用,请参阅资源部分)。
但是,我认为这个问题的框架很有趣,而且非常独特,它使用 FXML 定义了大部分模型,所以我想我应该将其改编为答案。
高级步骤
- 您需要为 PhongMaterial 提供一个带有diffuseMap 的图像,该图像将成为您的模型的纹理。
- 您需要在模型中定义 texCoords,它映射到diffuseMap 图像中的位置(它是一个范围为0 到1 的比例图)。
- 定义面时,需要在纹理贴图中指定索引,用于为面的顶点着色。
完整的解释超出了我现在准备在这里写的内容,但我建议您参考其他资源,如果需要,您可以在其中找到更多信息。
输出
这是渲染为 gif 的,因此保真度不是很好,PC 上的实际输出看起来更好,而且 gif 输出速度大大加快,但它确实表明了解决方案的作用。
纹理.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);
}
}
金字塔网格.fxml
纹理是定义为 PhongMaterial 的漫反射贴图的图像,该 PhongMaterial 是为作为网格节点的 MeshView 指定的。
在此示例中,材料是在 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 中定义尽可能多的数据而不使用
外部库?
关于复杂模型和重复使用预制模型的建议
对于复杂的模型,我建议不要在 FXML 中定义网格数据(面/顶点/纹理坐标),而是建议使用其他部分支持的标准 3d 格式(例如 .obj 或 .stl)软件。
网络上有一些常用格式的预创建模型(但不包括 fxml)。这样的模型可以附带必要的图像映射和纹理坐标定义来为它们着色。
您可以使用适用于各种 3D 模型格式的第三方导入器库将这些模型导入 JavaFX。如果您搜索,可以在网络上找到这些 JavaFX 导入器库,例如InteractiveMesh 和 f(x)yz 库。
相关问题
- Coloring individual triangles in a triangle mesh on javafx
- How to render 3D graphics properly
- 我认为 Jose 对 Rubik 的立方体覆盖的这个问题的回答非常适合您的应用程序,即使问题标题是通用的。
-
How to create a cube with all faces of different colors in JavaFX
-
applying texture to mesh in javafx
-
How to create a cube with all faces of different colors in JavaFX
-
Create a cube using different textures in JavaFX
-
How to create such shape using javaFx TriangleMesh?
强烈推荐了解带纹理的 3D 网格的教程
Create 3D Shapes using MeshView 是一个非常好的教程(比我想出的任何东西都好)。
本教程将图像映射到金字塔的面,就像您问题中的网格一样。它使用照片图像,但在您的情况下,您将使用不同的纯色区域。特别注意纹理坐标部分。
单击教程中的图像,它将显示一个将纹理坐标映射到图像上的整洁叠加层。这确实有助于可视化正在发生的事情。