Win32 OpenGL编程(8) 3D模型变换及其组合应用
write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie
提要
在前文(系列文章(7),以下简称XO7,系列其他文章类似)中的照相机比喻中提到了4种3D变换,如下:
1.确定照相机的位置的过程对应于“视图变换”(Viewing Transformations)
2.确定物体位置的过程对应于“模型变换”(Modeling Transformations)
3.确定照相机放大倍数的过程对应于“投影变换”(Projection Transformations)
4.确定照片大小的过程对应于“视口变换”(Viewport Transformations)
XO7中我们讲的是第一种变换视图变换,即改变观察者本身的位置,视角等的变换效果,本文开始继续按顺序讲解下一个3D变换过程,模型变换。
模型变换
模型变换指的是以设置模型(即我们观察的物体)的位置,方向为目的的变换过程,还在2D时,XO4一文的内容就已经涉及模型变换的知识了,(我还提供了一个2D中使用glTranslate*和glRotate*的较复杂例子,见《 Win32 OpenGL编程系列 2D例子 -- 七巧板图形绘制 》)只不过那时候我们纯粹是在2D空间中思考这个问题,并且描述如移动,旋转等变换时提及的对象是图元,其实那就是3D的模型变换在2D世界的一个简化版而已,我们可以将以前的知识在3D空间扩展开来,各种变换的效果和意义还是类似的。glTranslate*,glRotate*的使用方式与2D是类似的,仅仅是多考虑一个Z轴的值而已,比如,glTranslate*函数就可能将模型移出原来的x-y平面(即Z=0的平面),而glRotate*的旋转也可能使模型不再在原x-y平面上,对变换角度的计算时,不要漏了就可以了。
其实当时讲2D时还有个模型变换函数没有讲到,那就是改变模型大小的glScale*函数。
glScale — multiply the current matrix by a general scaling matrix
C Specification
void glScaled( GLdouble x,
GLdouble y,
GLdouble z);
void glScalef( GLfloat x,
GLfloat y,
GLfloat z);
Parametersx, y, z
Specify scale factors along the x, y, and z axes, respectively.
此函数的使用起来很简单,我用一个动画效果来演示此函数。此例是从XO7中的glPolygonFace例子改过来的,已经处理好了正面,背面和视角,我仅仅添加了几句glScale*函数的调用
//这里进行所有的绘图工作
void SceneShow(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 0.0, 0.0);
static GLfloat fScaleY = 1.0;
static GLboolean bScaleWay = true;
if (bScaleWay)
{
if (fScaleY <= 1.5)
{
fScaleY += 0.01;
}
else
{
bScaleWay = false;
}
}
else
{
if (fScaleY >= 0.5)
{
fScaleY -= 0.01;
}
else
{
bScaleWay = true;
}
}
glPushMatrix();
glScalef(1.0, fScaleY, 1.0);
DrawSmoothColorPyramid(0.5);
glPopMatrix();
glLoadIdentity();
gluLookAt(gViewPosX, gViewPosY, gViewPosZ, gViewDirX, gViewDirY, gViewDirZ, gViewUpDirX, gViewUpDirY, gViewUpDirZ);
glFlush();
}
此时模型的高度在0.5与1.5倍之间来回变换,加上以前的框架,你可以变换各种角度来查看。glScale*的使用在此例中就很明显了,x,y,z值分别对应原模型的变换倍数,1.0即不变,1.5即增长一半,以此类推。
为节省篇幅仅贴出关键片段,完整源代码见我博客源代码的2009-10-26/glScaleSample 目录,获取方式见文章最后关于获取博客完整源代码的说明。
模型变换的组合
以上3个模型变换可以一起组合使用,实现复杂的效果,七巧板一例中就是完全靠glTranslate*河glRotate*来为各块图形定位和控制旋转的。
原七巧板中的CShape绘制图形时使用的代码如下:
void Draw()
{
glLoadIdentity();
glColor4fv(mfvColor);
glPushMatrix();
glTranslatef(mfPosX, mfPosY, 0.0);
glRotatef(mfDegree, 0.0, 0.0, 1.0);
DrawImp();
glPopMatrix();
}
现在看起来很简单,无非就是先将一块七巧板通过glTranslatef移动到某个确定的X,Y位置,然后通过glRotatef旋转,那么,我们能不能先旋转好了角度,然后再移动到确定的位置去呢?从理论上来说这条路和以前应该一样嘛。比如下列代码,仅仅更改了glTranslatef和glRotatef的先后顺序,效果会一样吗?
void Draw()
{
glLoadIdentity();
glColor4fv(mfvColor);
glPushMatrix();
glRotatef(mfDegree, 0.0, 0.0, 1.0);
glTranslatef(mfPosX, mfPosY, 0.0);
DrawImp();
glPopMatrix();
}
但是最后的效果与我们所想的并不一样。
原因嘛,需要好好解释一下,正如我提及模型变换常常说是坐标系的变换,因为我喜欢从这个角度来看模型变换,模型变换改变的不是模型本身,改变的是模型所处的坐标系,此时,移动,改变的仅仅是坐标系的原点,转动的话,坐标系的方向都变了,缩放的话,坐标系的单位长度变化了。这样考虑的话就知道为什么上述例子中两个函数的调用顺序改变会导致结果改变了,先转动时,坐标系已经转动了,而我们移动的时候,数据还是相对于原来OpenGL默认的坐标系的,自然就不对了,为了更形象的看到结果,这里我给出一个同时显示三个不同处理三角锥的例子:
//这里进行所有的绘图工作
void SceneShow(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 0.0, 0.0);
// 原三角锥
DrawSmoothColorPyramid(0.5);
// 先转动后移动的三角锥
glPushMatrix();
glRotatef(90, 0.0, 0.0, 1.0);
glTranslatef(0.0, 0.5, 0.0);
DrawSmoothColorPyramid(0.5);
glPopMatrix();
// 先移动后转动的三角锥
glPushMatrix();
glTranslatef(0.0, -0.5, 0.0);
glRotatef(90, 0.0, 0.0, 1.0);
DrawSmoothColorPyramid(0.5);
glPopMatrix();
glLoadIdentity();
gluLookAt(gViewPosX, gViewPosY, gViewPosZ, gViewDirX, gViewDirY, gViewDirZ, gViewUpDirX, gViewUpDirY, gViewUpDirZ);
glFlush();
}
运行效果如下图所示:
例子还是在以前三角锥绘制代码上改动而来,运行效果如下图,中间的三角锥即是没有经过任何变换的原始三角锥。左边的那个三角锥是先转动后移动产生的三角锥,理解一下是怎么通过上述操作得出来的,首先,逆时针转动90度,此时此三角锥的局部坐标的Y正轴已经是指向屏幕的正左边了,所以,此时通过glTranslate*将其向Y正方向移动0.5,导致其移动到了屏幕的左边。下边那个三角锥即是先移动,后转动产生的,由于移动操作仅影响坐标轴的原点位置,不改变方向和单位长度,所以很好理解。主要注意那个先旋转后移动的三角锥,移动的方向已经不是屏幕的全局坐标系了。
为节省篇幅仅贴出关键片段,完整源代码见我博客源代码的2009-10-26/glComposModelTrans 目录,获取方式见文章最后关于获取博客完整源代码的说明。
glScale*与glRotate*类似,先调用的话也会对坐标系产生影响,导致以后的操作都会按照影响的结果而改变,比如,先将坐标系放大1倍,那么以前移动0.5的操作将会变成移动1,这一点由于和glRotate*组合使用的效果一致,见下面的例子:
//这里进行所有的绘图工作
void SceneShow(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 0.0, 0.0);
// 原三角锥
DrawSmoothColorPyramid(0.5);
// 先缩放,后移动
glPushMatrix();
glScalef(1.5, 1.5, 1.5);
glTranslatef(0.0, 0.3, 0.0);
DrawSmoothColorPyramid(0.5);
glPopMatrix();
// 先移动,后缩放
glPushMatrix();
glTranslatef(0.0, 0.3, 0.0);
glScalef(1.5, 1.5, 1.5);
DrawSmoothColorPyramid(0.5);
glPopMatrix();
glLoadIdentity();
gluLookAt(gViewPosX, gViewPosY, gViewPosZ, gViewDirX, gViewDirY, gViewDirZ, gViewUpDirX, gViewUpDirY, gViewUpDirZ);
glFlush();
}
作为比较对象,留下来的原三角锥在最下面(原中间位置),中间的那个大三角锥是按照默认大小坐标系上移了0.3距离,而上面大三角锥在程序中也是上移0.3距离,但是此距离因为首先经过了glScale*放大,所以可以看出实际效果上移动的要比默认的0.3要远,所以导致图像无法重合。
为节省篇幅仅贴出关键片段,完整源代码见我博客源代码的2009-10-26/glComposModelTrans2 目录,获取方式见文章最后关于获取博客完整源代码的说明。
最后,作为额外的例子,展示几个相关有趣的图以及代码,因为不牵涉到新知识,不做过多的解释。
有趣的组合效果,一个放大了1倍的三角锥和一个移动到上面的三角锥组合而成。
void SceneShow(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 0.0, 0.0);
glPushMatrix();
glScalef(2.0, 2.0, 2.0);
DrawSmoothColorPyramid(0.5);
glPopMatrix();
glPushMatrix();
glTranslatef(0.0, 0.5, 0.0);
DrawSmoothColorPyramid(0.5);
glPopMatrix();
glLoadIdentity();
gluLookAt(gViewPosX, gViewPosY, gViewPosZ, gViewDirX, gViewDirY, gViewDirZ, gViewUpDirX, gViewUpDirY, gViewUpDirZ);
glFlush();
}
参考资料
1. 《 OpenGL Reference Manual 》,OpenGL参考手册
2. 《OpenGL 编程指南》(《 OpenGL Programming Guide 》),Dave Shreiner,Mason Woo,Jackie Neider,Tom Davis 著,徐波译,机械工业出版社
3. 《Nehe OpenGL Tutorials》,Nehe著,在 http://nehe.gamedev.net/ 上可以找到教程及相关的代码下载,(有PDF版本教程下载)Nehe自己还做了一个面向对象的框架,作为演示程序来说,这样的框架非常合适。也有 中文版 ,各取所需吧。
4. 《OpenGL入门学习》 ,eastcowboy著,这是我在网上找到的一个比较好的教程,较为完善,而且非常通俗。这是第一篇的地址: http://bbs.pfan.cn/post-184355.html
本OpenGL系列其他文章
1. 《 Win32 OpenGL 编程(1)Win32下的OpenGL编程必须步骤 》
2. 《 Win32 OpenGL编程(2) 寻找缺失的OpenGL函数 》
3. 《 Win32 OpenGL编程(3) 基本图元(点,直线,多边形)的绘制 》
4. 《 Win32 OpenGL编程(4) 2D图形基础(颜色及坐标体系进阶知识) 》
5. 《 Win32 OpenGL编程(5)顶点数组详细介绍 》
6.《 Win32 OpenGL编程(6) 踏入3D世界 》
7.《 Win32 OpenGL编程(7) 3D视图变换——真3D的关键 》
应用举例:《 Win32 OpenGL编程系列 2D例子 -- 七巧板图形绘制 》
完整源代码获取说明
由于篇幅限制,本文一般仅贴出代码的主要关心的部分,代码带工程(或者makefile)完整版(如果有的话)都能用Mercurial在Google Code中下载。文章以博文发表的日期分目录存放,请直接使用Mercurial克隆下库:
https://blog-sample-code.jtianling.googlecode.com/hg/
Mercurial使用方法见《 分布式的,新一代版本控制系统Mercurial的介绍及简要入门 》
要是仅仅想浏览全部代码也可以直接到google code上去看,在下面的地址:
http://code.google.com/p/jtianling/source/browse?repo=blog-sample-code
原创文章作者保留版权 转载请注明原作者 并给出链接
write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie