|
第25课 |
|
|
变形和从文件中加载3D物体:
在这一课中,你将学会如何从文件加载3D模型,并且平滑的从一个模型变换为另一个模型。 |
|
|
|
欢迎来到这激动人心的一课,在这一课里,我们将介绍模型的变形。需要注意的是各个模型必须要有相同的顶点,才能一一对应,并应用变形。
在这一课里,我们同样要教会你如何从一个文件中读取模型数据。
文件开始的部分和前面一样,没有任何变化。 |
|
|
我们结下来添加几个旋转变量,用来记录旋转的信息。并使用cx,cy,cz设置物体在屏幕上的位置。
变量key用来记录当前的模型,step用来设置相邻变形之间的中间步骤。如step为200,则需要200次,才能把一个物体变为另一个物体。
最后我们用一个变量来设置是否使用变形。 |
|
GLfloat xrot,yrot,zrot,
xspeed,yspeed,zspeed,
cx,cy,cz=-15;
int key=1;
int step=0,steps=200;
bool morph=FALSE;
|
下面的结构定义一个三维顶点 |
|
typedef struct
{
float x, y, z;
} VERTEX;
|
下面的结构使用顶点来描述一个三维物体 |
|
typedef struct
{
int verts;
VERTEX *points;
} OBJECT;
|
maxver用来记录各个物体中最大的顶点数,如一个物体使用5个顶点,另一个物体使用20个顶点,那么物体的顶点个数为20。
结下来定义了四个我们使用的模型物体,并把相邻模型变形的中间状态保存在helper中,sour保存原模型物体,dest保存将要变形的模型物体。 |
|
int maxver;
OBJECT morph1,morph2,morph3,morph4,
helper,*sour,*dest;
|
WndProc()函数没有变化 |
|
|
下面的函数用来为模型分配保存顶点数据的内存空间 |
|
void objallocate(OBJECT *k,int n)
{
k->points=(VERTEX*)malloc(sizeof(VERTEX)*n);
}
|
下面的函数用来释放为模型分配的内存空间 |
|
void objfree(OBJECT *k)
{
free(k->points);
}
|
下面的代码用来读取文件中的一行。
我们用一个循环来读取字符,最多读取255个字符,当遇到'\n'回车时,停止读取并立即返回。 |
|
void readstr(FILE *f,char *string)
{
do
{
fgets(string, 255, f);
} while ((string[0] == '/') || (string[0] == '\n'));
return;
}
|
下面的代码用来加载一个模型文件,并为模型分配内存,把数据存储进去。 |
|
void objload(char *name,OBJECT *k)
{
int ver;
float rx,ry,rz;
FILE *filein;
char oneline[255];
filein = fopen(name, "rt");
readstr(filein,oneline);
sscanf(oneline, "Vertices: %d\n", &ver;);
k->verts=ver;
objallocate(k,ver);
|
下面的循环,读取每一行(即每个顶点)的数据,并把它保存到内存中?/td>
| |
for (int i=0;i<ver;i++)
{
readstr(filein,oneline);
sscanf(oneline, "%f %f %f", ℞, &ry;, &rz;);
k->points[i].x = rx;
k->points[i].y = ry;
k->points[i].z = rz;
}
fclose(filein);
if(ver>maxver) maxver=ver;
}
|
下面的函数根据设定的间隔,计算第i个顶点每次变换的位移 |
|
VERTEX calculate(int i)
{
VERTEX a;
a.x=(sour->points[i].x-dest->points[i].x)/steps;
a.y=(sour->points[i].y-dest->points[i].y)/steps;
a.z=(sour->points[i].z-dest->points[i].z)/steps;
return a;
}
|
ReSizeGLScene()函数没有变化 |
|
GLvoid ReSizeGLScene(GLsizei width, GLsizei height)
|
下面的函数完成初始化功能,它设置混合模式为半透明 |
|
int InitGL(GLvoid)
{
glBlendFunc(GL_SRC_ALPHA,GL_ONE);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClearDepth(1.0);
glDepthFunc(GL_LESS);
glEnable(GL_DEPTH_TEST);
glShadeModel(GL_SMOOTH);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
|
下面的代码用来加载我们的模型物体 |
|
maxver=0;
objload("data/sphere.txt",&morph1;);
objload("data/torus.txt",&morph2;);
objload("data/tube.txt",&morph3;);
|
第四个模型不从文件读取,我们在(-7,-7,-7)-(7,7,7)之间随机生成模型点,它和我们载如的模型都一样具有486个顶点。 |
|
objallocate(&morph4;,486);
for(int i=0;i<486;i++)
{
morph4.points[i].x=((float)(rand()%14000)/1000)-7;
morph4.points[i].y=((float)(rand()%14000)/1000)-7;
morph4.points[i].z=((float)(rand()%14000)/1000)-7;
}
|
初始化中间模型为球体,并把原和目标模型都设置为球 |
|
objload("data/sphere.txt",&helper;);
sour=dest=&morph1;
return TRUE;
}
|
下面是具体的绘制代码,向往常一样我们先设置模型变化,以便我们更好的观察。 |
|
void DrawGLScene(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(cx,cy,cz);
glRotatef(xrot,1,0,0);
glRotatef(yrot,0,1,0);
glRotatef(zrot,0,0,1);
xrot+=xspeed; yrot+=yspeed; zrot+=zspeed;
GLfloat tx,ty,tz;
VERTEX q;
|
接下来我们来绘制模型中的点,如果启用了变形,则计算变形的中间过程点。 |
|
glBegin(GL_POINTS);
for(int i=0;i<morph1.verts;i++)
{
if(morph) q=calculate(i); else q.x=q.y=q.z=0;
helper.points[i].x-=q.x;
helper.points[i].y-=q.y;
helper.points[i].z-=q.z;
tx=helper.points[i].x;
ty=helper.points[i].y;
tz=helper.points[i].z;
|
为了让动画开起来流畅,我们一共绘制了三个中间状态的点。让变形过程从蓝绿色向蓝色下一个状态变化。 |
|
glColor3f(0,1,1);
glVertex3f(tx,ty,tz);
glColor3f(0,0.5f,1);
tx-=2*q.x; ty-=2*q.y; ty-=2*q.y;
glVertex3f(tx,ty,tz);
glColor3f(0,0,1);
tx-=2*q.x; ty-=2*q.y; ty-=2*q.y;
glVertex3f(tx,ty,tz);
}
glEnd();
|
最后如果启用了变形,则增加递增的步骤参数,然后绘制下一个点。 |
|
if(morph && step<=steps)step++; else { morph=FALSE; sour=dest; step=0;}
return TRUE;
}
|
KillGLWindow() 函数基本没有变化,只是添加释放5个模型内存的代码 |
|
objfree(&morph1;);
objfree(&morph2;);
objfree(&morph3;);
objfree(&morph4;);
objfree(&helper;);
|
CreateGLWindow() 函数没有变化 |
|
BOOL CreateGLWindow()
LRESULT CALLBACK WndProc()
|
在WinMain()函数中,我们添加了一些键盘控制的函数 |
|
if(keys[VK_PRIOR])
zspeed+=0.01f;
if(keys[VK_NEXT])
zspeed-=0.01f;
if(keys[VK_DOWN])
xspeed+=0.01f;
if(keys[VK_UP])
xspeed-=0.01f;
if(keys[VK_RIGHT])
yspeed+=0.01f;
if(keys[VK_LEFT])
yspeed-=0.01f;
if (keys['Q'])
cz-=0.01f;
if (keys['Z'])
cz+=0.01f;
if (keys['W'])
cy+=0.01f;
if (keys['S'])
cy-=0.01f;
if (keys['D'])
cx+=0.01f;
if (keys['A'])
cx-=0.01f;
|
1,2,3,4键用来设置变形的目标模型 |
|
if (keys['1'] && (key!=1) && !morph)
{
key=1;
morph=TRUE;
dest=&morph1;
}
if (keys['2'] && (key!=2) && !morph)
{
key=2;
morph=TRUE;
dest=&morph2;
}
if (keys['3'] && (key!=3) && !morph)
{
key=3;
morph=TRUE;
dest=&morph3;
}
if (keys['4'] && (key!=4) && !morph)
{
key=4;
morph=TRUE;
dest=&morph4;
}
|
我希望你能喜欢这个教程,相信你已经学会了变形动画。
Piotr Cieslak 的代码非常的新颖,希望通过这个教程你能知道如何从文件中加载三维模型。
这份教程化了我三天的时间,如果有什么错误请告诉我。
|
版权与使用声明:
我是个对学习和生活充满激情的普通男孩,在网络上我以DancingWind为昵称,我的联系方式是[email protected],如果你有任何问题,都可以联系我。
引子
网络是一个共享的资源,但我在自己的学习生涯中浪费大量的时间去搜索可用的资料,在现实生活中花费了大量的金钱和时间在书店中寻找资料,于是我给自己起了个昵称DancingWind,其意义是想风一样从各个知识的站点中吸取成长的养料。在飘荡了多年之后,我决定把自己收集的资料整理为一个统一的资源库。
版权声明
所有DancingWind发表的内容,大多都来自共享的资源,所以我没有资格把它们据为己有,或声称自己为这些资源作出了一点贡献。故任何人都可以复制,修改,重新发表,甚至以自己的名义发表,我都不会追究,但你在做以上事情的时候必须保证内容的完整性,给后来的人一个完整的教程。最后,任何人不能以这些资料的任何部分,谋取任何形式的报酬。
发展计划
在国外,很多资料都是很多人花费几年的时间慢慢积累起来的。如果任何人有兴趣与别人共享你的知识,我很欢迎你与我联系,但你必须同意我上面的声明。
感谢
感谢我的母亲一直以来对我的支持和在生活上的照顾。
感谢我深爱的女友田芹,一直以来默默的在精神上和生活中对我的支持,她甚至把买衣服的钱都用来给我买书了,她真的是我见过的最好的女孩,希望我能带给她幸福。
源码 RAR格式 |
| <
第24课 |
第26课
> |