How to render 3D graphics properly

一笑奈何 提交于 2019-12-06 15:39:58
Marco13

EDIT:

The reason for the rendering artifacts that was originally given here was wrong, and the proposed solution may not be appropriate*. Details can be found in the Revision History. The actual solution is far simpler. Apologies for any inconveniences.

The reason for the rendering artifacts is that your camera clip planes are too far apart. You are setting

camera.setNearClip(0.00001);
camera.setFarClip(10000000.0);

which is far beyond what can sensibly be represented in a normal Z-buffer. Changing these lines to

camera.setNearClip(0.1);
camera.setFarClip(10000.0);

will fix the rendering errors.


* The original solution suggested to model the boxes from several Mesh instances. This has the advantage that it allows defining normals and thus to achieve a "realistic" looking 3D effect, but involves a bit more effort. See the Revision History for the "real 3D" solution.

José Pereda

While @Marco13 is a great and valid answer, if you don't want to import a model, as @jewelsea mentions, there is also a way to create one single mesh for each cube and color the faces as required for the Rubik's Cube.

This can be achieved by using a net image to color the faces of the mesh:

If you apply this image to a Box:

Box cube = new Box();
PhongMaterial material = new PhongMaterial();
material.setDiffuseMap(new Image(getClass().getResource("cubeNet.png").toExternalForm()));
cube.setMaterial(material);

you will get the same image repeated for the six faces.

So you can create your own box to map properly the texture coordinates, or use CuboidMesh from FXyz library.

CuboidMesh cube = new CuboidMesh();
cube.setTextureModeImage(getClass().getResource("cubeNet.png").toExternalForm());

This post shows you how different faces of a single mesh can be colored.

EDIT

Given that for any of the 27 cubies, a different image should be provided, a better approach is just using an image like this one:

and then modify the texture indices accordingly, as explained in this answer.

Basically, for each color:

public static final int RED     = 0;
public static final int GREEN   = 1;
public static final int BLUE    = 2;
public static final int YELLOW  = 3;
public static final int ORANGE  = 4;
public static final int WHITE   = 5;
public static final int GRAY    = 6;

its normalized x texture coordinate will be:

public static final float X_RED     = 0.5f / 7f;
public static final float X_GREEN   = 1.5f / 7f;
public static final float X_BLUE    = 2.5f / 7f;
public static final float X_YELLOW  = 3.5f / 7f;
public static final float X_ORANGE  = 4.5f / 7f;
public static final float X_WHITE   = 5.5f / 7f;
public static final float X_GRAY    = 6.5f / 7f;

So using a TriangleMesh to create a box:

private TriangleMesh createCube(int[] face) {
    TriangleMesh m = new TriangleMesh();
    m.getPoints().addAll(
     0.5f,  0.5f,  0.5f,
     0.5f, -0.5f,  0.5f,
     0.5f,  0.5f, -0.5f,
     0.5f, -0.5f, -0.5f,
    -0.5f,  0.5f,  0.5f,
    -0.5f, -0.5f,  0.5f,
    -0.5f,  0.5f, -0.5f,
    -0.5f, -0.5f, -0.5f
    );
    m.getTexCoords().addAll(
     X_RED, 0.5f, 
     X_GREEN, 0.5f,
     X_BLUE, 0.5f, 
     X_YELLOW, 0.5f, 
     X_ORANGE, 0.5f,  
     X_WHITE, 0.5f,
     X_GRAY, 0.5f
    );

Finally, we just need to add the faces: a list of vertices and texture indices. Let's create an array of indices ordered based on the common notation of faces on the Rubik's cube: F - R - U - B - L - D:

    m.getFaces().addAll(
     2, face[0], 3, face[0], 6, face[0],      // F      
     3, face[0], 7, face[0], 6, face[0],  

     0, face[1], 1, face[1], 2, face[1],      // R     
     2, face[1], 1, face[1], 3, face[1],         

     1, face[2], 5, face[2], 3, face[2],      // U   
     5, face[2], 7, face[2], 3, face[2],

     0, face[3], 4, face[3], 1, face[3],      // B      
     4, face[3], 5, face[3], 1, face[3],       

     4, face[4], 6, face[4], 5, face[4],      // L      
     6, face[4], 7, face[4], 5, face[4],    

     0, face[5], 2, face[5], 4, face[5],      // D      
     2, face[5], 6, face[5], 4, face[5]         
    );
    return m;
}

Now it's very simple to create the cube and its 27 cubies, based on a single cubie and a pattern of colors.

This code

int[] p = new int[]{BLUE, GRAY, GRAY, GRAY, ORANGE, WHITE};
MeshView meshP = new MeshView();
meshP.setMesh(createCube(p));
PhongMaterial mat = new PhongMaterial();
mat.setDiffuseMap(new Image(getClass().getResourceAsStream("palette.png")));
meshP.setMaterial(mat);

will create the cubie for the front-right-up position.

Defining the 27 positions, this will be the Rubik's cube:

The code required to create it can be found here.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!