You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Java/JavaFX中基于2D点或路径创建自定义3D模型

在JavaFX中实现自定义带面的3D模型解决方案

核心思路

要实现类似Photoshop的带上下底面及侧面的3D模型,无需依赖第三方库,直接用JavaFX原生的TriangleMesh即可完成。核心是将用户选取的2D多边形(已转为3D点),拆解为上底面、下底面、侧面的三角面,并正确配置顶点、纹理坐标与三角面索引。

步骤分解

1. 生成上下底面的3D顶点

基于用户已有的上底面3D点,生成下底面顶点(Z值取上底面Z减去模型厚度,厚度可由用户指定):

// 用户已生成的上底面3D点
List<Point3D> topPoints = points3D;
// 生成下底面顶点,厚度设为24(可替换为用户输入值)
List<Point3D> bottomPoints = new ArrayList<>();
double thickness = 24;
for (Point3D p : topPoints) {
    bottomPoints.add(new Point3D(p.getX(), p.getY(), p.getZ() - thickness));
}

2. 构建TriangleMesh的顶点数组

将上下底面的顶点按X、Y、Z顺序存入float数组,作为网格的顶点数据:

int totalVertex = topPoints.size() + bottomPoints.size();
float[] meshPoints = new float[totalVertex * 3];
int idx = 0;

// 写入上底面顶点
for (Point3D p : topPoints) {
    meshPoints[idx++] = (float)p.getX();
    meshPoints[idx++] = (float)p.getY();
    meshPoints[idx++] = (float)p.getZ();
}
// 写入下底面顶点
for (Point3D p : bottomPoints) {
    meshPoints[idx++] = (float)p.getX();
    meshPoints[idx++] = (float)p.getY();
    meshPoints[idx++] = (float)p.getZ();
}

3. 配置纹理坐标(可选,用于贴石材原图)

如果需要给模型贴石材纹理,将Canvas上的2D点转换为0-1范围的纹理坐标(UV):

int texCount = topPoints.size();
float[] texCoords = new float[texCount * 2];
idx = 0;
double canvasWidth = canvas.getWidth();
double canvasHeight = canvas.getHeight();

// 转换原始2D点为纹理坐标(JavaFX纹理Y轴反向)
for (Point2D p2d : points) {
    texCoords[idx++] = (float)(p2d.getX() / canvasWidth);
    texCoords[idx++] = (float)(1 - p2d.getY() / canvasHeight);
}

// 下底面复用上底面的纹理坐标
float[] fullTexCoords = new float[texCoords.length * 2];
System.arraycopy(texCoords, 0, fullTexCoords, 0, texCoords.length);
System.arraycopy(texCoords, 0, fullTexCoords, texCoords.length, texCoords.length);

4. 生成三角面索引

这是保证模型正确显示的关键,分别生成上底面、下底面、侧面的三角面:

List<Integer> faceIndices = new ArrayList<>();
int topVertexCount = topPoints.size();

// 上底面三角面(假设顶点按顺时针/逆时针有序排列)
for (int i = 1; i < topVertexCount - 1; i++) {
    // 格式:顶点索引,纹理索引, 顶点索引,纹理索引, 顶点索引,纹理索引
    faceIndices.add(0); faceIndices.add(0);
    faceIndices.add(i); faceIndices.add(i);
    faceIndices.add(i+1); faceIndices.add(i+1);
}

// 下底面三角面(反转顶点顺序,保证法线方向正确)
for (int i = 1; i < topVertexCount - 1; i++) {
    faceIndices.add(topVertexCount); faceIndices.add(0);
    faceIndices.add(topVertexCount + i + 1); faceIndices.add(i+1);
    faceIndices.add(topVertexCount + i); faceIndices.add(i);
}

// 侧面三角面(连接上下底面相邻顶点)
for (int i = 0; i < topVertexCount; i++) {
    int nextIdx = (i + 1) % topVertexCount;
    // 第一个三角形
    faceIndices.add(i); faceIndices.add(i);
    faceIndices.add(nextIdx); faceIndices.add(nextIdx);
    faceIndices.add(topVertexCount + i); faceIndices.add(i);
    // 第二个三角形
    faceIndices.add(nextIdx); faceIndices.add(nextIdx);
    faceIndices.add(topVertexCount + nextIdx); faceIndices.add(nextIdx);
    faceIndices.add(topVertexCount + i); faceIndices.add(i);
}

// 转换为int数组
int[] faceArray = new int[faceIndices.size()];
for (int i = 0; i < faceIndices.size(); i++) {
    faceArray[i] = faceIndices.get(i);
}

5. 创建MeshView并展示

将构建好的网格添加到3D场景,配置相机与光照:

TriangleMesh mesh = new TriangleMesh();
mesh.getPoints().setAll(meshPoints);
mesh.getTexCoords().setAll(fullTexCoords);
mesh.getFaces().setAll(faceArray);

MeshView meshView = new MeshView(mesh);
// 绑定石材纹理(可选)
Image stoneTexture = imageView.getImage();
meshView.setMaterial(new PhongMaterial(Color.WHITE, stoneTexture, null, null, null));

// 构建3D场景
Group root3d = new Group(meshView);
Scene scene = new Scene(root3d, 800, 600, true, SceneAntialiasing.BALANCED);
scene.setFill(Color.GRAY);

// 设置透视相机
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setTranslateZ(-1000); // 调整距离让模型完整显示
scene.setCamera(camera);

// 添加光照
AmbientLight ambientLight = new AmbientLight(Color.LIGHTGRAY);
PointLight pointLight = new PointLight(Color.WHITE);
pointLight.setTranslateX(500);
pointLight.setTranslateY(-300);
pointLight.setTranslateZ(-1000);
root3d.getChildren().addAll(ambientLight, pointLight);

// 显示窗口
Stage stage = new Stage();
stage.setScene(scene);
stage.show();

关键注意事项

  • 顶点顺序:用户选取的2D点必须按顺时针或逆时针有序排列,否则三角面会出现背面消隐问题。若点顺序混乱,需先按多边形中心点的极角排序。
  • 坐标偏移:Canvas原点在左上角,3D场景原点默认在中心,可通过平移顶点(如X减去Canvas宽度一半、Y减去高度一半)让模型居中。
  • 背面消隐调试:若模型显示不全,可临时设置meshView.setCullFace(CullFace.NONE)关闭背面消隐,最终需保证三角面法线方向正确。

内容的提问来源于stack exchange,提问作者FarHooD

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 06:30:17