基于纹理缓存来传递字体的轮廓信息
title: [GPU矢量纹理] 基于纹理缓存来传递字体的轮廓信息¶
在“GPU矢量纹理“算法中,我们会将 文字的轮廓信息 通过 Texture Buffer 传送到GPU当中,以便在GPU中渲染出文字。
本文将重点介绍在“GPU矢量纹理”案例中,对OpenGL Texture Buffer的使用。
着色器¶
先看在着色器中如何使用Texture Buffer。
#version 330 core
//字形描述
struct Glyph {
int start, count; //Curves中的索引
};
//二次贝塞尔曲线
struct Curve {
vec2 p0, p1, p2;
};
uniform isamplerBuffer glyphs; //字形缓存纹理(存储所有的字形描述)
uniform samplerBuffer curves; //曲线缓存纹理(存储所有字形的贝塞尔曲线)
// 根据索引,从glyphs中加载Glyph
Glyph loadGlyph(int index) {
Glyph result;
ivec2 data = texelFetch(glyphs, index).xy;
//texelFetch直接从缓冲区中取出像素值,不进行插值处理
result.start = data.x;
result.count = data.y;
return result;
}
// 根据索引,从curves中加载Curve
Curve loadCurve(int index) {
Curve result;
result.p0 = texelFetch(curves, 3*index+0).xy;
result.p1 = texelFetch(curves, 3*index+1).xy;
result.p2 = texelFetch(curves, 3*index+2).xy;
return result;
}
与使用2D纹理不同的是
Texture Buffer | Texture 2D | |
---|---|---|
申明 | uniform sampler2D texture1; |
uniform isamplerBuffer glyphs; |
取值 | in vec2 TexCoord; //[0, 1] texture(texture1, TexCoord); |
int index; //没有映射到[0,1] texelFetch(glyphs, index).xy; |
在CPU中如何准备数据¶
接下来看,如何在CPU中准备GPU所需数据。
创建与GPU所映射的数据结构¶
//字形(在Buffer中的位置)
struct BufferGlyph {
//start 开始位置
//count 曲线个数
int32_t start, count; // range of bezier curves belonging to this glyph
};
//曲线(即为贝塞尔曲线,由三个控制点所组成)
struct BufferCurve {
//三个顶点坐标
float x0, y0, x1, y1, x2, y2;
};
创建与Texture Buffer所映射的数据结构¶
std::vector<BufferGlyph> bufferGlyphs; //N个字形(对bufferCurves的索引)
std::vector<BufferCurve> bufferCurves; //N个字形的轮廓线都存储在这里
填充数据¶
根据需求,创建数据,并保存到bufferGlyphs
与bufferCurves
中即可。
- 组织
BufferGlyph
,并保存到bufferGlyphs
// 构建一个字符
void buildGlyph(uint32_t charcode, FT_UInt glyphIndex) {
//# 组织BufferGlyph
BufferGlyph bufferGlyph;
//...
for (int i = 0; i < face->glyph->outline.n_contours; i++) {
convertContour(); //组织BufferCurve
//...
}
//...
bufferGlyphs.push_back(bufferGlyph);
//...
}
- 组织
BufferCurve
,并保存到bufferCurves
void convertContour(...)
{
auto makeCurve = [](const glm::vec2& p0,
const glm::vec2& p1,
const glm::vec2& p2)
{
BufferCurve result;
result.x0 = p0.x;
result.y0 = p0.y;
result.x1 = p1.x;
result.y1 = p1.y;
result.x2 = p2.x;
result.y2 = p2.y;
return result;
};
//...
curves.push_back(makeCurve(b0, c0, d));
}
将数据从CPU传递到GPU¶
那么,如何将CPU中的数据如何传递到GPU中呢?
创建纹理与缓存¶
/*
*1. 本字体所有字形的轮廓线都存储在bufferCurves当中,用BufferGlyph来记录各个字形的索引
*2. 对于bufferGlyphs、bufferCurves,是当成Texture传入GPU的,因此还需要两个纹理插槽
*/
GLuint glyphTexture, curveTexture;
//glyphBuffer、curveBuffer实际上是使用的Texture的缓冲区
GLuint glyphBuffer, curveBuffer;
//在GPU中,bufferGlyphs、bufferCurves的缓冲区ID
//创建纹理
glGenTextures(1, &glyphTexture);
glGenTextures(1, &curveTexture);
//创建Buffer
glGenBuffers(1, &glyphBuffer);
glGenBuffers(1, &curveBuffer);
绑定纹理与缓存¶
//uniform isamplerBuffer glyphs;
glBindTexture(GL_TEXTURE_BUFFER, glyphTexture);
//明确glyphTexture的类型是Texture Buffer
glTexBuffer(GL_TEXTURE_BUFFER, GL_RG32I, glyphBuffer);
//将glyphBuffer绑定到glyphTexture上
glBindTexture(GL_TEXTURE_BUFFER, 0);
//绑定一个0句柄的纹理(即解绑glyphTexture的意思)
//uniform samplerBuffer curves;
glBindTexture(GL_TEXTURE_BUFFER, curveTexture);
glTexBuffer(GL_TEXTURE_BUFFER, GL_RG32F, curveBuffer);
glBindTexture(GL_TEXTURE_BUFFER, 0);
将CPU中的数据更新到GPU中¶
void uploadBuffers() {
glBindBuffer(GL_TEXTURE_BUFFER, glyphBuffer);
//绑定缓存
glBufferData(
GL_TEXTURE_BUFFER,
sizeof(BufferGlyph) * bufferGlyphs.size(),
bufferGlyphs.data(),
GL_STATIC_DRAW
);
//将CPU中的数据更新到GPU中
glBindBuffer(GL_TEXTURE_BUFFER, 0);
//绑定一个0句柄的缓存(即解绑glyphBuffer的意思)
glBindBuffer(GL_TEXTURE_BUFFER, curveBuffer);
glBufferData(GL_TEXTURE_BUFFER,
sizeof(BufferCurve) * bufferCurves.size(),
bufferCurves.data(),
GL_STATIC_DRAW
);
glBindBuffer(GL_TEXTURE_BUFFER, 0);
}
绘制时绑定纹理¶
GLint location;
location = glGetUniformLocation(program, "glyphs");
glUniform1i(location, 0); //纹理插槽 0
location = glGetUniformLocation(program, "curves");
glUniform1i(location, 1); //纹理插槽 1
//0纹理插槽,绑定glyphTexture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_BUFFER, glyphTexture);
//1纹理插槽,绑定curveTexture
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_BUFFER, curveTexture);
glActiveTexture(GL_TEXTURE0);