试图在OpenGL中绘制文本,但纹理稍微偏移

时间:2011-08-30 01:52:55

标签: opengl text

我正在尝试在OpenGL中绘制文本。我用一个小的C#程序将0x21到0x7E中的所有字符绘制成一个位图。我将其作为纹理加载并尝试使用它来绘制我的文本。它似乎有效,但我似乎遇到了一些奇怪的问题。 http://i54.tinypic.com/mmd7k8.png注意'a'旁边的额外像素以及'c'稍微被切断的事实。似乎整个事情都略有转变。有谁知道我为什么会遇到这个问题?我不想使用像gltext这样的库,因为我不想依赖libfreetype,因为我只绘制了几个字符串。这是我正在使用的代码:

static void textDrawChar(char c, int x, int y)
{
GLfloat vert[8];
GLfloat texcoord[8];

vert[0] = x;                            vert[1] = y;
vert[2] = x;                            vert[3] = y + TEXT_HEIGHT;
vert[4] = x + font_char_width[c-0x21];  vert[5] = y + TEXT_HEIGHT;
vert[6] = x + font_char_width[c-0x21];  vert[7] = y;

texcoord[0] = (float)(font_char_pos[c-0x21])/TOTAL_WIDTH;
texcoord[1] = 1.0f;
texcoord[2] = (float)(font_char_pos[c-0x21])/TOTAL_WIDTH;
texcoord[3] = 0.0f;
texcoord[4] = (float)(font_char_pos[c-0x21] + font_char_width[c-0x21])/TOTAL_WIDTH;
texcoord[5] = 0.0f;
texcoord[6] = (float)(font_char_pos[c-0x21] + font_char_width[c-0x21])/TOTAL_WIDTH;
texcoord[7] = 1.0f;

glBindTexture(GL_TEXTURE_2D, texture);
glVertexPointer(2, GL_FLOAT, 0, vert);
glTexCoordPointer(2, GL_FLOAT, 0, texcoord);
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, indices);
}

void textDraw(void)
{
textDrawChar('a', 300, 300);
textDrawChar('b', 300+font_char_width['a'-0x21], 300);
textDrawChar('c', 300+font_char_width['a'-0x21]+font_char_width['b'-0x21], 300);
}

编辑我在纹理坐标(0,2,4,6)上添加了4,这似乎解决了问题。但是,现在我需要知道这是否会继续工作或可能因未知原因而中断。这是位图创建代码,以防问题出现在那里。

using System;
using System.Drawing;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Collections;

namespace font_to_bmp
{
class Program
{
    static void Main(string[] args)
    {
        Bitmap b = new Bitmap(1715, 36);
        Graphics g = Graphics.FromImage(b);
        g.FillRectangle(new SolidBrush(Color.Transparent), 0, 0, 1715, 36);
        Font f = new Font("Liberation Sans", 24, GraphicsUnit.Point);
        g.PageUnit = GraphicsUnit.Pixel;
        int curx = 0;
        StreamWriter w = new StreamWriter(new FileStream("font_width.h", FileMode.Create, FileAccess.Write, FileShare.Read));
        w.WriteLine("static const int font_char_width[] = {");
        ArrayList hax = new ArrayList();
        for (int i = 0x21; i <= 0x7E; i++)
        {
            char c = (char)i;
            string s = c.ToString();
            SizeF sz = g.MeasureString(s, f);
            StringFormat sf = new StringFormat();
            sf.SetMeasurableCharacterRanges(new CharacterRange[] {new CharacterRange(0,1)});
            Region r = g.MeasureCharacterRanges(s, f, new RectangleF(0, 0, 1000, 1000), sf)[0];
            RectangleF r2 = r.GetBounds(g);
            Console.WriteLine("{0} is {1}x{2}", s, r2.Width, r2.Height);
            int w_int = (int)(Math.Ceiling(r2.Width));
            g.DrawString(s, f, new SolidBrush(Color.Black), curx, 0);
            hax.Add(curx);
            curx += w_int;
            w.WriteLine("\t{0},", w_int);
        }
        Console.WriteLine("Total width {0}", curx);
        w.WriteLine("};");
        w.WriteLine();

        w.WriteLine("static const int font_char_pos[] = {");

        foreach(int z in hax)
        {
            w.WriteLine("\t{0},", z);
        }
        w.WriteLine("};");

        w.Close();
        b.Save("font.png");
    }
}
}

1 个答案:

答案 0 :(得分:3)

精确地在纹理中寻址像素有点棘手。请参阅OpenGL Texture Coordinates in Pixel Space

中给出的答案

这已被问过几次,但我手边没有链接,所以快速而粗略的解释。假设纹理宽度为8像素:

 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
 ^   ^   ^   ^   ^   ^   ^   ^   ^
0.0  |   |   |   |   |   |   |  1.0
 |   |   |   |   |   |   |   |   |
0/8 1/8 2/8 3/8 4/8 5/8 6/8 7/8 8/8

数字表示纹理的像素,条纹表示纹理的边缘,并且在最近的情况下过滤像素之间的边界。但是你想要点击像素的中心。所以你对纹理坐标感兴趣

(0/8 + 1/8)/ 2 = 1 /(2 * 8)

(1/8 + 2/8)/ 2 = 3 /(2 * 8)

...

(7/8 + 8/8)/ 2 = 15 /(2 * 8)

或者更一般地,对于N宽纹理中的像素i,适当的纹理坐标是

(2i - 1)/(2N)

但是,如果您想要将纹理与屏幕像素完美对齐,请记住您指定为坐标的不是四边形的像素,而是边缘,这取决于投影可能与屏幕像素边缘对齐,而不是中心,因此可能需要其他纹理坐标。