首页 > 解决方案 > 使用 libGDX 以小 (<0.5) 比例渲染字体时出现问题

问题描述

BitmapFont为我的 libGDX 游戏创建了一个并希望在玩家面前渲染它(在空间中的恒定位置,即像一个标志而不像一个 HUD)。可悲的是,渲染在小范围内表现得很奇怪,以至于文本变得不可读。这是怎么回事,有什么办法可以防止这种情况发生吗?

在刻度 0.5F (ok):在此处输入图像描述

在 0.2F (???) 时:在此处输入图像描述

在比例 0.1F (???):在此处输入图像描述

完整的最小可重现示例(减去构建/项目文件):

package xjcl.extracredits2020

import com.badlogic.gdx.*
import com.badlogic.gdx.graphics.*
import com.badlogic.gdx.graphics.g2d.*
import com.badlogic.gdx.math.Vector3

class RangeAnxietyGame : ApplicationAdapter() {
    lateinit var cam: PerspectiveCamera
    lateinit var spriteBatch: SpriteBatch
    lateinit var font: BitmapFont

    override fun create() {
        Gdx.input.apply { isCursorCatched = true }
        spriteBatch = SpriteBatch()
        font = BitmapFont()
        cam = PerspectiveCamera(67F, Gdx.graphics.width.toFloat(), Gdx.graphics.height.toFloat()).apply {
            position.set(Vector3(0f, 1.8f, 0f))
            lookAt(0f, 1.8f, 1f)
            update()
        }
    }

    override fun render() {
        Gdx.gl.glViewport(0, 0, Gdx.graphics.width, Gdx.graphics.height)
        Gdx.gl.glClearColor(0f, 0f, 0f, 1f)
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT or GL20.GL_DEPTH_BUFFER_BIT)

        if (Gdx.input.isKeyPressed(Input.Keys.F))
            Gdx.graphics.setFullscreenMode(Gdx.graphics.displayMode)
        if (Gdx.input.isKeyPressed(Input.Keys.G))
            Gdx.graphics.setWindowedMode(1280, 720)
        if (Gdx.input.isKeyPressed(Input.Keys.ESCAPE))
            Gdx.app.exit()

        val dt = Gdx.graphics.deltaTime  // seconds
        cam.apply {
            rotate(Gdx.input.deltaX * -20*dt, 0F, 1F, 0F)
            rotate(up.cpy().crs(direction), Gdx.input.deltaY * 10*dt)
            Gdx.input.setCursorPosition(Gdx.graphics.width/2, Gdx.graphics.height/2)
            update()
        }

        spriteBatch.apply {  // goal text -- bad and hacky
            begin()
            projectionMatrix = cam.combined.cpy().translate(0F, 0F, 50F)
            font.data.setScale(-0.5F, 0.5F)
            font.draw(this, "${cam.position.z.toInt()}m/50m\n${Gdx.graphics.width}x${Gdx.graphics.height} " +
                    "${(1 / dt).toInt()}fps\nat scale (-0.5F, 0.5F)", 0F, 0F)
            end()
        }
    }
}

此外,捕获光标首先起作用,但随后中断(如果我进入全屏,F然后退出G,光标可以在所有方面逃逸。我也可以全屏逃逸到我的第二台显示器。setCursorPosition没有什么区别。)我应该为此发布一个单独的问题?

标签: javakotlinfontslibgdx

解决方案


你需要使用

font.setUseIntegerPositions(false)

所以它不会将每个字符的位置四舍五入为整数坐标。在 3D 空间中这样做是没有意义的,它会导致你的字母相互重叠。(libGDX 默认这样做的原因是它会导致以像素完美比例以 2D 呈现的文本看起来更清晰。)

在此处输入图像描述


背景:您的字体似乎Nearest用作缩小过滤器。最近的过滤意味着对于每个屏幕像素,它从纹理中选择最近的坐标并将其显示为屏幕像素的颜色。当绘制的纹理小于相对于屏幕像素大小的源图像大小并且您正在使用Nearest过滤时,您将拥有像屏幕截图所示的丑陋伪影。

如果您在 3D 空间中绘制文本,最好使用其中一个MipMapLinearNearest或作为您的缩小过滤器。MipMapLinearLinear这将允许 OpenGL 在源图像像素之间进行插值,使文本看起来清晰。MipMapLinearLinear有这两个选择的更好的外观,代价是一些GPU。我总是使用MipMapLinearLinear并且从未有过足够覆盖屏幕的文本,以至于对性能的影响会非常显着。

要在位图字体文件中解决此问题,请找到以下行:

filter: Nearest,Nearest

并将其更改为

filter: MipMapLinearLinear,Linear

最后Linear是将放大过滤器设置为插值,这样当您靠近它时,文本看起来不会像像素化。

此外,在 Heiro 或 BMFont 或您用于创建字体的任何应用程序中应该有一个设置,因此您不必手动编辑字体文件的文本。


推荐阅读