programming4us
programming4us
MULTIMEDIA

iPhone 3D Programming : Drawing an FPS Counter (part 2) - Rendering the FPS Text

- How To Install Windows Server 2012 On VirtualBox
- How To Bypass Torrent Connection Blocking By Your ISP
- How To Install Actual Facebook App On Kindle Fire
8/25/2011 3:17:24 PM

2. Rendering the FPS Text

Now that we’re past the grunt work of generating the glyphs texture, we can move on to the actual rendering code. A frames-per-second counter is much more useful than our other toy demos, so this time let’s strive to make the rendering code very self-contained and easy to integrate into any project. We can do this by creating a C++ class wholly implemented within a single header file. Example 3 shows the basic outline for this class.

Example 3. FpsRenderer.h skeleton
#include <OpenGLES/ES1/gl.h>
#include <OpenGLES/ES1/glext.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#include "../Textures/NumeralsTexture.h"

typedef unsigned int PVRTuint32;

struct PVR_Texture_Header {
// ...see PVRTTexture.h in the PowerVR SDK...
};

class FpsRenderer {
public:
FpsRenderer(vec2 windowSize)
{
...
}
void RenderFps()
{
...
}

private:

static const int MaxNumDigits = 3;
static const int VertsPerDigit = 6;
static const int FloatsPerVert = 4;
static const int FloatsPerDigit = VertsPerDigit * FloatsPerVert;
static const int TexCoordOffset = sizeof(float) * 2;
static const int BytesPerVert = sizeof(float) * FloatsPerVert;
static const int BytesPerDigit = sizeof(float) * FloatsPerDigit;

uint64_t GetElapsedNanoseconds()
{
uint64_t current = mach_absolute_time();
uint64_t duration = current - m_previousTime;
m_previousTime = current;
mach_timebase_info_data_t info;
mach_timebase_info(&info);
duration *= info.numer;
duration /= info.denom;
return duration;
}

float* WriteGlyphVertex(const Glyph& glyph, vec2 pos, int corner, float* vertex)
{
...
}

double m_filterConstant;
double m_fps;
uint64_t m_previousTime;
vec2 m_windowSize;
vec2 m_textureSize;
GLuint m_textureHandle;
GLuint m_vbo;
};



2.1. Stabilizing the counter with a low-pass filter

To prevent the FPS counter from fluctuating wildly, we’ll using a low-pass filter similar to the one we used for the accelerometer . The application can compute a constant called the smoothing factor, which is always between zero and one. Here’s one way of doing so:

double ComputeSmoothingFactor(double sampleRate, double cutoffFrequency)
{
double dt = 1.0 / sampleRate;
double RC = 1.0 / cutoffFrequency;
return dt / (dt + RC);
}

In the previous listing, cutoffFrequency and sampleRate help define what constitutes “noise” in the signal. However, for our purposes, computing a smoothing factor like this is a bit pedantic; pragmatically speaking, it’s perfectly fine to come up with a reasonable number through experimentation. I find that a value of 0.1 works well for a frame rate counter. A higher smoothing factor would result in a more spastic counter.

2.2. Fleshing out the FpsRenderer class

Let’s go ahead and implement the constructor of the FpsRenderer class; see Example 4. It’s responsible for loading up the glyphs texture and creating the empty VBO for rendering up to three digits.

Example 4. FpsRenderer constructor
FpsRenderer(vec2 windowSize)
{
m_filterConstant = 0.1;
m_fps = 0;
m_windowSize = windowSize;
m_previousTime = mach_absolute_time();

glGenTextures(1, &m_textureHandle);
glBindTexture(GL_TEXTURE_2D, m_textureHandle);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
PVR_Texture_Header* header = (PVR_Texture_Header*) NumeralsTexture;
const unsigned char* bytes = (unsigned char*) NumeralsTexture;
const unsigned char* imageData = bytes + header->dwHeaderSize;
GLenum type = GL_UNSIGNED_BYTE;
GLenum format = GL_ALPHA;
int w = header->dwWidth;
int h = header->dwHeight;
m_textureSize = vec2(w, h);
glTexImage2D(GL_TEXTURE_2D, 0, format, w, h,
0, format, type, imageData);

glGenBuffers(1, &m_vbo);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
int totalSize = BytesPerDigit * MaxNumDigits;
glBufferData(GL_ARRAY_BUFFER, totalSize, 0, GL_DYNAMIC_DRAW);
}



The FpsRenderer class has only one public method; see Example 5. This method is responsible for updating the moving average and rendering the digits. Note that updating the VBO is quite a hassle; we’ll demonstrate a much simpler way of rendering textured rectangles in the next section.

Example 5. RenderFps() method
void RenderFps()
{
uint64_t deltaTime = GetElapsedNanoseconds();
double fps = 1000000000.0 / deltaTime;
double alpha = m_filterConstant;
m_fps = fps * alpha + m_fps * (1.0 - alpha);
fps = round(m_fps);

char digits[MaxNumDigits + 1] = {0};
sprintf(digits, "%d", (int) fps);
int numDigits = strlen(digits);
vec2 pos(5, 10);

vector<float> vbo(numDigits * FloatsPerDigit);
float* vertex = &vbo[0];
for (char* digit = &digits[0]; *digit; ++digit) {
int glyphIndex = *digit - '0';
const Glyph& glyph = NumeralGlyphs[glyphIndex];
vertex = WriteGlyphVertex(glyph, pos, 0, vertex);
vertex = WriteGlyphVertex(glyph, pos, 1, vertex);
vertex = WriteGlyphVertex(glyph, pos, 2, vertex);
vertex = WriteGlyphVertex(glyph, pos, 2, vertex);
vertex = WriteGlyphVertex(glyph, pos, 3, vertex);
vertex = WriteGlyphVertex(glyph, pos, 1, vertex);
pos.x += glyph.Metrics.XAdvance;
}

glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBufferSubData(GL_ARRAY_BUFFER, 0,
BytesPerDigit * numDigits, &vbo[0]);
glBindTexture(GL_TEXTURE_2D, m_textureHandle);
glVertexPointer(2, GL_FLOAT, BytesPerVert, 0);
glTexCoordPointer(2, GL_FLOAT, BytesPerVert,
(GLvoid*) TexCoordOffset);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrthof(0, m_windowSize.x, 0, m_windowSize.y, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glColor4f(1, 1, 1, 1);
glDisableClientState(GL_NORMAL_ARRAY);
glDrawArrays(GL_TRIANGLES, 0, numDigits * VertsPerDigit);
glEnableClientState(GL_NORMAL_ARRAY);
glDisable(GL_BLEND);
}



Next we need to implement the private WriteGlyphVertex method, which generates the VBO data for a given corner of a glyph rectangle. It takes a pointer-to-float for input, advances it after writing out each value, and then returns it to the caller (see Example 6).

Example 6. WriteGlyphVertex() method
float* WriteGlyphVertex(const Glyph& glyph, vec2 position, 
int corner, float* vertex)
{
vec2 texcoord;
texcoord.x = glyph.Position.X;
texcoord.y = glyph.Position.Y + glyph.Metrics.Height;

position.y -= glyph.Metrics.Height + glyph.Metrics.YBearing;

if (corner % 2) {
position.x += glyph.Metrics.Width;
texcoord.x += glyph.Metrics.Width;
}

if (corner / 2) {
position.y += glyph.Metrics.Height;
texcoord.y -= glyph.Metrics.Height;
}

*vertex++ = position.x;
*vertex++ = position.y;
*vertex++ = (1 + texcoord.x) / m_textureSize.x;
*vertex++ = 1 - (1 + texcoord.y) / m_textureSize.y;

return vertex;
}



That’s it for the frame rate counter! It’s pretty easy to use the class from within the rendering engine class; see Example 7.

Example 7. Using the FpsRenderer class
...
#include "FpsRenderer.h"

class RenderingEngine : public IRenderingEngine {
public:
RenderingEngine(IResourceManager* resourceManager);
void Initialize();
void Render(float objectTheta, float fboTheta) const;
private:
...
FpsRenderer* m_fpsRenderer;
};

void RenderingEngine::Initialize()
{
...
m_fpsRenderer = new FpsRenderer(m_screenSize);
}

void RenderingEngine::Render(float objectTheta, float fboTheta) const
{
...
m_fpsRenderer->RenderFps();
}

...
Other  
 
Top 10
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
- Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
- Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
REVIEW
- First look: Apple Watch

- 3 Tips for Maintaining Your Cell Phone Battery (part 1)

- 3 Tips for Maintaining Your Cell Phone Battery (part 2)
programming4us programming4us
programming4us
 
 
programming4us