KTX 格式和 SBM 格式
写这一篇文章的由来是,自己看到《OpenGL 超级宝典》第五章的纹理一节,发现直接调用几个封装好的函数就完事了,自己也不是很清楚内部到底做了什么。主要是因为这块涉及到了两个文件格式:KTX 和 SBM,它们封装了纹理图片和顶点相关的数据。
为了了解这两个文件格式,并且在之后的学习中能更加方便的提取其中的数据,自己特意不嫌麻烦的写了相对应的解析小工具。
KTX 格式
KTX 格式算是一种标准格式,可用来存储纹理数据。书籍配套所使用的 KTX 格式版本相对简单,文件格式头部定义也很简洁:
- struct header
- {
- unsigned char identifier[12];
- unsigned int endianness;
- unsigned int gltype;
- unsigned int gltypesize;
- unsigned int glformat;
- unsigned int glinternalformat;
- unsigned int glbaseinternalformat;
- unsigned int pixelwidth;
- unsigned int pixelheight;
- unsigned int pixeldepth;
- unsigned int arrayelements;
- unsigned int faces;
- unsigned int miplevels;
- unsigned int keypairbytes;
- };
各个成员的含义,这边也不细究:这也是写额外工具的原因,可以结合实际的文件,借此了解它们的值;同时在用到这个文件的时候,还能结合其值进一步了解。头部后面跟着的就是所需要的纹理数据。
如图 1 所示,就是一个 KTX 文件的解析结果:右上角展示了头部中各个成员的值,并且显示了纹理图片。
SBM 格式
SBM 格式相较于 KTX 格式会复杂的多,它是书籍作者自定义的一种文件格式,详细的说明在书中附录一节有介绍。在此处,如图 2 所示,我们直接结合一个具体文件的解析内容进行说明,这样更加直观。
首先和 KTX 格式一样,SBM 一开始也有一个头部定义:
- typedef struct SB6M_HEADER_t
- {
- union
- {
- unsigned int magic;
- char magic_name[4];
- };
- unsigned int size;
- unsigned int num_chunks;
- unsigned int flags;
- } SB6M_HEADER;
这边主要关注 num_chunks 成员。SBM 格式的总体布局就是由头部加上若干个区块组成的,而这里的 num_chunks 成员就是指明后续有多少个区块。图 2 中解析的文件有两个区块。
此处就不特意讲解 size 或者 offset 相关的成员含义了,它们是基于文件头还是区块头这些细节,自己处理的时候需要多加注意。
区块头部的定义如下:
- typedef struct SB6M_CHUNK_HEADER_t
- {
- union
- {
- unsigned int chunk_type;
- char chunk_name[4];
- };
- unsigned int size;
- } SB6M_CHUNK_HEADER;
有许多不同的区块,它们依据 magic_name 成员进行区分。size 成员可以跳过当前区块,以处理下一个区块。
如图 2 所示,此文件中定义了两个区块,分别是顶点数据块和顶点属性块,下面我们来依次了解它们。其他没有介绍到的区块也都大同小异,在用到的时候,再结合书中附录进行详细了解。
顶点数据块
顶点数据块的定义如下所示:
- typedef struct SB6M_CHUNK_VERTEX_DATA_t
- {
- SB6M_CHUNK_HEADER header;
- unsigned int data_size;
- unsigned int data_offset;
- unsigned int total_vertices;
- } SB6M_CHUNK_VERTEX_DATA;
顶点数据块的 magic_name 为 VRTX。data_size 指示数据大小;data_offset 指示数据偏移;total_vertices 指示顶点个数。
顶点属性块
顶点属性块的定义如下:
- typedef struct SB6M_VERTEX_ATTRIB_CHUNK_t
- {
- SB6M_CHUNK_HEADER header;
- unsigned int attrib_count;
- SB6M_VERTEX_ATTRIB_DECL attrib_data[1];
- } SB6M_VERTEX_ATTRIB_CHUNK;
顶点数据块的 magic_name 为 ATRB。attrib_data 是个可变长度的数组,用于描述顶点属性信息,其个数由 attrib_count 指定。顶点属性的定义如下:
- typedef struct SB6M_VERTEX_ATTRIB_DECL_t
- {
- char name[64];
- unsigned int size;
- unsigned int type;
- unsigned int stride;
- unsigned int flags;
- unsigned int data_offset;
- } SB6M_VERTEX_ATTRIB_DECL;
其中,name 指示不同的属性,比如顶点位置为 position,纹理映射为 map1;data_offset 指示属性对应数据的偏移;size、type 和 stride 成员指定具体数据的含义和布局,这些跟 OpenGL 数据相关。
调试
如上,我们已经可以获取到顶点位置数据、纹理映射数据以及纹理贴图数据。我们可以把顶点位置数据、纹理映射数据转化成 obj 格式,然后结合纹理贴图导入到 Maya 中进行查看。
obj 格式在之前的文章中有简单提及,这边也再次简单说明一下。
都是简单提及,可以看出 obj 格式是真的非常容易上手。
- v 0.499815 0.097545 -0.099419
像这样 v 打头的,定义一个顶点坐标。
- vt 0.015625 0.968750
像这样 vt 打头的,定义一个纹理坐标。
- f 1/1 2/2 3/3
像这样 f 打头的,定义一个三角面,第一组定义顶点坐标索引,第二组定义纹理坐标索引。
如图 3 所示,把 obj 文件用 Maya 打开,可以看到此物体的样子以及其 UV 坐标。并且可以使用提取得到的贴图图片进行渲染查看。
以上验证的方式可以发现,学习的过程何尝不是一种“造轮子”的过程。