本人在学习opengl做项目时,需要设计三维人脸模型。于是寻找多种方案。求得用opengl读取OBJ模型文件,并进行纹理修饰这一解决方案。在使用中用到了glm.c的库。主要用到如下几个函数,并进行了分析。希望能给大家有所帮助,如果分析中有不恰当的地方,望批评指正。建议花点时间看看
(1)obj文件的读取函数
GLMmodel*
glmReadOBJ(char* filename)
{
GLMmodel* model;
FILE* file;
/* open the file */
file = fopen(filename, "r");
if (!file) {
fprintf(stderr, "glmReadOBJ() failed: can't open data file \"%s\".\n",
filename);
exit(1);
}
/* allocate a new model */
model = (GLMmodel*)malloc(sizeof(GLMmodel));
model->pathname = strdup(filename);//模型文件和材质库的路径
model->mtllibname = NULL;//材质库名
model->numvertices = 0;//顶点数
model->vertices = NULL;//储存顶点的向量
model->numnormals = 0;//模型法向数
model->normals = NULL;//储存法向的向量
model->numtexcoords = 0;//纹理坐标数
model->texcoords = NULL;//储存纹理坐标的向量
model->numfacetnorms = 0;//规则平面数
model->facetnorms = NULL;//储存规则平面的向量
model->numtriangles = 0;//三角形数
model->triangles = NULL;//储存三角形的向量
model->nummaterials = 0;//材质数
model->materials = NULL;//储存材质的向量
model->numgroups = 0;//图元组数
model->groups = NULL;//储存图元组的向量
model->position[0] = 0.0;//模型的位置
model->position[1] = 0.0;
model->position[2] = 0.0;
/* make a first pass through the file to get a count of the number
of vertices, normals, texcoords & triangles */
glmFirstPass(model, file);//只是获得v(顶点)、vt(贴图坐标)、vn(法线)、f(面)等数量
/* allocate memory 开辟数据空间*/
model->vertices = (GLfloat*)malloc(sizeof(GLfloat) *
3 * (model->numvertices + 1));
model->triangles = (GLMtriangle*)malloc(sizeof(GLMtriangle) *
model->numtriangles);
if (model->numnormals) {
model->normals = (GLfloat*)malloc(sizeof(GLfloat) *
3 * (model->numnormals + 1));
}
if (model->numtexcoords) {
model->texcoords = (GLfloat*)malloc(sizeof(GLfloat) *
2 * (model->numtexcoords + 1));
}
/* rewind to beginning of file and read in the data this pass */
rewind(file);//将文件内部的位置指针重新指向一个流(数据流/文件)的开头
glmSecondPass(model, file);//读取v(顶点)、vt(贴图坐标)、vn(法线)、f(面)等的数据值
/* close the file */
fclose(file);
return model;
}
(2)/按mode指定模式使用当前的OPENGL绘制上下文(context)绘制模型函数
//mode指定写入方式,此参数为取或(“|”)的位联合:
//GLM_NONE - 只按顶点处理
//GLM_FLAT - 按面计算法向
//GLM_SMOOTH - 按顶点计算法向
//GLM_TEXTURE - 包含纹理座标
//GLM_COLOR - 只包含颜色信息(纯色材质)
//GLM_MATERIAL - 包含材质信息
//其中GLM_FLAT和GLM_SMOOTH不能同时指定
//GLM_COLOR和GLM_MATERIAL也不能同时指定
GLvoid
glmDraw(GLMmodel* model, GLuint mode)//画出obj模型
{
static GLuint i;
static GLMgroup* group;
static GLMtriangle* triangle;
static GLMmaterial* material;
assert(model);
assert(model->vertices);
/* do a bit of warning */
if (mode & GLM_FLAT && !model->facetnorms) {
printf("glmDraw() warning: flat render mode requested "
"with no facet normals defined.\n");
mode &= ~GLM_FLAT;
}
if (mode & GLM_SMOOTH && !model->normals) {
printf("glmDraw() warning: smooth render mode requested "
"with no normals defined.\n");
mode &= ~GLM_SMOOTH;
}
if (mode & GLM_TEXTURE && !model->texcoords) {
printf("glmDraw() warning: texture render mode requested "
"with no texture coordinates defined.\n");
mode &= ~GLM_TEXTURE;
}
if (mode & GLM_FLAT && mode & GLM_SMOOTH) {
printf("glmDraw() warning: flat render mode requested "
"and smooth render mode requested (using smooth).\n");
mode &= ~GLM_FLAT;
}
if (mode & GLM_COLOR && !model->materials) {
printf("glmDraw() warning: color render mode requested "
"with no materials defined.\n");
mode &= ~GLM_COLOR;
}
if (mode & GLM_MATERIAL && !model->materials) {
printf("glmDraw() warning: material render mode requested "
"with no materials defined.\n");
mode &= ~GLM_MATERIAL;
}
if (mode & GLM_COLOR && mode & GLM_MATERIAL) {
printf("glmDraw() warning: color and material render mode requested "
"using only material mode.\n");
mode &= ~GLM_COLOR;
}
if (mode & GLM_COLOR)
glEnable(GL_COLOR_MATERIAL);
else if (mode & GLM_MATERIAL)
glDisable(GL_COLOR_MATERIAL);
/* perhaps this loop should be unrolled into material, color, flat,
smooth, etc. loops? since most cpu's have good branch prediction
schemes (and these branches will always go one way), probably
wouldn't gain too much? */
group = model->groups;
while (group) {//mode若有GLM_MATERIAL,则配置材质属性
if (mode & GLM_MATERIAL) {
material = &model->materials[group->material];
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, material->ambient);//材质属性中的环境光
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, material->diffuse);//材质属性中的散射光
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, material->specular);//材质属性中的镜面反射光
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, material->shininess);//材质属性中的发射光
}
if (mode & GLM_COLOR) {
glColor3fv(material->diffuse);
}
glBegin(GL_TRIANGLES);//绘制三角形
for (i = 0; i < group->numtriangles; i++) {
triangle = &T(group->triangles[i]);
if (mode & GLM_FLAT)
glNormal3fv(&model->facetnorms[3 * triangle->findex]);
if (mode & GLM_SMOOTH)
glNormal3fv(&model->normals[3 * triangle->nindices[0]]);
if (mode & GLM_TEXTURE)//若要进行纹理修饰,则mode需要有GLM_TEXTURE
glTexCoord2fv(&model->texcoords[2 * triangle->tindices[0]]);//指定需绘制三角形第一个点的纹理坐标
glVertex3fv(&model->vertices[3 * triangle->vindices[0]]);
if (mode & GLM_SMOOTH)
glNormal3fv(&model->normals[3 * triangle->nindices[1]]);
if (mode & GLM_TEXTURE)//若要进行纹理修饰,则mode需要有GLM_TEXTURE
glTexCoord2fv(&model->texcoords[2 * triangle->tindices[1]]);//指定需绘制三角形第二个点的纹理坐标
glVertex3fv(&model->vertices[3 * triangle->vindices[1]]);
if (mode & GLM_SMOOTH)
glNormal3fv(&model->normals[3 * triangle->nindices[2]]);
if (mode & GLM_TEXTURE)//若要进行纹理修饰,则mode需要有GLM_TEXTURE
glTexCoord2fv(&model->texcoords[2 * triangle->tindices[2]]);//指定需绘制三角形第三个点的纹理坐标
glVertex3fv(&model->vertices[3 * triangle->vindices[2]]);
}
glEnd();
group = group->next;
}
}
(3)glmFirstPass(model, file);//只是获得v(顶点)、vt(贴图坐标)、vn(法线)、f(面)等数量
(再此不做分析了,挺简单的)
(4)glmSecondPass(model, file);//读取v(顶点)、vt(贴图坐标)、vn(法线)、f(面)等的数据值
static GLvoid
glmSecondPass(GLMmodel* model, FILE* file)
{
GLuint numvertices; /* number of vertices in model *///顶点数
GLuint numnormals; /* number of normals in model *///模型法向数
GLuint numtexcoords; /* number of texcoords in model *///模型法向数
GLuint numtriangles; /* number of triangles in model *///三角形数
GLfloat* vertices; /* array of vertices *///储存顶点的向量
GLfloat* normals; /* array of normals *///储存法向的向量
GLfloat* texcoords; /* array of texture coordinates *///储存纹理坐标的向量
GLMgroup* group; /* current group pointer *///储存图元组的向量
GLuint material; /* current material */
int v, n, t;
char buf[128];
/* set the pointer shortcuts */
vertices = model->vertices;
normals = model->normals;
texcoords = model->texcoords;
group = model->groups;
/* on the second pass through the file, read all the data into the
allocated arrays */
numvertices = numnormals = numtexcoords = 1;
numtriangles = 0;
material = 0;
while(fscanf(file, "%s", buf) != EOF) {
switch(buf[0]) {
case '#': /* comment */
/* eat up rest of line */
fgets(buf, sizeof(buf), file);
break;
case 'v': /* v, vn, vt */
switch(buf[1]) {
case '\0': /* vertex */
fscanf(file, "%f %f %f",
&vertices[3 * numvertices + 0], //第numvertices个顶点的x轴坐标
&vertices[3 * numvertices + 1], //第numvertices个顶点的y轴坐标
&vertices[3 * numvertices + 2]);//第numvertices个顶点的z轴坐标
numvertices++;
break;
case 'n': /* normal */
fscanf(file, "%f %f %f",
&normals[3 * numnormals + 0],//第numnormals个顶点法向量的x轴坐标
&normals[3 * numnormals + 1], //第numnormals个顶点法向量的y轴坐标
&normals[3 * numnormals + 2]);//第numnormals个顶点法向量的z轴坐标
numnormals++;
break;
case 't': /* texcoord */
fscanf(file, "%f %f",
&texcoords[2 * numtexcoords + 0],//第numtexcoords个贴图坐标的x轴坐标
&texcoords[2 * numtexcoords + 1]);//第numtexcoords个贴图坐标的y轴坐标
numtexcoords++;
break;
}
break;
case 'u':
fgets(buf, sizeof(buf), file);
sscanf(buf, "%s %s", buf, buf);
group->material = material = glmFindMaterial(model, buf);
break;
case 'g': /* group */
/* eat up rest of line */
fgets(buf, sizeof(buf), file);
#if SINGLE_STRING_GROUP_NAMES
sscanf(buf, "%s", buf);
#else
buf[strlen(buf)-1] = '\0'; /* nuke '\n' */
#endif
group = glmFindGroup(model, buf);
group->material = material;
break;
case 'f': /* face */
v = n = t = 0;
fscanf(file, "%s", buf);
/* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */
if (strstr(buf, "//")) {
/* v//n */
sscanf(buf, "%d//%d", &v, &n);
T(numtriangles).vindices[0] = v < 0 ? v + numvertices : v;
T(numtriangles).nindices[0] = n < 0 ? n + numnormals : n;
fscanf(file, "%d//%d", &v, &n);
T(numtriangles).vindices[1] = v < 0 ? v + numvertices : v;
T(numtriangles).nindices[1] = n < 0 ? n + numnormals : n;
fscanf(file, "%d//%d", &v, &n);
T(numtriangles).vindices[2] = v < 0 ? v + numvertices : v;
T(numtriangles).nindices[2] = n < 0 ? n + numnormals : n;
group->triangles[group->numtriangles++] = numtriangles;
numtriangles++;
while(fscanf(file, "%d//%d", &v, &n) > 0) {
T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
T(numtriangles).nindices[0] = T(numtriangles-1).nindices[0];
T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
T(numtriangles).nindices[1] = T(numtriangles-1).nindices[2];
T(numtriangles).vindices[2] = v < 0 ? v + numvertices : v;
T(numtriangles).nindices[2] = n < 0 ? n + numnormals : n;
group->triangles[group->numtriangles++] = numtriangles;
numtriangles++;
}
} else if (sscanf(buf, "%d/%d/%d", &v, &t, &n) == 3) {
/* v/t/n */
T(numtriangles).vindices[0] = v < 0 ? v + numvertices : v;
T(numtriangles).tindices[0] = t < 0 ? t + numtexcoords : t;
T(numtriangles).nindices[0] = n < 0 ? n + numnormals : n;
fscanf(file, "%d/%d/%d", &v, &t, &n);
T(numtriangles).vindices[1] = v < 0 ? v + numvertices : v;
T(numtriangles).tindices[1] = t < 0 ? t + numtexcoords : t;
T(numtriangles).nindices[1] = n < 0 ? n + numnormals : n;
fscanf(file, "%d/%d/%d", &v, &t, &n);
T(numtriangles).vindices[2] = v < 0 ? v + numvertices : v;
T(numtriangles).tindices[2] = t < 0 ? t + numtexcoords : t;
T(numtriangles).nindices[2] = n < 0 ? n + numnormals : n;
group->triangles[group->numtriangles++] = numtriangles;
numtriangles++;
while(fscanf(file, "%d/%d/%d", &v, &t, &n) > 0) {
T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
T(numtriangles).tindices[0] = T(numtriangles-1).tindices[0];
T(numtriangles).nindices[0] = T(numtriangles-1).nindices[0];
T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
T(numtriangles).tindices[1] = T(numtriangles-1).tindices[2];
T(numtriangles).nindices[1] = T(numtriangles-1).nindices[2];
T(numtriangles).vindices[2] = v < 0 ? v + numvertices : v;
T(numtriangles).tindices[2] = t < 0 ? t + numtexcoords : t;
T(numtriangles).nindices[2] = n < 0 ? n + numnormals : n;
group->triangles[group->numtriangles++] = numtriangles;
numtriangles++;
}
} else if (sscanf(buf, "%d/%d", &v, &t) == 2) {
/* v/t */
T(numtriangles).vindices[0] = v < 0 ? v + numvertices : v;
T(numtriangles).tindices[0] = t < 0 ? t + numtexcoords : t;
fscanf(file, "%d/%d", &v, &t);
T(numtriangles).vindices[1] = v < 0 ? v + numvertices : v;
T(numtriangles).tindices[1] = t < 0 ? t + numtexcoords : t;
fscanf(file, "%d/%d", &v, &t);
T(numtriangles).vindices[2] = v < 0 ? v + numvertices : v;
T(numtriangles).tindices[2] = t < 0 ? t + numtexcoords : t;
group->triangles[group->numtriangles++] = numtriangles;
numtriangles++;
while(fscanf(file, "%d/%d", &v, &t) > 0) {
T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
T(numtriangles).tindices[0] = T(numtriangles-1).tindices[0];
T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
T(numtriangles).tindices[1] = T(numtriangles-1).tindices[2];
T(numtriangles).vindices[2] = v < 0 ? v + numvertices : v;
T(numtriangles).tindices[2] = t < 0 ? t + numtexcoords : t;
group->triangles[group->numtriangles++] = numtriangles;
numtriangles++;
}
} else {
/* v */
sscanf(buf, "%d", &v);
T(numtriangles).vindices[0] = v < 0 ? v + numvertices : v;
fscanf(file, "%d", &v);
T(numtriangles).vindices[1] = v < 0 ? v + numvertices : v;
fscanf(file, "%d", &v);
T(numtriangles).vindices[2] = v < 0 ? v + numvertices : v;
group->triangles[group->numtriangles++] = numtriangles;
numtriangles++;
while(fscanf(file, "%d", &v) > 0) {
T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
T(numtriangles).vindices[2] = v < 0 ? v + numvertices : v;
group->triangles[group->numtriangles++] = numtriangles;
numtriangles++;
}
}
break;
default:
/* eat up rest of line */
fgets(buf, sizeof(buf), file);
break;
}
}
#if 0
/* announce the memory requirements */
printf(" Memory: %d bytes\n",
numvertices * 3*sizeof(GLfloat) +
numnormals * 3*sizeof(GLfloat) * (numnormals ? 1 : 0) +
numtexcoords * 3*sizeof(GLfloat) * (numtexcoords ? 1 : 0) +
numtriangles * sizeof(GLMtriangle));
#endif
}