I want to render text in LWJGL by using modern OpenGL (rendering with VBO and shader) but I have no idea how to do it.
The OpenGL Shading Language (GLSL) is the principal shading language for OpenGL. While, thanks to OpenGL Extensions, there are several shading languages available for use in OpenGL, GLSL (and SPIR-V) are supported directly by OpenGL without extensions.
The process of transforming font outlines into pixels is called rasterization. The operating system's text-rendering engine places the outline (ie the shape) of each character at the desired font size on a pixel grid. Next, it colours all the pixels whose centre is inside the outline (see image below).
Here is an approach:
class CharCoords {
public int x, y, width, height;
}
(0,0), (0,1), (1,0), (1,1)
#version 120
uniform mat4 PVMmat; // The projection-view-model matrix
uniform vec4 charCoords; // The CharCoord struct for the character you are rendering, {x, y, w, h}
uniform float texSize; // The size of the texture which contains the rasterized characters (assuming it is square)
uniform vec2 offset; // The offset at which to paint, w.r.t the first character
attribute vec2 vertex;
varying vec2 tc;
void main(){
// Transform from absolute texture coordinates to normalized texture coordinates
// This works because the rectangle spans [0,1] x [0,1]
// Depending on where the origin lies in your texture (i.e. topleft or bottom left corner), you need to replace "1. - vertex.y" with just "vertex.y"
tc = (charCoords.xy + charCoords.zw * vec2(vertex.x, 1. - vertex.y)) / texSize;
// Map the vertices of the unit square to a rectangle with correct aspect ratio and positioned at the correct offset
float x = (charCoords[2] * vertex.x + offset.x) / charCoords[3];
float y = vertex.y + offset.y / charCoords[3];
// Apply the model, view and projection transformations
gl_Position = PVMmat * vec4(x, y, 0., 1.);
}
#version 120
uniform vec4 color;
uniform sampler2D tex;
varying vec2 tc;
void main() {
gl_FragColor = color * texture2D(tex, tc);
}
public void drawString(Matrix4f PVMmat, String text, Color color, HAlign halign, VAlign valign) {
Vector2f offset = new Vector2f();
// Font alignment
if(halign == HAlign.Center){
offset.x = -(int) (0.5f * getWidth(text));
}else if(halign == HAlign.Right){
offset.x = -getWidth(text);
}
if(valign == VAlign.Middle){
offset.y = -(int) (0.5f * getHeight());
}else if(valign == VAlign.Top){
offset.y = -getHeight();
}
m_shader.bind();
m_shader.setAttributeBuffer("vertex", m_vertexBuffer, 2);
m_shader.setUniformMatrix("PVMmat", PVMmat);
m_shader.setUniformVector("color", color);
m_shader.setUniformScalar("texSize", (float)m_textureSize);
m_shader.setTexture("tex", m_fontTexture, GL11.GL_TEXTURE_2D);
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, m_model.getIndexBuffer());
for(int i = 0; i < text.length(); ++i) {
CharCoords coo = m_charMap.get(text.charAt(i));
m_shader.setUniformVector("charCoords", new Vector4f(coo.x, coo.y, coo.width, coo.height));
m_shader.setUniformVector("offset", offset);
GL11.glDrawElements(GL11.GL_TRIANGLES, m_indexCount, GL11.GL_UNSIGNED_INT, 0);
offset.x += coo.width;
}
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
m_shader.unbind();
}
where the functions getHeigth
and getWidth
are:
public int getWidth(String text) {
int totalwidth = 0;
for (int i = 0; i < text.length(); i++) {
CharCoords coo = m_charMap.get(text.charAt(i));
totalwidth += coo.width;
}
return totalwidth;
}
public int getHeight() {
return m_fontMetrics.getHeight();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With