SourceForge.net Logo
Home
Documentation
Tutorials
    Beginning
    Gears
    Noise
    Bumpmapping
    Point sprites
    Hello World
    Armature
    Geometryshader
    Toolscripting
    Keyframe animation
    Instancing
    Bezier Surface
    Deferred shading
    Depth peeling
    Particle System
Links
Support This Project
OpenGL
This article need Lumina 0.1.4.

In GLSL it's not a good idea to start with "Hello World!", because it's a little bit
more complex. Many libraries for printing or drawing text in text in OpenGL are
character based. Each char needs an own call or diplaylist. For this tutorial we need a
font texture with 16x16 chars. The charset should be a latin1 or similar.





The most work will be done by the fragmentshader:

uniform sampler2D Textmap;

uniform sampler2D Fontmap;
uniform vec2 size;

void main(void){
float cha = texture2D(Textmap, vec2(gl_TexCoord[0])).a * 255.0/256.0;
vec2 coord = vec2(fract(16.0 * cha ), floor(16.0 - cha * 16.01)/ 16.0);
coord += fract( gl_TexCoord[0].xy * size)/vec2(16.0,16.0);
gl_FragColor = texture2D(Fontmap,coord );
}

There are two textur samplers. The Textmap is a one channel alpha texture that stores
the "text buffer". Each texel represents one char. Printing is very simple with:
glTexSubImage2D(GL_TEXTURE_2D, 0, x_position, y_position, length_of_text , 1, GL_ALPHA,
GL_UNSIGNED_BYTE, "text"); Note that it's important to use the gl.NEAREST filter.

The second texture is the font map. gl.LINEAR filter is possible, but
gl.LINEAR_MIPMAP_LINEAR, will be rendered with artifacts at the char tiles borders.

The uniform "size" stores the size of text area.

The 1st line in the main() fetch the char.
The 2nd calculates the offset for the right char.
The 3rd add the fraction to the texcoords.
The last line fetch the the texel from the font map


There is no special vertex shader needed:
void main(void){

gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
gl_TexCoord[0] = gl_MultiTexCoord0;
}



A smal script to use the shader with Lumina:
shader = gl.Shader(Vertexshader,Fragmentshader);

shader.Uniformi("Fontmap",1);
shader.Uniformi("Textmap",0);
shader.Uniform("size",16,16); // Texture size
tan = shader.Loc("Tangent");

Textmap.Image2d(16,16,gl.ALPHA8);
Textmap.MinFilter = gl.NEAREST; //Fix: Set the Filter to nearest instead linear
Textmap.MagFilter = gl.NEAREST;

//Simple wrapper for glTexSubImage2D
for (i = 0 ; i < 16 ; i ++)Textmap.Print(0,i,"Hello World ! ");

function render(){
Fontmap.Bind(1);
Textmap.Bind(0);
gl.Rotate(60 * World.getTime(), 0,0,1);

shader.Bind();

Sphere.UvCoords.Bind();
Sphere.Vertex.Bind();

Sphere.Index.Draw();

Sphere.UvCoords.Unbind();
Sphere.Vertex.Unbind();

shader.Unbind();
}





It's easy to scroll text with this shader, a simple modification in the vertex shader is
needed (texture matrix or addition with a uniform var). That can help to build a quake
like overlay console, without complex drawing routines. Only bind the two textures, the
shader and draw a quad....


Improvement:

It's possible to render text with mipmaping and without artifacts by using
the "ARB_shader_texture_lod" extension described int the
OpenGL Pipeline Newsletter.
Th calculate the correct lod, it's only needed to pass the correct texture coordinates
gradients to the texture mapping unit:
#extension GL_ARB_shader_texture_lod : enable

uniform sampler2D Textmap;
uniform sampler2D Fontmap;
uniform vec2 size;

void main(void){
float cha = texture2D(Textmap, vec2(gl_TexCoord[0])).a * 255.0/256.0;
vec2 coord = vec2(fract(16.0 * cha ), floor(16.0 - cha * 16.01)/ 16.0);
coord += fract( gl_TexCoord[0].xy * size)/vec2(16.0,16.0);
vec4 c =
texture2DGrad(Fontmap,coord,dFdx(gl_TexCoord[0].xy),dFdy(gl_TexCoord[0].xy));
gl_FragColor = c * c.a + 0.5 * (1.0 - c.a);
}


EDIT