Freetype的学习过程总结
1. 编译环境的搭建
AndroidQ以前的版本编译freetype的库文件,需要设置ndk的编译环境,用make_standalone_toolchain.sh的脚本配置环境,但是现在google的官网上,已经抛弃了sh文本的使用,改用python的脚本,所以从官网上找到如下的配置环境。
1.1 64bits的编译环境配置如下:
python ~/androidQ/sdk_10.0/ndk-bundle/build/tools/make_standalone_toolchain.py --arch arm64 --api 28 --install-dir=my-toolchain –force
# Add the standalone toolchain to the search path.
export PATH=$PATH:`pwd`/my-toolchain/bin
# Tell configure what tools to use.
export target_host=aarch64-linux-android
export AR=$target_host-ar
export AS=$target_host-clang
export CC=$target_host-clang
export CXX=$target_host-clang++
export LD=$target_host-ld
export STRIP=$target_host-strip
# Tell configure what flags Android requires.
export CFLAGS="-fPIE -fPIC"
export LDFLAGS="-pie"
./configure --host= aarch64-linux-android --prefix=/freetype --without-zlib --with-png=no --with-harfbuzz=no
以上的配置需要做,因为编译出来的动态库,才会连接libc.so否则就会链接libc.so.6
Linux的应用链接的是libc.so.6
Android的应用链接的是libc.so
make -j10
make install DESTDIR=$(pwd)
1.2 32bits的编译环境配置如下:
区别64bits的编译配置的地方就是如下的操作,另外要设置:
export target_host= arm-linux-androideabi
./configure --host=arm-linux-androideabi --prefix=/freetype --without-zlib --with-png=no --with-harfbuzz=no
其他的没什么区别。
2. Android studio 的工程的编译以及遇到的问题
运行Android的工程遇到的问题总结:
-
如何解决Android studio错误“Unsupported Modules Detected: Compilation is not supported for following modules”
1) 结束项目 2) 关闭Android工作室 3) 删除.IDEA目录 4) 删除所有.iml文件 5) 打开android studio并导入该项目
-
注意:AndroidP之后的版本,不允许,操作sdcard的内容,所以font.ttf的文件可以放到Android的application的安装目录下:
File file = new File(context.getFilesDir() + "/font.ttf"); Log.d("Text", file.getPath() + "canread =" + file.exists());
-
由于Android的对三方的link的库文件有限制,需要把三方的库文件先添加到
手机目录下的/system/etc/public.libraries.txt 文件中,然后再把libfreetype.so的文件分别push到 /system/lib和/system/lib64 的目录下,否则开机会找不到这个库文件,系统无法开机。并且程序运行也找不到对应的文件。
-
Android studio的build.gradle编译脚本的配置:
defaultConfig { applicationId 'com.android.gles3jni' minSdkVersion "${platformVersion}" targetSdkVersion 28 ndk { abiFilters 'armeabi-v7a' //这边需要配置,否则默认的是windows_x86_64的编译器,然后跟arm平台的编译libfreetype的库不符合。 } } externalNativeBuild { cmake { path 'src/main/cpp/CMakeLists.txt'//编译native的库文件 } }
-
Makefile的编写需要注意下:
cmake_minimum_required(VERSION 3.4.1) //这个是版本号 set(OPENGL_LIB GLESv3) // GLESv3这个应该是Android环境对OpenGL支持的宏定义 // 设置OPENGL_LIB为GLESv3 include_directories( //头文件放在根目录下会减少编译错误也好识别 ./include/freetype2/freetype ./include/freetype2/ ./) add_library(gles3jni SHARED gles3jni.cpp RendererES2.cpp RendererES3.cpp) # Include libraries needed for gles3jni lib target_link_libraries(gles3jni ${OPENGL_LIB} android m EGL log -l${PROJECT_SOURCE_DIR}/libs/armeabi-v7a/libfreetype.so )// PROJECT_SOURCE_DIR是当前makefile文件的路径
3. Freetype使用OpenglES绘制过程遇到的问题
opengles 的glDrawArray 的调用参数说明
float vertices[] = {
// positions // texture coords
0.5f, 0.5f, 0.0f, 1.0f, 1.0f, // top right
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, // bottom left
-0.5f, 0.5f, 0.0f, 0.0f, 1.0f // top left
};
unsigned int indices[] = {
2, 1, 0, // first triangle
2, 0, 3 // second triangle
};
目前我的手机测试下来的结果是,opengl的绘制,以手机屏幕的中心点为原点(0,0)(现在只考虑2D的平面,没有projection的转换)调用glDrawArray的时候,以左下角的(-0.5f, -0.5f)为起点,按照逆时针的方向绘制顶点,用indices的设置需要是2,1,0的顺序,第二个三角形也是依次类推的做绘制的操作glUseProgram的函数调用需要设置在那个vertices data的配置之前,否则可能不生效。
4. freetype的字体查找绘制的过程
4.1 freetype的库文件,主要是以下的这三个,所以一版的功能只需要include以下的三个文件就行
#include <ft2build.h>
#include <ftglyph.h>
#include FT_FREETYPE_H
4.2 freetype的绘制流程如下:
4.2.1 opengl的绘制有时候输出的结果不是预期的时候,不好分析问题,一般先check三部分的内容,
static const char VERTEX_SHADER[] =
"#version 300 es\n"
"layout (location = 0) in vec2 vertex;\n"
"layout (location = 1) in vec2 aTexCoord;\n"
"out highp vec2 TexCoords;\n"
"void main() {\n"
" gl_Position = vec4(vertex, 0.0, 1.0);\n"
" TexCoords = aTexCoord;\n"
"}\n";
static const char FRAGMENT_SHADER[] =
"#version 300 es\n"
"in highp vec2 TexCoords;\n"
"out highp vec4 color;\n"
"uniform sampler2D text;\n"
"uniform highp vec3 textColor;\n"
"void main() {\n"
" highp vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);\n"
" color = vec4(textColor, 1.0) * sampled;\n"
//" color = texture(text, TexCoords);\n"
//" color = vec4(1.0, 0.0, 1.0, 1.0);\n" //debug使用
"}\n";
a. texture的问题,为了验证program和vertex的data没有问题,我先把以上的fragmeng的shader写上固定的颜色
color = vec4(1.0, 0.0, 1.0, 1.0),check是否正确的生成了texture的内容。
b. vertex 的问题,设置vertex的位置如下:
float vertices11[] = {
// positions // texture coords
0.5f, 0.5f, 0.0f, 1.0f, 1.0f, // top right
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, // bottom left
-0.5f, 0.5f, 0.0f, 0.0f, 1.0f // top left
};
看下能否绘制出来最基本的两个三角形,也就是长方形的框。
c. 需要check每一步gl的调用输出是否有error,这样就需要调用geterror来打印log分析哪一步有问题。
bool checkGlError(const char* funcName) {
GLint err = glGetError();
if (err != GL_NO_ERROR) {
ALOGE("GL error after %s(): 0x%08x\n", funcName, err);
return true;
}
return false;
}
4.2.2. 代码的实现部分:
bool RendererES3::init() {
mProgram = createProgram(VERTEX_SHADER, FRAGMENT_SHADER);
if (!mProgram)
return false;
// Set OpenGL options
glEnable(GL_CULL_FACE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glUseProgram(mProgram);
// FreeType
FT_Library ft;
// All functions return a value different than 0 whenever an error occurred
if (FT_Init_FreeType(&ft))
ALOGE("ERROR::FREETYPE: Could not init FreeType Library");
// Load font as face
FT_Face face;
/*load新的字库,这边我用的是中文的字库做实验,如果无法读取这个ttf的字库会返回error*/
if (FT_New_Face(ft, "/data/data/com.android.gles3jni/DroidSansFallback.ttf", 0, &face))
ALOGE("ERROR::FREETYPE: Failed to load font");
// Set size to load glyphs as
FT_Set_Pixel_Sizes(face, 0, 48);
// Disable byte-alignment restriction
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
checkGlError("1111111111111111");
// Load first 128 characters of ASCII set,如果是英文的字符ASCII码的话,就只需要存储128个字符对应的bitmap的信息,这样可能会节省时间方便查找绘制,如果是中文的字库的话,就不建议这么做,因为中文的字形状有很多种,需要另外的缓存机制,类似Android skia的缓存机制,需要研究下
//for (GLubyte c = 0; c < 128; c++)
//{wchar_t cc = 0x6211; “unicode的“我”字的编码”,因为freetype无法试别中文字体,需要转换成unicode的编码才能查到这个字体
{//wchar_t cc = 0x3002;//这个是个“。”的字形
// Load character glyph
int error = 0;
FT_Load_Char(face, 0x3002, FT_LOAD_RENDER);
{
ALOGE("ERROR::FREETYTPE: Failed to load Glyph");
//continue;
}
// error = FT_Get_Glyph(face->glyph, &glyph);
//目前实验下来,FT_Get_Char_Index+FT_Get_Glyph+ FT_Glyph_To_Bitmap,拿不到文字的bitmap,画出来是乱码,这个需要再查看接口使用是否有问题。不管是中文字形的unicode 和英文的ASCII都能正确的查询得到bitmap和基本的字形信息。
if(!error)
{
// FT_Glyph_To_Bitmap(&glyph,FT_RENDER_MODE_NORMAL, 0, 0);
// FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph;
// FT_Bitmap bitmap = bitmap_glyph->bitmap;
// Generate texture
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RED,//由于freetype默认的load的bitmap的图片是只有R通道的输出,所以只用这个RED的设置就行。以后的设置会有区别,会根据数据填充的方式不同变化。
face->glyph->bitmap.width,//bitmap的宽
face->glyph->bitmap.rows, //bitmap的高
0,
GL_RED,
GL_UNSIGNED_BYTE,
face->glyph->bitmap.buffer//bitmap的字形raw data
);
// Set texture options
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Now store character for later use, 这边是存储查询到的信息,做成了map,这个是为了一开始实现ASCII做缓存用的
Character character = {
texture,
face->glyph->bitmap.width, face->glyph->bitmap.rows,
face->glyph->bitmap_left, face->glyph->bitmap_top,
face->glyph->advance.x
};
Characters.insert(std::pair<char , Character>('a', character));
}
}
checkGlError("2222222222222");
glBindTexture(GL_TEXTURE_2D, 0);
// Destroy FreeType once we're finished
FT_Done_Face(face); //查询完字体之后需要释放freetype的内存。
FT_Done_FreeType(ft);
checkGlError("3333333333333");
// Configure VAO/VBO for texture quads
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 4 * 4, NULL, GL_DYNAMIC_DRAW); //初始化buffer data的信息
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0); //初始化vertex data的信息
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
ALOGE("Using OpenGL ES 3.0 renderer 111");
checkGlError("444444");
return true;
}
真正的绘制字体的部分,主要是计算vertex的位置信息:
void RendererES3::RenderText(std::string text, GLfloat x, GLfloat y, GLfloat scale, float color[])
{
// Activate corresponding render state
glUniform3f(glGetUniformLocation(mProgram, "textColor"), color[0], color[1], color[2]);//这个可以做背景颜色的绘制,这个之后再更新
glActiveTexture(GL_TEXTURE0);
glBindVertexArray(VAO);
// Iterate through all characters
//std::string::const_iterator c;
GLfloat skip = 0.0;
//for (c = text.begin(); c != text.end(); c++)
{
Character ch = Characters['a'];
//ALOGE(" char : %c", *c);
ALOGE("x = %f, y=%f", x, y);
ALOGE("w = %u, h =%u", ch.w, ch.h);
ALOGE("bx = %d, by =%d", ch.BearingX, ch.BearingY);
ALOGE("Ad = %ld", ch.Advance);
GLfloat xpos = x + ch.BearingX *scale; //scale字体需要做放大处理,否则对应到屏幕上,会很小,但是目前无法确认这个bearingx的单位是什么,可能是1/64的pixel的大小。
xpos /= 1080.0f;
GLfloat ypos = y - (ch.h - ch.BearingY) ; //(x,y)是字体绘制左下角的起始位置
ypos /= 1920.0f;//因为我没有乘上投影矩阵,所以我这边需要除以宽高,做归一化的处理这个有待研究,是否需要除以宽高
GLfloat w = ch.w * scale / 1080.0f;
GLfloat h = ch.h * scale / 1920.0f;
//skip /= 1080.0f;
// Update VBO for each character 以下的设置是画在(0,0)周围
GLfloat vertices[4][4] = {
{ (xpos + w)+skip, (ypos + h), 1.0, 1.0 },
{ (xpos + w)+skip, -ypos, 1.0, 0.0 },
{ -xpos+skip, -ypos, 0.0, 0.0 },
{ -xpos+skip, (ypos + h), 0.0, 1.0 },
}; //这个贴图出来的文字是倒立的,所以需要texture的位置如下:
GLfloat vertices[4][4] = {
{ (xpos + w)+skip, (ypos + h), 1.0, 0.0 },
{ (xpos + w)+skip, -ypos, 1.0, 1.0 },
{ -xpos+skip, -ypos, 0.0, 1.0 },
{ -xpos+skip, (ypos + h), 0.0, 0.0 },
};其实就是图片的竖直方向的翻转可参考手机坐标屏幕的位置
// Render glyph texture over quad
glBindTexture(GL_TEXTURE_2D, ch.TextureID);
// Update content of VBO memory
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) *16, vertices, GL_DYNAMIC_DRAW);
//glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); // glBufferSubData 和glBufferData 的使用需要再做check https://learnopengl-cn.readthedocs.io/zh/latest/04%20Advanced%20OpenGL/07%20Advanced%20Data/
// position attribute
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// texture coord attribute
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float) + 0));
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// Render quad
glDrawArrays(GL_TRIANGLES, 0, 3);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, indices);
// Now advance cursors for next glyph (note that advance is number of 1/64 pixels) 画多个文字的时候需要有间隔,这个可做参考,但是我觉着还是用width做参考比较好,这个advance的值应该是用在上下文字距离的计算
skip = w ; // Bitshift by 6 to get value in pixels (2^6 = 64 (divide amount of 1/64th pixels by 64 to get amount of pixels))
}
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
}
以上运行application之后运行的结果如下:
5. Freetype显示特殊字体的设置
5.1 斜体设置
斜体在FreeType中可以通过矩阵变换来实现,只要把矩阵设置成一个切边矩阵就可以了,方法如下:
// 倾斜度,越大就越斜
float lean = 0.5f;
FT_Matrix matrix;
matrix.xx = 0x10000L;
matrix.xy = lean * 0x10000L;
matrix.yx = 0;
matrix.yy = 0x10000L;
FT_Set_Transform( face, &matrix, 0 );
FT_Load_Char(face, 0x6211, FT_LOAD_RENDER);
5.2 描边设置,这个算法看起来比较复杂,这个是从网上找的范例
index = FT_Get_Char_Index(face, 0x6211); //从字库中找到这个中文字体的轮廓点阵
//load为一个不需要生成bitmap的glyph
if (FT_Load_Glyph(face, index, FT_LOAD_NO_BITMAP) == 0) {
Pixel32 *pxl = NULL;
GLuint imgWidth = 0; //生成的pixel的宽/高
GLuint imgHeight = 0;
GLuint imgSize = 0;
ALOGE("DEBUG::FREETYTPE: load Glyph successfully");
if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
Spans spans;
RenderSpans(ft, &face->glyph->outline, &spans);//拿到原始的轮廓点阵
Spans outlineSpans;
FT_Stroker stroker;
FT_Stroker_New(ft, &stroker); //FT_Stroker_New创建一个笔触
//设置笔触为描边
FT_Stroker_Set(stroker, (int)(WriteGlyphAsTGA * 64), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
if (FT_Get_Glyph(face->glyph, &glyph) == 0) {
FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1);
if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
FT_Outline *out = &reinterpret_cast<FT_OutlineGlyph>(glyph)->outline;
RenderSpans(ft, out, &outlineSpans); //拿到描边的轮廓点阵
}
FT_Stroker_Done(stroker);
FT_Done_Glyph(glyph);
if (!spans.empty())
{
// Figure out what the bounding rect is for both the span lists.
计算出来最大的rect范围,包含所有的轮廓点。
Rect rect(spans.front().x,
spans.front().y,
spans.front().x,
spans.front().y);
for (Spans::iterator s = spans.begin();
s != spans.end(); ++s)
{
rect.Include(Vec2(s->x, s->y));
rect.Include(Vec2(s->x + s->width - 1, s->y));
}
for (Spans::iterator s = outlineSpans.begin();
s != outlineSpans.end(); ++s)
{
rect.Include(Vec2(s->x, s->y));
rect.Include(Vec2(s->x + s->width - 1, s->y));
}
// Get some metrics of our image.
imgWidth = rect.Width(),
imgHeight = rect.Height(),
imgSize = imgWidth * imgHeight;
// Allocate data for our image and clear it out to transparent.
Pixel32 *pxl = new Pixel32[imgSize];
memset(pxl, 0, sizeof(Pixel32) * imgSize);
Pixel32 outlineCol = Pixel32(255, 90, 30);
Pixel32 fontCol = Pixel32(255, 255, 255);
// Loop over the outline spans and just draw them into the
// image. 根据对应的轮廓的点阵,填充颜色,有点的地方才需要填充颜 色,这个需要研究如何计算这个点。
for (Spans::iterator s = outlineSpans.begin();
s != outlineSpans.end(); ++s)
for (int w = 0; w < s->width; ++w)
pxl[(int)((imgHeight - 1 - (s->y - rect.ymin)) * imgWidth
+ s->x - rect.xmin + w)] =
Pixel32(outlineCol.r, outlineCol.g, outlineCol.b,
s->coverage);
// Then loop over the regular glyph spans and blend them into
// the image.
for (Spans::iterator s = spans.begin();
s != spans.end(); ++s)
for (int w = 0; w < s->width; ++w)
{
Pixel32 &dst =
pxl[(int)((imgHeight - 1 - (s->y - rect.ymin)) * imgWidth
+ s->x - rect.xmin + w)];
Pixel32 src = Pixel32(fontCol.r, fontCol.g, fontCol.b,
s->coverage);
dst.r = (int)(dst.r + ((src.r - dst.r) * src.a) / 255.0f);
dst.g = (int)(dst.g + ((src.g - dst.g) * src.a) / 255.0f);
dst.b = (int)(dst.b + ((src.b - dst.b) * src.a) / 255.0f);
dst.a = MIN(255, dst.a + src.a);
}
// Generate texture
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RGBA, //上边的pixels的数据,是32位的数据,所以这边要添加RGBA的数据类型,否则会画不出来形状。
imgWidth,
imgHeight,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
pxl
);
// Set texture options
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Now store character for later use
Character character = {
texture,
imgWidth, imgHeight,
10.0f, 10.0,
0
};
Characters.insert(std::pair<char , Character>('a', character));
}
}
}
}
5.2.1 FT_outline的轮廓输出说明,
struct Span
{
Span() { }
Span(int _x, int _y, int _width, int _coverage)
: x(_x), y(_y), width(_width), coverage(_coverage) { }
int x, y, width, coverage;
};
(x,y)的坐标表示文字的轮廓落在了这个坐标点上,需要给这个点对应的显示颜色。
Y轴的数据从小开始,相同的y对应的整个横线上,一起存完,才会接着存y+1对应的坐标点。
X轴上,以bearingX为基线从小到大存贮span的点,
相同的x的坐标上,连续的坐标值,如果alpha的值一样的话,比如 3,4,5,6 有相同的alpha = 255的话,x = 3, w = 4, coverage = 255, y值的话就是纵坐标的值。
跑了句号“。”显示的坐标如下:(下图只是坐标的展示,没有那么准确)
BearingX : 1, BearingY : 8 // 这个BearingY值需要研究?????
整体的字体宽高是14,14
x : y : spans[i].len : spans[i].coverage
count = 6, y =-6 //y的值可是负值,对应的-6的横线上,有6点,每个点的alpha都不同,所以需要存6个坐标点。
5 : -6 : 1 : 22
6 : -6 : 1 : 82
7 : -6 : 1 : 105
8 : -6 : 1 : 92
9 : -6 : 1 : 40
10 : -6 : 1 : 0
……………
count = 6, y =2 // y=2的横线上,存储了8个点,其中x=2, 3的值,存储的alpha相同。
1 : 2 : 1 : 170
2 : 2 : 2 : 255
4 : 2 : 1 : 113
10 : 2 : 1 : 61
11 : 2 : 2 : 255
13 : 2 : 1 : 217
轮廓就是一个一个的整数的坐标点组成的,可以参考下图理解,只画了部分边上的点,中间取整数省略了。
5.3 加粗
字体加粗的方式,freetype的中有
FT_Outline_Embolden(&face->glyph->outline,200)
FT_Outline_EmboldenXY(&face->glyph->outline, 1, 200);
我设置的是对轮廓加粗,xy的方法是,为了调整xy大小,实现不同程度的粗细的字体。
6. 设置texture的时候需要注意下:
生成纹理用glTexImage2D来生成:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
glGenerateMipmap(GL_TEXTURE_2D);
参考了别的博客写的接口解释:
第一个参数指定了纹理目标(Target)。设置为GL_TEXTURE_2D意味着会生成与当前绑定的纹理对象在同一个目标上的纹理(任何绑定到GL_TEXTURE_1D和GL_TEXTURE_3D的纹理不会受到影响)。
第二个参数为纹理指定多级渐远纹理的级别,如果你希望单独手动设置每个多级渐远纹理的级别的话。这里我们填0,也就是基本级别。
第三个参数告诉OpenGL我们希望把纹理储存为何种格式。我们的图像只有RGBA值,因此我们也把纹理储存为RGBA值。
第四个和第五个参数设置最终的纹理的宽度和高度。我们之前加载图像的时候储存了它们,所以我们使用对应的变量。
下个参数应该总是被设为0(历史遗留问题)。
第七第八个参数定义了源图的格式和数据类型。我们使用RGBA值加载这个图像,并把它们储存为char(byte)数组,我们将会传入对应值。
最后一个参数是真正的图像,这个是存贮的数据pixels,如果这个里面保存的是RGBA的,格式,那
image[0] -> R值, 也就是char型的长度
image[1] -> G
image[2] -> B
image[3] -> A
当调用glTexImage2D时,当前绑定的纹理对象就会被附加上纹理图像。然而,目前只有基本级别(Base-level)的纹理图像被加载了,如果要使用多级渐远纹理,我们必须手动设置所有不同的图像(不断递增第二个参数)。或者,直接在生成纹理之后调用glGenerateMipmap。这会为当前绑定的纹理自动生成所有需要的多级渐远纹理。