Reputation: 15309
I have created a BitmapFont
for my libGDX game and want to render it in front of the player (in a constant position in space, i.e. like a sign and not like a HUD). Sadly the rendering behaves weird at small scales, so much that the text becomes unreadable. What's going on and is there any way to prevent this?
Full minimal reproducible example (minus build/project files):
font.data.setScale(-0.5F, 0.5F)
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()
}
}
}
Additionally, capturing the cursor works at first but then breaks (If I enter fullscreen with F
and then exit with G
, the cursor can escape on all sides. I can also escape in fullscreen to my second monitor. setCursorPosition
does not make a difference.) Should I post a separate question for this?
Upvotes: 2
Views: 421
Reputation: 93591
You need to use
font.setUseIntegerPositions(false)
so it doesn't round off each character's position to an integer coordinate. It doesn't make sense to do that in 3D space, and it's causing your letters to fall on top of each other. (The reason libGDX does this by default is that it causes text rendered in 2D at pixel perfect scale to look crisper.)
Background: Your font appears to use Nearest
as the minification filter. Nearest filtering means for each screen pixel, it picks the nearest coordinate from the texture and shows that as the color of the screen pixel. When a texture is drawn smaller than it's source image size relative to screen pixel size and you're using Nearest
filtering, you will have ugly artifacts like your screenshots show.
It is best to use either MipMapLinearNearest
or MipMapLinearLinear
as your minifcation filter if you are drawing your text in 3D space. This will allow OpenGL to interpolate between source image pixels so the text will look crisp. MipMapLinearLinear
has the better appearance of these two choices, at the cost of some GPU. I always use MipMapLinearLinear
and have never had text covering enough of the screen that the performance impact would ever be significant.
To fix this in your bitmap font file, find this line:
filter: Nearest,Nearest
and change it to
filter: MipMapLinearLinear,Linear
The last Linear
is to set the magnification filter to interpolate, so the text won't look as pixelated when you get close to it.
Also, there should be a setting for this in Heiro or BMFont or whatever app you're using to create your font, so you shouldn't have to manually edit the font file's text.
Upvotes: 3