- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
在这里学习 OpenGL。尝试编写一个位图字体渲染系统。
我正在使用Hiero生成字体文件(Angel Code 字体格式)和图集(.fnt
和 .png
文件)。
我首先解析字体文件中读取的字体和字符数据。那部分很容易。 (经验证,解析结果确实全部正确)
// (typedefs) u32: unsigned int, r32: float, string: char*
struct font_character
{
u32 Id;
r32 X, Y;
r32 Width, Height;
r32 XOffset, YOffset;
r32 XAdvance;
};
struct font_set
{
string Name;
u32 Atlas;
r32 Size;
u32 Stretch;
u32 Smooth;
u32 AntiAliasing;
u32 Padding[4];
u32 Spacing[2];
u32 LineHeight;
u32 BaseLine;
r32 Width, Height;
u32 CharacterCount;
font_character Characters[128];
};
// Parsing related codes...
font_set FontLoad(string FilePath)
{
font_set Result = {};
string Content = ReadFile(FilePath);
if (Content)
{
List(string) FontSettings;
ListAlloc(FontSettings, 1024);
bool DoneParsing = false;
while(!DoneParsing)
{
token Token = GetToken(Content);
switch(Token.Type)
{
case TokenType_EOF:
DoneParsing = true;
break;
case TokenType_Unknown:
Assert(!"Unknown token in font file");
break;
case TokenType_Number:
case TokenType_String:
case TokenType_Identifier:
ListPush(FontSettings, Token.Content);
break;
}
}
for (int i = 0, Count = ListCount(FontSettings); i < Count; i += 2)
{
string SettingKey = FontSettings[i];
string SettingValue = FontSettings[i + 1];
if (StringEqual(SettingKey, "face"))
Result.Name = SettingValue;
else if (StringEqual(SettingKey, "size"))
Result.Size = atoi(SettingValue);
else if (StringEqual(SettingKey, "stretchH"))
Result.Stretch = atoi(SettingValue);
else if (StringEqual(SettingKey, "smooth"))
Result.Smooth = atoi(SettingValue);
else if (StringEqual(SettingKey, "aa"))
Result.AntiAliasing = atoi(SettingValue);
else if (StringEqual(SettingKey, "lineHeight"))
Result.LineHeight = atoi(SettingValue);
else if (StringEqual(SettingKey, "base"))
Result.BaseLine = atoi(SettingValue);
else if (StringEqual(SettingKey, "scaleW"))
Result.Width = atoi(SettingValue);
else if (StringEqual(SettingKey, "scaleH"))
Result.Height = atoi(SettingValue);
else if (StringEqual(SettingKey, "spacing"))
{
// Ascii(48) = Decimal(0)
Result.Spacing[0] = SettingValue[0] - 48;
Result.Spacing[1] = SettingValue[2] - 48;
}
else if (StringEqual(SettingKey, "padding"))
{
Result.Padding[0] = SettingValue[0] - 48;
Result.Padding[1] = SettingValue[2] - 48;
Result.Padding[2] = SettingValue[4] - 48;
Result.Padding[3] = SettingValue[6] - 48;
}
else if (StringEqual(SettingKey, "char"))
{
font_character Character;
// Although they're 10 pairs of data, we're gonna skip the last two cause we don't care about them
For(u32, PairIndex, 8)
{
string CharKey = FontSettings[(i + 1) + PairIndex * 2];
string CharValue = FontSettings[(i + 2) + PairIndex * 2];
if (StringEqual(CharKey, "id"))
Character.Id = atoi(CharValue);
else if (StringEqual(CharKey, "x"))
Character.X = atoi(CharValue);
else if (StringEqual(CharKey, "y"))
Character.Y = atoi(CharValue);
else if (StringEqual(CharKey, "width"))
Character.Width = atoi(CharValue);
else if (StringEqual(CharKey, "height"))
Character.Height = atoi(CharValue);
else if (StringEqual(CharKey, "xoffset"))
Character.XOffset = atoi(CharValue);
else if (StringEqual(CharKey, "yoffset"))
Character.YOffset = atoi(CharValue);
else if (StringEqual(CharKey, "xadvance"))
Character.XAdvance = atoi(CharValue);
}
Result.Characters[Result.CharacterCount++] = Character;
i += 19;
}
else i--;
}
}
// Load texture
char TexturePath[256];
sprintf(TexturePath, "%s.png", FilePath);
Result.Atlas = TextureLoad(TexturePath); // loads texture from file via stbi_load, does glGenTexture, configures texture parameters etc.
return (Result);
}
然后我们开始渲染,这就是我有点挣扎的地方。我的理解是,我需要使用从字体数据中获得的字符数据来构建可以渲染到屏幕上的四边形。
首先,这是我的着色器。顶点着色器:
#version 330 core
layout (location = 0) in vec2 VertPos;
layout (location = 1) in vec2 VertUV;
out vec2 FragUV;
uniform mat4 Projection;
void main()
{
gl_Position = Projection * vec4(VertPos, 0, 1);
FragUV = VertUV;
}
和片段着色器:
#version 330 core
out vec4 FinalColor;
in vec2 FragUV;
uniform sampler2D FontAtlas;
uniform vec3 Color;
void main()
{
FinalColor = vec4(Color, texture(FontAtlas, FragUV).a);
}
这是我加载字体和渲染的方法:
struct font_renderer
{
string Text;
v3 Color;
r32 CurrentX;
u32 VAO, VBO;
u32 Initialized;
u32 Shader;
};
// In an initialization function
font_set Font = FontLoad("res/fonts/Courier New.fnt");
u32 FontShader = ShaderLoadFromFile("Font.vert", "Font.frag");
// In an update/render function
font_renderer FontRenderer = {};
FontRenderer.Text = "A";
FontRenderer.Color = V3(1, 0, 0);
FontRenderer.CurrentX = 20;
FontRenderer.Shader = FontShader;
FontRender(&FontRenderer, &Font);
渲染功能(只是想在屏幕上显示一些东西)
void FontRender(font_renderer *Renderer, font_set *Font)
{
u32 NumChars = StringLength(Renderer->Text);
u32 Size = NumChars * 12;
if (!Renderer->Initialized)
{
glGenBuffers(1, &Renderer->VBO);
glBindBuffer(GL_ARRAY_BUFFER, Renderer->VBO);
glBufferData(GL_ARRAY_BUFFER, Size * 2, 0, GL_STATIC_DRAW);
glGenVertexArrays(1, &Renderer->VAO);
glBindVertexArray(Renderer->VAO);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, 0, 0, 0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, 0, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
Renderer->Initialized = 1;
}
/*r32 *VertPos = Calloc(Size, r32);
r32 *VertUV = Calloc(Size, r32);*/
// Temporary code, just trying to render a single character on screen
r32 VertPos[12]; // we need a quad <=> 2 triangles <=> 6 vertices <=> 12 floats
r32 VertUV[12]; // same for UVs
For(u32, i, NumChars) // for loop macro
{
font_character Character = Font->Characters[Renderer->Text[i]]; // assuming that 'Characters' are ordered correctly
r32 X = Character.X;
r32 Y = Character.Y;
r32 XOffset = Character.XOffset;
r32 YOffset = Character.YOffset;
r32 XAdvance = Character.XAdvance;
r32 Width = Character.Width;
r32 Height = Character.Height;
// Triangle 1 (clock-wise winding order)
{
// Top Left
VertPos[i] = Renderer->CurrentX + XOffset;
VertPos[i + 1] = YOffset;
// Bottom Left
VertPos[i + 2] = Renderer->CurrentX + XOffset;
VertPos[i + 3] = YOffset + Height;
// Bottom Right
VertPos[i + 4] = Renderer->CurrentX + XOffset + Width;
VertPos[i + 5] = YOffset + Height;
}
// Triangle 2
{
// Bottom Right
VertPos[i + 6] = VertPos[i + 4];
VertPos[i + 7] = VertPos[i + 5];
// Top Right
VertPos[i + 8] = Renderer->CurrentX + XOffset + Width;
VertPos[i + 9] = YOffset;
// Top Left
VertPos[i + 10] = VertPos[i];
VertPos[i + 11] = VertPos[i + 1];
}
// UV 1
{
// Top left
VertUV[i] = X / Font->Width;
VertUV[i + 1] = Y / Font->Height;
// Bottom left
VertUV[i + 2] = X / Font->Width;
VertUV[i + 3] = (Y + Height) / Font->Height;
// Bottom right
VertUV[i + 4] = (X + Width) / Font->Width;
VertUV[i + 5] = (Y + Height) / Font->Height;
}
// UV 2
{
// Bottom right
VertUV[i + 6] = VertUV[i + 4];
VertUV[i + 7] = VertUV[i + 5];
// Top right
VertUV[i + 8] = (X + Width) / Font->Width;
VertUV[i + 9] = Y / Font->Height;
// Top left
VertUV[i + 10] = VertUV[i];
VertUV[i + 11] = VertUV[i + 1];
}
}
glBindBuffer(GL_ARRAY_BUFFER, Renderer->VBO);
u32 Offset = 0;
glBufferSubData(GL_ARRAY_BUFFER, Offset, Size, VertPos);
Offset += Size;
glBufferSubData(GL_ARRAY_BUFFER, Offset, Size, VertUV);
m4 FontProjection = Orthographic(0, 800, 600, 0, -1, +1);
glDisable(GL_DEPTH_TEST);
ShaderUse(Renderer->Shader);
glBindVertexArray(Renderer->VAO);
TextureBind(Font->Atlas);
ShaderSetV3(Renderer->Shader, "Color", Renderer->Color);
ShaderSetM4(Renderer->Shader, "Projection", &FontProjection);
glDrawArrays(GL_TRIANGLES, 0, NumChars * 6);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
正如你所看到的,我实际上只是想在屏幕上显示一个角色。我什至没有考虑字体大小等,只是保持简单。我选择从 TopLeft->BottomLeft->BottomRight->TopRight->TopLeft
开始的顶点,如果我错了,请纠正我,但它是顺时针缠绕顺序。我的顶点着色器中有两个插槽:位置和 UV。我通过调用 glBufferSubData
运行程序我没有得到任何渲染输出。只是空白屏幕。很确定我错过了一些明显的事情或做了一些愚蠢的事情,我看不到它。我做错了什么?
请注意,纹理、字体数据和着色器均已正确加载。
感谢任何帮助。
最佳答案
您的代码似乎存在许多问题(甚至忽略您采用的可怕的 C-in-C++ 编码风格)。我还没有找到所有这些,所以我不能说我找到了你遇到的那个。
但我确实找到了这些:
<小时/>u32 Size = NumChars * 12;
您使用Size * 2
作为缓冲区的大小。所以字符串中的每个字符占用 24 个字节; 12 代表你的位置,12 代表你的纹理坐标。
但是你的位置和纹理坐标是float
。每个 float 4 个字节 * 每个位置 2 个 float * 每个字符 6 个位置 = 每个字符 48 个字节。
所以你的尺寸计算非常错误。您可以通过检查 sizeof(VertPos)
并将其与您的 Size
变量进行比较来验证这一点。你会发现它们非常不同。
glVertexAttribPointer(0, 2, GL_FLOAT, 0, 0, 0);
glVertexAttribPointer(1, 2, GL_FLOAT, 0, 0, 0);
这表示您的位置和纹理坐标都具有相同的偏移量。这意味着着色器中的位置和纹理坐标将获得相同的值。我很确定这不是您想要的。
您的纹理坐标数组来自缓冲区的不同部分。所以你需要应用适当的偏移量。假设您已经正确计算了 Size
,则如下所示:
glVertexAttribPointer(1, 2, GL_FLOAT, 0, 0, reinterpret_cast<void*>(Size));
关于c - 位图字体渲染问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34013584/
我想填充 3D 等高线图 (contour3(X,Y,Z)),就像 2D 等高线填充图 (contourf(X,Y,Z))。但我无法弄清楚如何实现这一目标。 contour3 和 surf 的组合不是
我有一个 c3.js 折线图,表示 2 个值的演变。我需要折线图的工具提示是饼图(工具提示 = 另一个 c3.js 图形)。 这是我成功的: http://jsfiddle.net/owhxgaqm/
我有具有结构的 Pandas 数据框: A B 0 1 1 1 2 1 2 3 4 3 3 7 4 6 8 如何生成 Seaborn Violin 图,每列作为其自己的单独
我正在使用 D3DXSPRITE 方法将我的 map 图 block 绘制到屏幕上,我刚刚添加了一个缩放功能,当您按住向上箭头时会放大,但注意到您现在可以看到图 block 之间的间隙,这是一些屏幕截
今天我们开始学习目前学习到的最难最复杂的数据结构图。 简单回顾一下之前学习的数据结构,数组、单链表、队列等线性表中数据元素是一对一关系,而树结构中数据元素是一对多关系,而图结构中数据元素则是多对
1、系统环境如下图: 2、为该系统添加一块新的虚拟硬盘,添加后需重启虚拟机,否则系统不识别;如下图,/dev/sdc 是新添加的硬盘; 3、fdisk /dev/sdc为新硬盘创建分区:
1、nagios简介 nagios是一款开源的电脑系统和网络监视工具,能有效监控windows、linux和unix的主机状态,交换机路由器等网络设置,打印机等。在系统或服务状态异常时发
越来越多人开始习惯用手机上网,浏览网页、查看邮件···移动化已经成为互联网发展必然趋势,包括facebook在内的很多互联网公司都将移动广告作为下一个淘金地
1.图片处理 1.圆角图片 复制代码 代码如下: /** * 转换成圆角 * &n
Microsoft SQL Server Management Studio是SQL SERVER的客户端工具,相信大家都知道。我不知道大伙使用导入数据的情况怎么样,反正我最近是遇到过。主要是因为没
debian6系统: 首先先安装mysql吧: 打开终端(root)用户登入 apt-get purge mysql-server-5.5 安装完成后: 默认情况下Mysql只允许本地登录
fedora16英文环境下支持中文输入法的方法 fedora16英文环境下支持FCITX的中文输入法: $ im-chooser 就会出现选择界面,选择第二个就行了。
Net预编译命令 C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_compiler.exe -? 显示说明 我们需要选择的命令为&n
有的时候电脑出现一些故障有的时候通过将其修改bios设置的方法来解决故障,那么在bios上设置能不能将电脑恢复出厂设置呢?其实也是可以的。方法也很简单的,只要会进入电脑的bios懂的上面英文的意思就
笔者曾介绍过Deepin 将对龙芯进行全面支持,打造最优美龙芯电脑桌面。现在Deepin团队移植工作取得了突破性的成果,Deepin桌面已经在龙芯3A和龙芯3B电脑上成功运行起来了。 以下为龙芯3
在安装一些软件之后,我们的电脑总是会发生一点小变化,不是桌面上多了几个网址图标,就是IE浏览器的默认主页被篡改成乱七八糟的网址。最可气的是,在IE设置中将默认主页改回来后,下次启动Win7后又变了回
“注册表编辑器怎么打开”虽说不是很难的问题,但是对于对电脑常识不是很擅长的网民来说,当电脑出现问题或需要更改设置时,着实还是件头疼的问题。因为需要打开注册表进行操作解决。那么如何打开注册表编辑器呢?
这篇文章重点介绍10个重要的WordPress安全插件和技巧,用来保护WordPress网站或者博客。 1. WP Security 人工帮助你修复被黑客入侵的网站,只要按照他们网站上的联系电话
其实运用object和javascript调用外部文件,也能实现不同栏目调用不同友情链接,即相当于调用不同栏目友情链接文件, {dede:field.typeid/}来获取当前栏目的ID。
我有一个复值矩阵。 如果我发出命令: plot(myMatrix) 然后它在图形设备上显示一种散点图,X 轴标记为 Re(myMatrix),Y 轴标记为 Im(myMatrix)。这显示了我正在寻找
我是一名优秀的程序员,十分优秀!