Distance fields for font rendering (Part 1)

Pushing myself forward again, I will publish some entries about implementing font rendering into a texture atlas, encoded with distance fields and with support for Unicode chars (the possibility to reduce the texture size will probably allow large amounts of glyphs in one single texture) to finally show proper labels and text boxes in Engendro3D.


 

By checking info about how to render fonts in OpenGL, I found this question, which lead me to the Valve’s paper (.pdf).

The results, along with the need to have huge amounts of chars pre-rendered to a font atlas for certain languages, got my interest.

Since the Distance fields looks slightly like blurred images, I made a quick png texture in Gimp containing 6 letters (with Eufm10 font) and I used the Gaussian Blur filter (10 radius) to produce this faked distance Field:

wtx_blured

Then, with a very simple GLSL fragment shader, this is the result:

dfblured

No Bilinear interpolation:

gblured_noint

Zoomed into the ‘B’:

zoomblur

No Bilinear interpolation:

zoomblur_noint

Has outline and glow and, while the result is not the best, the logic works.

The shader I wrote can probably be improved a lot, but I will use the same for all the tests I’ll do:

#version 110

uniform float time;
uniform sampler2D tex;

void main()
{
float color = texture2D(tex,gl_TexCoord[0].st).r;
	if (color <= 0.5)
		gl_FragColor = vec4(0,0,1,1);
	else
		if (color <= 0.6){
			gl_FragColor = vec4(1,1,1,1);}
		else
		{
			if (color < 1.0)
				gl_FragColor = max(0.0,1.0 - color) * 
				vec4(.9,.4,.2,1) * 2.0 * max(sin(-time),sin(time));
			else
				gl_FragColor = vec4(0.0);
		}
}

 

In the next part I will show a Distance Field generator in Python and Pillow (PIL).

GUIs quirks and quacks

 

In the search of a ready-to-use GUI system, I came across several options, but in the end I have decided to make my own, simply because none of the options was completely fittable to my needs, specifically for the next reasons:

Maybe one of the most famous options. Looks very good and has official Python bindings.
I never managed to make it work in either Linux nor Windows. In Linux, after four hours of compiling, and a previously failed process due to missing dependencies, I gave up.
In Windows, the compiling took only two hours, only after I fixed some errors thanks to this instructions:

http://knightforged.com/cegui/

After finishing, I realized that the python bindings were not selected, so I started again, just to found severals errors raised for the lack of Boost. Unfortunately, I neither managed to compile the Python bindings for Boost.

 

Is recommended, looks promising and it appears to be usable from Python, but… It needs boost, so no further research on that.

Maybe not very well known like the others, but they are completely Python based, so no need for Boost. I tried to convert both to plain PyOpenGL, since SimplUI uses Pyglet’s event system (very different to mine) and BGEUI is based on Blender Game Engine and Fixed function (wich I won’t use), but after some days I realized that the effort needed for a conversion-integration into my engine would end up taking too much time. Maybe near to the time needed to make my own. So I gave up with pre-made solutions.

 

Result, after three days of work, the first screenshot of the first ‘Panel control’ of my GUI:

gui

Right now, the widgets support opacity and are stretched automatically. In the picture seen over a 3D duck with specular reflection. I have already one directional light. Current cost of the panel: 1ms

It only uses four vertices in a VBO and an index buffer. Then 3 unifforms for each control’s position/size, color and background image. This setup establish the possibility to render the GUI controls either as normal 2D ‘widgets’ or as 3D objects attached to the scene models and, with a little more work, controls could be rendered using instancing where available (all the GUI in one draw call).

and the gestation continues.

PyOpenGL performance 2 and optimizations

Premature optimization is the root of all evil

Is it?

Note: frame times at the end.

The conversion of all modules from SharpDX to PyOpenGL is finished, so I ran the test program again. Result: 430 FPS (previously 649). Time to optimize.

First: check with cprofile module. Most time spent is in the shaders uniforms update, then in the events raising.

  • After reducing some gpu calls, >> +20 FPS
  • Converting many For loops to list comprehensions and map() >> +10FPS

I was worried because the achievements in frames where small, then I remembered the events reporting.

  • After removing all the onRenderEnd calls, where I was doing rotation of the dragon and window title update >> +1000 FPS

The engine ended up running at 1500 FPS (max). Amazing, but unrealistic, since casting events is completely necessary. Maybe a threaded approach will give improvement in this point.

But since frame times is the right way to measure, I converted all numbers, getting:

  • First gain: 0.1ms
  • Second: 0.04ms
  • Last: 1.5ms

Was it right? After trying the same program in my slow laptop, the timings probed to be correct… and meaningless. All of them:
The result in my laptop was of 58FPS, when before the improvements was of 50FPS. Only 2.7ms Total, after all the stuff I did and removed.

Final result: I’m done with optimizations. Maybe, optimizing before having something good is not good. I will move on to the next step, that will not be an editor this time.

PyOpenGL performance 1

The first concern about rewriting the engine in Python + OpenGL was about having to learn one more language that in the beginning I didn’t wanted to learn. The speed of OpenGL was never any worry, mainly because I trusted in the findings of Valve.

The second concern, was Python speed. Being an interpreted language, it seemed that it was going to be inherently slower that C++ or even worst, slower than C#. Working with Basic.net previously, I was accustomed to the idea of having slow code, but SharpDX is being said to be not so distant to raw C++ in speed. And it proved to be truth after my initial tests, so… What to expect from PyOpenGL?

The first test I made was to load the Stanford dragon (after converting my meshes loader to Python, with Pyassimp) and the result was not satisfactory (or so I thought): The FPS is around 40 (up to 60 without recording), wich seemed to be slow, even in my slow laptop. Still, I could not say anything yet, until making a test in ‘the big one’ and having a comparison with SharpDX. After some days, here it is:

Amazing!

PyOpenGL 2/3.1 is faster than SharpDX (DX9), just like the C++ versions. Even better, I have not made any optimization to the Python version. I’m not using PyOpenGL-Accelerate nor Cython!, so it is possible to get even better speed with those tools 😀 .

The only real difference between the two tests is the shader used, but I doubt that the impact from it is that big.

For reference, the next data:

Graphics card: Nvidia GTX 560 se

Processor: Intel Core 2 Duo 2.6GB

OS: Windows 7 32 bits

Memory: 3GB

Direct X 9 without fixed function Pipeline and shader model 2

OpenGL 2 without fixed function Pipeline and GLSL 1.2

Max FPS without recording:

– DirectX: 570 (first)

– OpenGL: 649 (second)

dx9 ogl2

Something interesting is the fact that the OpenGL app will reduce its speed if the DirectX one is running, as seen on the “Both” part of the video. Also, the OGL will slow down with any movement of any window or any interaction with the desktop. The DX window will remain steady. Does that mean that Windows gives preference to DX tasks over OGL or was it my card?

In the PyOpenGL site, they accept the wrapper being slow, but for some deprecated features, that I’m not using anyway, so I’m safe from that. And with this results and the Cython option, I’m really happy, so the conversion continues.