使用 libGDX 以小比例 (<0.5) 渲染字体时出现问题

Issue when rendering fonts with libGDX at small (<0.5) scales

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

在 0.5F 比例下(正常):

比例为 0.2F (???):

比例为 0.1F (???):

完整 minimal reproducible example(减去 build/project 个文件):

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 没有什么区别。)我应该 post 一个单独的问题吗?

你需要使用

font.setUseIntegerPositions(false)

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


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

如果您要在 3D 中绘制文本,最好使用 MipMapLinearNearestMipMapLinearLinear 作为缩小过滤器 space。这将允许 OpenGL 在源图像像素之间进行插值,使文本看起来清晰。 MipMapLinearLinear 有这两个选择中更好的外观,代价是一些 GPU。我总是使用 MipMapLinearLinear 并且从来没有文本覆盖足够大的屏幕以致于对性能产生重大影响。

要在您的位图字体文件中修复此问题,请找到此行:

filter: Nearest,Nearest

并将其更改为

filter: MipMapLinearLinear,Linear

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

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