Use 2D Text into 3D scenes in JavaFX results in blurry texts

浪尽此生 提交于 2021-02-08 06:34:50

问题


I'm trying to display various Text objects inside a 3D scene

The problem is that when i zoom close to the text it looks very blurry.

Here's a screenshot from my app

Question: how can i improve the graphic quality of the text on the screen?

I've tried setting cache as in JavaFX 2D text with background in 3D scene because it seemed to improve graphics quality on the answer screenshots but it did not help me in my case.

Below is a compileable example stripped down form my app. It just create some random Text objects with positions and provides you a way to move around. Notice how the texts displays horribly when you zoom close.

You F10 and F11 to zoom in and out and mouse drag to move the camera.

package timelinefx;

import static javafx.application.Application.launch;
import java.util.Random;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.SubScene;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;
import javafx.scene.CacheHint;


public class TextInto3DScene extends Application {
    static Stage stage;
    static Scene scene;
    static Group root;
    static Group subSceneRoot;
    static PerspectiveCamera camera;
    static SubScene subScene;
    static boolean cached = true;
    static int width = 1000;
    static int height = width;

    @Override
    public void start(Stage primaryStage) throws Exception {
        createWindow();
    }

    protected static void createWindow(){
        stage = new Stage();
        root = new Group();
            root.setCache(cached);
            root.setCacheHint(CacheHint.QUALITY); //doesn't work
        scene = new Scene(root, 1500, 700, true, SceneAntialiasing.BALANCED);
        //Initialize the camera
        camera = new PerspectiveCamera(true);
        camera.setNearClip(0.1);
        camera.setFarClip(5000);
        camera.setTranslateZ(-200);

        //creates the SubScene and add the camera to it!
        subSceneRoot = new Group();
            subSceneRoot.setCache(cached); //doesn't work

        subScene = new SubScene(subSceneRoot, 1800, 700, true, SceneAntialiasing.BALANCED);
            subScene.setCache(cached);
            root.getChildren().add(subScene); 
        subScene.setFill(Color.WHITE);
        subScene.setCamera(camera);

        //Create random texts
        for( int i = 0;i<=100;++i){
            Text text = new Text(Math.random()* width-width/2, Math.random()*height-height/2, randomText() );
            text.setFill(  Color.BLACK  );
            text.setTranslateZ( Math.random()*750 );
            text.setCache(cached); //doesn't work
            text.setCacheHint(CacheHint.QUALITY);
            subSceneRoot.getChildren().add(text);
        }
        stage.setScene(scene);
        setEventHandlers();
        stage.show();
    }

    static void setEventHandlers() {
        scene.setOnMouseDragged((event) -> {

            //camera.setRotate(event.getX());
            double translateX = (event.getSceneX() - 0) / stage.getWidth();
            translateX = translateX * width - width/2;
            double translateY = (event.getSceneY() - 0) / stage.getHeight();
            translateY = translateY * height - height/2;
            double tz;
            if(!camera.getTransforms().isEmpty()){
                tz = camera.getTransforms().get(0).getTz();
            } else {
                tz = camera.getTranslateZ();
            }
            camera.getTransforms().clear();
            camera.getTransforms().addAll(  new Translate(translateX, translateY, tz)     );
        });

        //KEY PRESSED
        scene.setOnKeyPressed((event) -> {
            switch (event.getCode().toString()) {
                case "F10":
                    double amt = event.isControlDown() ? 100 : 30;
                    camera.setTranslateZ(camera.getTranslateZ() + amt);
                    break;
                case "F11":
                    amt = event.isControlDown() ? -100 : -30;
                    camera.setTranslateZ(camera.getTranslateZ() + amt);
                    break;
            }
        });
    }

    static String randomText(){
        String out = "";
        Random r = new Random();
        for(int i = 0;i<=10;++i){
            char c = (char) (r.nextInt(26) + 'a');
            out += c;
        }
        return out;
    }

    public static void main(String[] args) {
        launch(args);
    }

}

回答1:


The main difference between what you are trying to do and the referred question is the camera movement: cache worked in that question because while rotating the node, the camera remained at the same position.

But in your case, you render the Text nodes at a given position, and with a given quality, and when you start translating the camera, these nodes are not rendered differently (with different size or quality), thus they become blurry.

If cache won't work, there is another possibility, as suggested in this other question: using 3D nodes.

This has the obvious cost of dealing with 3D objects, more heavy weight than the 2D text nodes.

The main idea is: for a given text, we'll write the text into an image, and we'll use this image as the diffuse map of a 3D cuboid. Then we'll add the cuboid to the sub scene and translate to the x,y,z coordinates.

We need the CuboidMesh from FXyz3D library, as the built-in 3D Box places the same image into every face, so that wouldn't work. We can implement a 3D prism with a custom TriangleMesh as well, but the cuboid already provides that.

Then we can generate a net image for the cuboid, and set it so we have the desired card:

private CuboidMesh generateCard(String message) {

    Text text5 = new Text(message);
    text5.setFont(Font.font("Arial", FontWeight.BLACK, FontPosture.REGULAR, 60));
    GridPane grid = new GridPane();
    grid.setAlignment(Pos.CENTER);

    CuboidMesh contentShape = new CuboidMesh(40, 8, 0.01);
    PhongMaterial shader = new PhongMaterial();

    GridPane.setHalignment(text5, HPos.CENTER);

    grid.add(text5, 3, 1);

    double w = contentShape.getWidth() * 10; // more resolution
    double h = contentShape.getHeight() * 10;
    double d = contentShape.getDepth() * 10;
    final double W = 2 * d + 2 * w;
    final double H = 2 * d + h;

    ColumnConstraints col1 = new ColumnConstraints();
    col1.setPercentWidth(d * 100 / W);
    ColumnConstraints col2 = new ColumnConstraints();
    col2.setPercentWidth(w * 100 / W);
    ColumnConstraints col3 = new ColumnConstraints();
    col3.setPercentWidth(d * 100 / W);
    ColumnConstraints col4 = new ColumnConstraints();
    col4.setPercentWidth(w * 100 / W);
    grid.getColumnConstraints().addAll(col1, col2, col3, col4);

    RowConstraints row1 = new RowConstraints();
    row1.setPercentHeight(d * 100 / H);
    RowConstraints row2 = new RowConstraints();
    row2.setPercentHeight(h * 100 / H);
    RowConstraints row3 = new RowConstraints();
    row3.setPercentHeight(d * 100 / H);
    grid.getRowConstraints().addAll(row1, row2, row3);
    grid.setPrefSize(W, H);
    grid.setBackground(new Background(new BackgroundFill(Color.WHITESMOKE, CornerRadii.EMPTY, Insets.EMPTY)));
    new Scene(grid);
    WritableImage image = grid.snapshot(null, null);
    shader.setDiffuseMap(image);
    contentShape.setMaterial(shader);
    return contentShape;
}    

The card looks dark, but with ambient light we can get rid of its dark background.

Let's generate the 100 cards:

for (int i = 0; i <= 100; i++) {
    CuboidMesh card = generateCard(randomText());
    card.setTranslateX(Math.random() * width - width / 2);
    card.setTranslateY(Math.random() * height - height / 2);
    card.setTranslateZ(Math.random() * 750);
    subSceneRoot.getChildren().add(card);
}

Finally set the subScene:

subScene.setFill(Color.WHITESMOKE);
subSceneRoot.getChildren().add(new AmbientLight(Color.WHITE));

and run it:

and zoom with the camera:

The text doesn't look blur anymore. If you still want more resolution you can create bigger snapshots, but that comes with a memory cost.

There is still a small issue, as the cards are not transparent and cover those behind them (I've tried to reduce their height as much as possible).



来源:https://stackoverflow.com/questions/49469089/use-2d-text-into-3d-scenes-in-javafx-results-in-blurry-texts

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