JavaFX 3D球体-纹理映射保存比例?

4
我想创建一个JavaFX 3D球体,用地球的贴图来渲染。这个贴图是从维基百科获得的,属于等距方位投影: enter image description here 球体的渲染如下所示: enter image description here 明显可以看到,在极点处,贴图不再保持比例。我在 openJDK 系统上找到了一个 bug 文件,我认为它与这种行为有关:https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8092112
遗憾的是,在7年内,没有人采取了提出 bug 的人所请求的更改。您是否知道在JavaFX 3D球体上正确渲染等距球面投影的替代方法?
只是作为参考,我的代码如下:
    Sphere earthSphere = new Sphere(EARTH_RADIUS, 256);
    PhongMaterial material = new PhongMaterial();
    material.setDiffuseMap(new Image(Main.class.getResourceAsStream("/images/earth2.jpg")));
    earthSphere.setMaterial(material);

2
也许您可以在安装中修补Sphere类,使用bug报告中的建议修复。或者,您可以修改输入纹理,使默认的JavaFX球体纹理映射器适用于您的纹理,但我不知道如何操作。 - jewelsea
1
如果有人想知道地球应该是什么样子的,可以在nasa上看到它的模型。 - jewelsea
1个回答

2

最后,我使用网格自己实现了它,以满足我的需求。以下是代码(如果您感兴趣,它最终会出现在GitHub项目中):

public static Group createEarthSphere() {
    // Use triangular mesh
    int latLevels = 90;
    int lonLevels = 180;

    TriangleMesh mesh = new TriangleMesh(VertexFormat.POINT_NORMAL_TEXCOORD);
    double radius = EARTH_RADIUS;

    double latIncAngle = (Math.PI/latLevels);
    double lonIncAngle = (Math.PI * 2)/lonLevels;
    double textLatIncr = 1.0/latLevels;
    double textLonIncr = 1.0/lonLevels;

    int currentPointOffset = 0;
    int currentNormalOffset = 0;
    int currentTextOffset = 0;
    for(int i = 0; i < latLevels; ++i) {
        for(int j = 0; j < lonLevels; ++j) {
            // The point list is: top left - bottom left - bottom right - top right
            // The faces-normal points are: (0,0) (1,1) (2,2) (0,3) (2,4) (3,5)
            Point3D tp1 = new Point3D(0,radius * Math.cos(Math.PI - (i * latIncAngle)), radius * Math.sin(Math.PI - (i * latIncAngle)));
            Point3D tp2 = new Point3D(0,radius * Math.cos(Math.PI - (i * latIncAngle + latIncAngle)), radius * Math.sin(Math.PI - (i * latIncAngle + latIncAngle)));
            Point3D topLeft = new Rotate(Math.toDegrees(j * lonIncAngle), new Point3D(0, 1, 0)).transform(tp1);
            Point3D bottomLeft =  new Rotate(Math.toDegrees(j * lonIncAngle), new Point3D(0, 1, 0)).transform(tp2);
            Point3D bottomRight = new Rotate(Math.toDegrees(j * lonIncAngle + lonIncAngle), new Point3D(0, 1, 0)).transform(tp2);
            Point3D topRight = new Rotate(Math.toDegrees(j * lonIncAngle + lonIncAngle), new Point3D(0, 1, 0)).transform(tp1);

            // Compute normals
            Point3D topLeftNormal_1 = computeNormal(topLeft, bottomLeft, bottomRight); // 0
            Point3D bottomLeftNormal_1 = computeNormal(bottomLeft, bottomRight, topLeft); // 1
            Point3D bottomRightNormal_1 = computeNormal(bottomRight, topLeft, bottomLeft); // 2
            Point3D topLeftNormal_2 = computeNormal(topLeft, bottomRight, topRight); // 3
            Point3D bottomRightNormal_2 = computeNormal(bottomRight, topRight, topLeft); // 4
            Point3D topRightNormal_2 = computeNormal(topRight, topLeft, bottomRight); // 5

            // Add points
            mesh.getPoints().addAll((float) topLeft.getX(), (float) topLeft.getY(), (float) topLeft.getZ()); // 0
            mesh.getPoints().addAll((float) bottomLeft.getX(), (float) bottomLeft.getY(), (float) bottomLeft.getZ()); // 1
            mesh.getPoints().addAll((float) bottomRight.getX(), (float) bottomRight.getY(), (float) bottomRight.getZ()); // 2
            mesh.getPoints().addAll((float) topRight.getX(), (float) topRight.getY(), (float) topRight.getZ()); // 3

            // Add normals
            mesh.getNormals().addAll((float) topLeftNormal_1.getX(), (float) topLeftNormal_1.getY(), (float) topLeftNormal_1.getZ()); // 0
            mesh.getNormals().addAll((float) bottomLeftNormal_1.getX(), (float) bottomLeftNormal_1.getY(), (float) bottomLeftNormal_1.getZ()); // 1
            mesh.getNormals().addAll((float) bottomRightNormal_1.getX(), (float) bottomRightNormal_1.getY(), (float) bottomRightNormal_1.getZ()); // 2
            mesh.getNormals().addAll((float) topLeftNormal_2.getX(), (float) topLeftNormal_2.getY(), (float) topLeftNormal_2.getZ()); // 3
            mesh.getNormals().addAll((float) bottomRightNormal_2.getX(), (float) bottomRightNormal_2.getY(), (float) bottomRightNormal_2.getZ()); // 4
            mesh.getNormals().addAll((float) topRightNormal_2.getX(), (float) topRightNormal_2.getY(), (float) topRightNormal_2.getZ()); // 5

            // Add texture
            float[] p0t = { (float) (i * textLatIncr), 1.0f - (float) (j * textLonIncr) };
            float[] p1t = { (float) (i * textLatIncr + textLatIncr), 1.0f - (float) (j * textLonIncr) };
            float[] p2t = { (float) (i * textLatIncr + textLatIncr), 1.0f - (float) (j * textLonIncr + textLonIncr) };
            float[] p3t = { (float) (i * textLatIncr), 1.0f - (float) (j * textLonIncr + textLonIncr) };

            mesh.getTexCoords().addAll(
                    p0t[1], p0t[0],
                    p1t[1], p1t[0],
                    p2t[1], p2t[0],
                    p3t[1], p3t[0]
            );

            // Add faces
            mesh.getFaces().addAll(
                    currentPointOffset + 0, currentNormalOffset + 0, currentTextOffset + 0,
                    currentPointOffset + 2, currentNormalOffset + 2, currentTextOffset + 2,
                    currentPointOffset + 1, currentNormalOffset + 1, currentTextOffset + 1,
                    currentPointOffset + 0, currentNormalOffset + 3, currentTextOffset + 0,
                    currentPointOffset + 3, currentNormalOffset + 5, currentTextOffset + 3,
                    currentPointOffset + 2, currentNormalOffset + 4, currentTextOffset + 2

            );

            currentPointOffset += 4;
            currentNormalOffset += 6;
            currentTextOffset += 4;
        }
    }

    MeshView meshView = new MeshView(mesh);
    meshView.setCullFace(CullFace.BACK);
    PhongMaterial material = new PhongMaterial();
    material.setDiffuseMap(new Image(Main.class.getResourceAsStream("/images/earth.jpg")));
    meshView.setMaterial(material);
    return new Group(meshView);
}

private static Point3D computeNormal(Point3D p1, Point3D p2, Point3D p3) {
    return (p3.subtract(p1).normalize()).crossProduct(p2.subtract(p1).normalize()).normalize();
}

结果如下:

结果是:

enter image description here

现在一切都恰到好处,经度/纬度与纹理正确匹配。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接