在 JavaFX 中将 2D 文本用于 3D 场景会导致文本模糊
Use 2D Text into 3D scenes in JavaFX results in blurry texts
我正在尝试在 3D 场景中显示各种文本对象
问题是,当我放大到接近文本时,它看起来非常模糊。
这是我的应用程序的屏幕截图
问题:如何提高屏幕上文字的图形质量?
我试过设置缓存,如 3D 场景中带背景的 JavaFX 2D 文本
因为它似乎提高了答案屏幕截图的图形质量,但对我的情况没有帮助。
下面是从我的应用程序中删除的可编译示例。它只是创建一些带有位置的随机文本对象,并为您提供一种四处移动的方式。请注意当您放大时文本显示的非常糟糕。
你 F10 和 F11 放大和缩小,鼠标拖动 移动相机。
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);
}
}
您尝试执行的操作与参考的 之间的主要区别在于相机移动:缓存在该问题中有效,因为在旋转节点时,相机保持在同一位置。
但在您的情况下,您在给定位置以给定质量渲染文本节点,并且当您开始平移相机时,这些节点不会以不同方式呈现(具有不同大小或质量),因此它们变得模糊。
如果缓存不起作用,还有另一种可能性,如其他 中所建议:使用 3D 节点。
这有明显的处理3D对象的成本,比2D文本节点更重。
主要思想是:对于给定的文本,我们将文本写入图像,我们将使用此图像作为 3D 长方体的漫反射贴图。然后我们将长方体添加到子场景并转换为 x,y,z 坐标。
我们需要来自 FXyz3D library 的 CuboidMesh
,因为 built-in 3D Box
将相同的图像放置在每张脸上,所以这是行不通的。我们也可以使用自定义 TriangleMesh
实现 3D 棱镜,但是长方体已经提供了。
然后我们可以为长方体生成一个网络图像,并设置它,这样我们就有了想要的卡片:
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;
}
卡片看起来很暗,但是通过环境光我们可以去除它的深色背景。
让我们生成 100 张卡片:
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);
}
最后设置子场景:
subScene.setFill(Color.WHITESMOKE);
subSceneRoot.getChildren().add(new AmbientLight(Color.WHITE));
和运行它:
并用相机变焦:
文字看起来不再模糊了。如果您仍然想要更高的分辨率,您可以创建更大的快照,但这会带来内存成本。
还有一个小问题,卡片不是透明的,会遮住后面的卡片(我尽量降低高度)。
我正在尝试在 3D 场景中显示各种文本对象
问题是,当我放大到接近文本时,它看起来非常模糊。
这是我的应用程序的屏幕截图
问题:如何提高屏幕上文字的图形质量?
我试过设置缓存,如 3D 场景中带背景的 JavaFX 2D 文本 因为它似乎提高了答案屏幕截图的图形质量,但对我的情况没有帮助。
下面是从我的应用程序中删除的可编译示例。它只是创建一些带有位置的随机文本对象,并为您提供一种四处移动的方式。请注意当您放大时文本显示的非常糟糕。
你 F10 和 F11 放大和缩小,鼠标拖动 移动相机。
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);
}
}
您尝试执行的操作与参考的
但在您的情况下,您在给定位置以给定质量渲染文本节点,并且当您开始平移相机时,这些节点不会以不同方式呈现(具有不同大小或质量),因此它们变得模糊。
如果缓存不起作用,还有另一种可能性,如其他
这有明显的处理3D对象的成本,比2D文本节点更重。
主要思想是:对于给定的文本,我们将文本写入图像,我们将使用此图像作为 3D 长方体的漫反射贴图。然后我们将长方体添加到子场景并转换为 x,y,z 坐标。
我们需要来自 FXyz3D library 的 CuboidMesh
,因为 built-in 3D Box
将相同的图像放置在每张脸上,所以这是行不通的。我们也可以使用自定义 TriangleMesh
实现 3D 棱镜,但是长方体已经提供了。
然后我们可以为长方体生成一个网络图像,并设置它,这样我们就有了想要的卡片:
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;
}
卡片看起来很暗,但是通过环境光我们可以去除它的深色背景。
让我们生成 100 张卡片:
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);
}
最后设置子场景:
subScene.setFill(Color.WHITESMOKE);
subSceneRoot.getChildren().add(new AmbientLight(Color.WHITE));
和运行它:
并用相机变焦:
文字看起来不再模糊了。如果您仍然想要更高的分辨率,您可以创建更大的快照,但这会带来内存成本。
还有一个小问题,卡片不是透明的,会遮住后面的卡片(我尽量降低高度)。