如何将 stb_truetype 位图加载到 OpenGL 纹理中
How to load an stb_truetype bitmap into and OpenGL texture
我有以下代码从 std::wstring 创建位图,然后使用它保存到图像中。
#include <stb_image_write.h>
#include <stb_truetype.h>
unsigned char buffer[24 << 20];
stbtt_fontinfo font;
fread(buffer, 1, 1000000, fopen("../assets/fonts/unibody_8.ttf", "rb"));
stbtt_InitFont(&font, buffer, 0);
float scale = stbtt_ScaleForPixelHeight(&font, 48 * 2);
int ascent, descent;
stbtt_GetFontVMetrics(&font, &ascent, &descent, 0);
int baseline = (int)(ascent * scale);
int width = 0;
for (const auto &character : content) {
int advance, leftSideBearing;
stbtt_GetCodepointHMetrics(&font, character, &advance, &leftSideBearing);
texture.width += advance * scale;
}
int height = (ascent + (descent * -1)) * scale;
std::vector<unsigned char> pixels(
(size_t)(texture.width * texture.height), (unsigned char)0);
float xpos = 0.0f;
int characterIndex = 0;
while (content[characterIndex]) {
int advance, lsb, x0, y0, x1, y1;
float x_shift = xpos - (float)floor(xpos);
stbtt_GetCodepointHMetrics(
&font, content[characterIndex], &advance, &lsb);
stbtt_GetCodepointBitmapBoxSubpixel(
&font,
content[characterIndex],
scale,
scale,
x_shift,
0,
&x0,
&y0,
&x1,
&y1);
auto stride = width * (baseline + y0) + (int)xpos + x0;
stbtt_MakeCodepointBitmapSubpixel(
&font,
&pixels.at(0) + stride,
x1 - x0,
y1 - y0,
texture.width,
scale,
scale,
x_shift,
0,
content[characterIndex]);
xpos += (advance * scale);
if (content[characterIndex + 1]) {
int kernAdvance = stbtt_GetCodepointKernAdvance(
&font, content[characterIndex], content[characterIndex + 1]);
xpos += scale * kernAdvance;
}
++characterIndex;
}
// This step works fine, this means that the data in pixels is good
stbi_write_png("image.png", width, height, 1, pixels.data(), 0);
我现在想做的是将位图加载到 OpenGL 纹理中,但这一步似乎使应用程序崩溃了。
uint32_t id = 0;
glGenTextures(1, &id);
glBindTexture(GL_TEXTURE_2D, id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// This crashes the application, I guess it's because the data in pixels is not valid for an OpenGL texture
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RGBA,
width,
height,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
pixels.data());
我试图捕获异常,但程序只是悄无声息地崩溃了,所以我不确定如何处理。
这个
std::vector<unsigned char> pixels(
(size_t)(texture.width * texture.height), (unsigned char)0);
将像素数据设置为单色位图宽度 * 高度,每像素 8 位。
但是这里:
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RGBA,
width,
height,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
pixels.data());
你告诉 GL 每个像素有 32 位,红色、绿色、蓝色和 alpha 各有 8 位。结果,GL 读取的内存将是您提供的内存的 4 倍,读取超过缓冲区的末尾,如果幸运的话,在之后的某个时间点由于某些分段错误而使应用程序崩溃。
您没有指定您使用的 GL 版本。对于现代 GL,您将使用 GL_RED
作为纹理格式,并直接通过着色器或通过纹理混合设置处理到 RGBA 的转换。对于古老的遗留 GL,您可能必须使用 GL_LUMINANCE
格式。
您还必须知道 GL 的默认解包行对齐方式是 4 字节,因此如果您的宽度不是 4 的倍数,您还必须明确设置 glPixelStore(GL_UNPACK_ALIGNMENT,1)
以匹配您的数据对齐方式.
我有以下代码从 std::wstring 创建位图,然后使用它保存到图像中。
#include <stb_image_write.h>
#include <stb_truetype.h>
unsigned char buffer[24 << 20];
stbtt_fontinfo font;
fread(buffer, 1, 1000000, fopen("../assets/fonts/unibody_8.ttf", "rb"));
stbtt_InitFont(&font, buffer, 0);
float scale = stbtt_ScaleForPixelHeight(&font, 48 * 2);
int ascent, descent;
stbtt_GetFontVMetrics(&font, &ascent, &descent, 0);
int baseline = (int)(ascent * scale);
int width = 0;
for (const auto &character : content) {
int advance, leftSideBearing;
stbtt_GetCodepointHMetrics(&font, character, &advance, &leftSideBearing);
texture.width += advance * scale;
}
int height = (ascent + (descent * -1)) * scale;
std::vector<unsigned char> pixels(
(size_t)(texture.width * texture.height), (unsigned char)0);
float xpos = 0.0f;
int characterIndex = 0;
while (content[characterIndex]) {
int advance, lsb, x0, y0, x1, y1;
float x_shift = xpos - (float)floor(xpos);
stbtt_GetCodepointHMetrics(
&font, content[characterIndex], &advance, &lsb);
stbtt_GetCodepointBitmapBoxSubpixel(
&font,
content[characterIndex],
scale,
scale,
x_shift,
0,
&x0,
&y0,
&x1,
&y1);
auto stride = width * (baseline + y0) + (int)xpos + x0;
stbtt_MakeCodepointBitmapSubpixel(
&font,
&pixels.at(0) + stride,
x1 - x0,
y1 - y0,
texture.width,
scale,
scale,
x_shift,
0,
content[characterIndex]);
xpos += (advance * scale);
if (content[characterIndex + 1]) {
int kernAdvance = stbtt_GetCodepointKernAdvance(
&font, content[characterIndex], content[characterIndex + 1]);
xpos += scale * kernAdvance;
}
++characterIndex;
}
// This step works fine, this means that the data in pixels is good
stbi_write_png("image.png", width, height, 1, pixels.data(), 0);
我现在想做的是将位图加载到 OpenGL 纹理中,但这一步似乎使应用程序崩溃了。
uint32_t id = 0;
glGenTextures(1, &id);
glBindTexture(GL_TEXTURE_2D, id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// This crashes the application, I guess it's because the data in pixels is not valid for an OpenGL texture
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RGBA,
width,
height,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
pixels.data());
我试图捕获异常,但程序只是悄无声息地崩溃了,所以我不确定如何处理。
这个
std::vector<unsigned char> pixels( (size_t)(texture.width * texture.height), (unsigned char)0);
将像素数据设置为单色位图宽度 * 高度,每像素 8 位。
但是这里:
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
你告诉 GL 每个像素有 32 位,红色、绿色、蓝色和 alpha 各有 8 位。结果,GL 读取的内存将是您提供的内存的 4 倍,读取超过缓冲区的末尾,如果幸运的话,在之后的某个时间点由于某些分段错误而使应用程序崩溃。
您没有指定您使用的 GL 版本。对于现代 GL,您将使用 GL_RED
作为纹理格式,并直接通过着色器或通过纹理混合设置处理到 RGBA 的转换。对于古老的遗留 GL,您可能必须使用 GL_LUMINANCE
格式。
您还必须知道 GL 的默认解包行对齐方式是 4 字节,因此如果您的宽度不是 4 的倍数,您还必须明确设置 glPixelStore(GL_UNPACK_ALIGNMENT,1)
以匹配您的数据对齐方式.