您的位置:首页 > 运维架构

利用OpenGL进行模型表现

2010-03-04 17:14 393 查看
这篇文章介绍一下用OpenGL来表现模型对象。比如用3DMAX等工具做好一个模型后,我们希望能够在自己的程序中使用它。一般使用这些工具的导出功能导出特定的文件,然后读取其中我们感兴趣的部分,再把这些感兴趣的数据在我们的程序中组织好就可以了。利用OpenGL来表现静态模型是很简单的。

这里为了简单起见,模型对象文件为txt文本,只包含了模型的顶点个数,面个数,顶点坐标,索引信息。当然在一些复杂的模型文件中,还包括法线,贴图坐标等等信息。重要的问题是,如果组织这些信息。

219483
435667
3.031209 1.875606 1.422974
3.030240 1.863352 1.404835
3.030240 1.863352 1.404835
3.021032 1.846390 1.404004
3.021032 1.846390 1.404004
3.050020 1.888645 1.389481
3.040140 1.883706 1.400404

.

.

.

2869 2977 2973
2973 2974 2869
2979 2978 2975
2980 2979 2976
2976 2979 2975
2982 3104 3101
3096 2983 2982
3094 2984 3096
2984 2983 3096
3094 2972 2984
2979 2873 2978
2979 2980 2988

.

.

.

如上面的信息一样,第一行数据为该模型的顶点个数,第二行数据为该模型的三角形面个数。后面的数据分别人顶点坐标和每一个面的顶点索引信息。很自然的,首先可以想到要在程序中表现该模型,需要vertex的数据类型,于是可以定义这样的结构体,可以包含顶点的坐标和法线信息。

view plaincopy to clipboardprint?
struct Vertex
{
double P[3];
double N[3];
};
struct Vertex
{
double P[3];
double N[3];
};

这里的顶点结构包含了顶点坐标P和法线N。

然后就是面的数据类型

view plaincopy to clipboardprint?
struct Face
{
int Idx[3];
};
struct Face
{
int Idx[3];
};

一个面由3个顶点构成,只要有了顶点的索引就可以了。

最后就是模型的数据类型了

view plaincopy to clipboardprint?
struct Model
{
int vNum;
int fNum;
Vertex *VertList;
Face *FaceList;
};
struct Model
{
int vNum;
int fNum;
Vertex *VertList;
Face *FaceList;
};

这个数据类型包含了模型顶点数vNum,面数fNum,指向顶点列表的指针VertList和指向面列表的指针FaceList。

然后这样只要用Model来定义一个模型就可以了

Model MyModel;

下面来看看如果从文件中读取信息。可以把读取文件的部分写成一个函数,比如叫LoadModel,函数的参数只需要提供文件名就可以了。

view plaincopy to clipboardprint?
void LoadModel(char *fname)
{
FILE *fp;
fp = fopen(fname, "r");

fscanf(fp, "%d", &MyModel.vNum);
fscanf(fp, "%d", &MyModel.fNum);

MyModel.VertList = new Vertex [MyModel.vNum];
MyModel.FaceList = new Face [MyModel.fNum];

int vNum = MyModel.vNum;
int fNum = MyModel.fNum;

for (int i = 0; i < vNum; ++i)
{
double x, y, z;
fscanf(fp, "%lf %lf %lf", &x, &y, &z);
MyModel.VertList[i].P[0] = x;
MyModel.VertList[i].P[1] = y;
MyModel.VertList[i].P[2] = z;

MyModel.VertList[i].N[0] = 0.0;
MyModel.VertList[i].N[1] = 0.0;
MyModel.VertList[i].N[2] = 0.0;

}

for (int i = 0; i < fNum; ++i)
{
int idx0, idx1, idx2;
fscanf(fp, "%d %d %d", &idx0, &idx1, &idx2);
MyModel.FaceList[i].Idx[0] = idx0;
MyModel.FaceList[i].Idx[1] = idx1;
MyModel.FaceList[i].Idx[2] = idx2;

double x0, y0, z0;
double x1, y1, z1;
double x2, y2, z2;

x0 = MyModel.VertList[idx0].P[0];
y0 = MyModel.VertList[idx0].P[1];
z0 = MyModel.VertList[idx0].P[2];

x1 = MyModel.VertList[idx1].P[0];
y1 = MyModel.VertList[idx1].P[1];
z1 = MyModel.VertList[idx1].P[2];

x2 = MyModel.VertList[idx2].P[0];
y2 = MyModel.VertList[idx2].P[1];
z2 = MyModel.VertList[idx2].P[2];

double ax, ay, az;
double bx, by, bz;
double nx, ny, nz;
ax = x1 - x0;
ay = y1 - y0;
az = z1 - z0;

bx = x2 - x0;
by = y2 - y0;
bz = z2 - z0;

nx = ay * bz - az * by;
ny = az * bx - ax * bz;
nz = ax * by - ay * bx;

MyModel.VertList[idx0].N[0] += nx;
MyModel.VertList[idx0].N[1] += ny;
MyModel.VertList[idx0].N[2] += nz;

MyModel.VertList[idx1].N[0] += nx;
MyModel.VertList[idx1].N[1] += ny;
MyModel.VertList[idx1].N[2] += nz;

MyModel.VertList[idx2].N[0] += nx;
MyModel.VertList[idx2].N[1] += ny;
MyModel.VertList[idx2].N[2] += nz;
}

for (int i = 0; i < vNum; ++i)
{
double nx, ny, nz;
nx = MyModel.VertList[i].N[0];
ny = MyModel.VertList[i].N[1];
nz = MyModel.VertList[i].N[2];

double norm = sqrt(nx * nx + ny * ny + nz * nz);

MyModel.VertList[i].N[0] /= norm;
MyModel.VertList[i].N[1] /= norm;
MyModel.VertList[i].N[2] /= norm;
}


}
void LoadModel(char *fname)
{
FILE *fp;
fp = fopen(fname, "r");

fscanf(fp, "%d", &MyModel.vNum);
fscanf(fp, "%d", &MyModel.fNum);

MyModel.VertList = new Vertex [MyModel.vNum];
MyModel.FaceList = new Face [MyModel.fNum];

int vNum = MyModel.vNum;
int fNum = MyModel.fNum;

for (int i = 0; i < vNum; ++i)
{
double x, y, z;
fscanf(fp, "%lf %lf %lf", &x, &y, &z);
MyModel.VertList[i].P[0] = x;
MyModel.VertList[i].P[1] = y;
MyModel.VertList[i].P[2] = z;

MyModel.VertList[i].N[0] = 0.0;
MyModel.VertList[i].N[1] = 0.0;
MyModel.VertList[i].N[2] = 0.0;

}

for (int i = 0; i < fNum; ++i)
{
int idx0, idx1, idx2;
fscanf(fp, "%d %d %d", &idx0, &idx1, &idx2);
MyModel.FaceList[i].Idx[0] = idx0;
MyModel.FaceList[i].Idx[1] = idx1;
MyModel.FaceList[i].Idx[2] = idx2;

double x0, y0, z0;
double x1, y1, z1;
double x2, y2, z2;

x0 = MyModel.VertList[idx0].P[0];
y0 = MyModel.VertList[idx0].P[1];
z0 = MyModel.VertList[idx0].P[2];

x1 = MyModel.VertList[idx1].P[0];
y1 = MyModel.VertList[idx1].P[1];
z1 = MyModel.VertList[idx1].P[2];

x2 = MyModel.VertList[idx2].P[0];
y2 = MyModel.VertList[idx2].P[1];
z2 = MyModel.VertList[idx2].P[2];

double ax, ay, az;
double bx, by, bz;
double nx, ny, nz;
ax = x1 - x0;
ay = y1 - y0;
az = z1 - z0;

bx = x2 - x0;
by = y2 - y0;
bz = z2 - z0;

nx = ay * bz - az * by;
ny = az * bx - ax * bz;
nz = ax * by - ay * bx;

MyModel.VertList[idx0].N[0] += nx;
MyModel.VertList[idx0].N[1] += ny;
MyModel.VertList[idx0].N[2] += nz;

MyModel.VertList[idx1].N[0] += nx;
MyModel.VertList[idx1].N[1] += ny;
MyModel.VertList[idx1].N[2] += nz;

MyModel.VertList[idx2].N[0] += nx;
MyModel.VertList[idx2].N[1] += ny;
MyModel.VertList[idx2].N[2] += nz;
}

for (int i = 0; i < vNum; ++i)
{
double nx, ny, nz;
nx = MyModel.VertList[i].N[0];
ny = MyModel.VertList[i].N[1];
nz = MyModel.VertList[i].N[2];

double norm = sqrt(nx * nx + ny * ny + nz * nz);

MyModel.VertList[i].N[0] /= norm;
MyModel.VertList[i].N[1] /= norm;
MyModel.VertList[i].N[2] /= norm;
}

}

上面的代码比较长,但是内容很简单。首先打开文件,然后分别以整型的方式读取顶点个数和面个数,保存在结构体变量中。然后为顶点列表和面列表动态申请内存空间,在循环中分别读取每个数据。由于顶点包括法线信息,所以这里还进行了法线的计算。这里采用平均法线,即把一个顶点的多个法线加在一起,然后再进行单位化。由于一个顶点可能被用于多个面,所以该顶点的法线就是所有面的面法线的和。然后在程序初始化的时候,我们就可以调用这个函数来进行对模型的初始化。

最后就是里利用OpenGL来渲染模型了。有了上面的所有信息,要渲染模型就很简单了。我们只用利用画三角形的方法就可以了。

view plaincopy to clipboardprint?
glBegin(GL_TRIANGLES);
for (int i = 0; i < MyModel.fNum; ++i)
{
int idx0, idx1, idx2;
idx0 = MyModel.FaceList[i].Idx[0];
idx1 = MyModel.FaceList[i].Idx[1];
idx2 = MyModel.FaceList[i].Idx[2];

glNormal3dv(MyModel.VertList[idx0].N);
glVertex3dv(MyModel.VertList[idx0].P);

glNormal3dv(MyModel.VertList[idx1].N);
glVertex3dv(MyModel.VertList[idx1].P);

glNormal3dv(MyModel.VertList[idx2].N);
glVertex3dv(MyModel.VertList[idx2].P);
}
glEnd();
glBegin(GL_TRIANGLES);
for (int i = 0; i < MyModel.fNum; ++i)
{
int idx0, idx1, idx2;
idx0 = MyModel.FaceList[i].Idx[0];
idx1 = MyModel.FaceList[i].Idx[1];
idx2 = MyModel.FaceList[i].Idx[2];

glNormal3dv(MyModel.VertList[idx0].N);
glVertex3dv(MyModel.VertList[idx0].P);

glNormal3dv(MyModel.VertList[idx1].N);
glVertex3dv(MyModel.VertList[idx1].P);

glNormal3dv(MyModel.VertList[idx2].N);
glVertex3dv(MyModel.VertList[idx2].P);
}
glEnd();

可以看到,渲染部分很简单,就是在循环中依次访问面列表中每个面。并且给出每个面每个顶点的法线。







*原创文章,转载请注明出处



本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/zhangci226/archive/2009/12/23/5060634.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: