《深入了解OpenGL》第二讲顶点线性变换.docx
- 文档编号:27657064
- 上传时间:2023-07-03
- 格式:DOCX
- 页数:16
- 大小:22.09KB
《深入了解OpenGL》第二讲顶点线性变换.docx
《《深入了解OpenGL》第二讲顶点线性变换.docx》由会员分享,可在线阅读,更多相关《《深入了解OpenGL》第二讲顶点线性变换.docx(16页珍藏版)》请在冰豆网上搜索。
《深入了解OpenGL》第二讲顶点线性变换
《深入了解OpenGL》第二讲:
顶点线性变换
时间:
2010-05-1112:
56点击:
2157次
上周发布了《深入了解OpenGL》第一讲:
绘制基本图元,今天CocoaChina会员zenny_chen带来了OpenGL知识第二讲。
帖子地址这一章节可能会比较枯燥,
上周发布了《深入了解OpenGL》第一讲:
绘制基本图元,今天CocoaChina会员“zenny_chen”带来了OpenGL知识第二讲。
帖子地址c:
\"
这一章节可能会比较枯燥,当然示例代码将会非常有趣。
枯燥的是将会有很多线性代数的知识夹杂其中。
在本章中我们将对3D图形进行绘制。
我们会介绍顶点变换的过程,以及法线向量与绘制顺序。
这里将首先介绍一下一个三维空间上的顶点被映射到二维屏幕坐标的步骤,请各位务必牢记:
物体顶点坐标(顶点)=》模型视图变换->视觉坐标=》投影变换->裁剪坐标=》透视划分->规格化设备坐标=》视口变换->窗口坐标
上图中,=》表示经过某种变换;->表示得到某个坐标。
下面我们将根据这个顺序来依次介绍各个变换。
这里将主要介绍模型视图变换和投影变换。
先介绍OpenGL中物体顶点坐标的表示。
OpenGL中,物体的顶点坐标是由一个四维的行向量来表示的,表示为:
(x,y,z,w)。
其中,x,y,z分别对应x轴、y轴和z轴,而w用于辅助做线性变换。
我们会理所当然地认为,三维坐标系只要三个分量就够了,w到底有何用呢?
我们这里首先要说明一下,在OpenGL中,线性变换矩阵的变换都是用矩阵的乘法,没有加减法,因此所有线性变换都是通过矩阵的乘法来完成的。
所以,比如说我们要把一个顶点:
(1,1,1)沿x轴向右平移5个单位,那要用三维变换矩阵表示的话就要是:
M=[6,0,0;0,1,0;0,0,1]。
那么v\'=v*M=(6,1,1)。
但很多场合下使用三维空间做变换将会使计算变得非常复杂,甚至很难进行变换,因此引入四维空间使得变换更加灵活。
比如,顶点v=(1,1,1,1)沿x轴向右平移5个单位,那么变换矩阵M=[1,0,0,0;0,1,0,0;0,0,1,0;5,0,0,1]。
我们将看到这个4x4的变换矩阵与3x3的相比就显得简单很多。
因为对于平移来说,本身就是加法,而三维空间中就需要使用乘法,从而加大了计算负荷。
在OpenGL中,物体的顶点坐标在进行计算时总是以行向量的形式表示,并且计算时总是在最左边。
下面将正式介绍视图各种变换。
先贴代码:
1.-(void)prepareOpenGL
2.{
3. glShadeModel(GL_SMOOTH);
4.
5. glClearColor(0.0,0.0,0.0,0.0);
6. glViewport(0,0,320,320);
7.
8. glMatrixMode(GL_PROJECTION);
9. glLoadIdentity();
10. glOrtho(-1.0,1.0,-1.0,1.0,1.0,5.0);
11.
12. glMatrixMode(GL_MODELVIEW);
13. glLoadIdentity();
14. glTranslatef(0.3f,0.2f,0.0f);
15. glRotatef(-30.0f,0.0f,0.0f,1.0f);
16.
17. glFrontFace(GL_CCW);
18. glEnable(GL_CULL_FACE|GL_DEPTH_TEST);
19.}
20.
21.-(void)drawRect:
(NSRect)dirtyRect{
22.
23. staticstruct
24. {
25. GLubytecolours[4];
26. GLfloatvertices[3];
27.
28. }vertexInfoList[]={
29. {{255,0,0,255},{-0.5f,0.5f,-1.0f}},
30. {{0,255,0,255},{-0.5f,-0.5f,-1.0f}},
31. {{0,0,255,255},{0.5f,0.5f,-2.0f}},
32. {{255,0,255,255},{0.5f,-0.5f,-2.0f}}
33. };
34.
35. //Drawingcodehere.
36. glClear(GL_COLOR_BUFFER_BIT);
37.
38. glInterleavedArrays(GL_C4UB_V3F,0,vertexInfoList);
39.
40. glCullFace(GL_BACK);
41. glDrawArrays(GL_TRIANGLE_STRIP,0,4);
42.
43. glFlush();
44.}
下面介绍视图模型变换。
视图模型变换是将三维物体的顶点做平移、旋转等操作。
我们可以这么理解,我们把摄像机镜头固定好,然后观察物体的移动行为。
在上述代码的第12行,glMatrixMode(GL_MODELVIEW);用于将当前矩阵变换的操作作用到视图模型变换栈上。
glLoadIdentity();作用是将当前的矩阵作为单位矩阵,即:
[1,0,0,0;0,1,0,0;0,0,1,0;0,0,0,1]。
其实际效果就是:
C=I;其中,C表示本矩阵模式的当前矩阵,I表示单位矩阵。
glTranslatef(0.3f,0.2f,0.0f);是平移变换,函数原型为:
voidglTranslated(GLdoublex,
GLdoubley,
GLdoublez);
voidglTranslatef(GLfloatx,
GLfloaty,
GLfloatz);
其中,x表示沿x轴平移多少单位;y表示沿y轴平移多少单位;z表示沿z轴平移多少单位。
而,其对应的平移变换矩阵就是:
[1,0,0,0;0,1,0,0;0,0,1,0;x,y,z,1]。
那么glTranslatef(0.3f,0.2f,0.0f);实际效果就是:
C\'=T*C=T*I。
因为我们刚才调用了glLoadIdentity,因此在调用此函数时的当前矩阵就是I。
而下面的glRotatef(-30.0f,0.0f,0.0f,1.0f);则是将所要绘制的物体的每个顶点坐标绕z轴,顺时针旋转30度。
下面给出glRotatef的原型:
voidglRotated(GLdoubleangle,
GLdoublex,
GLdoubley,
GLdoublez);
voidglRotatef(GLfloatangle,
GLfloatx,
GLfloaty,
GLfloatz);
其中,angle表示角度制的角度;x、y和z表示绕着由x、y和z构成的向量进行旋转。
如果angle大于0,则做逆时针旋转,小于0做顺时针旋转,等于0不动。
由于glRotate所对应的变换矩阵比较复杂,各位可以参考OpenGL官网上的手册。
如果将旋转变换矩阵表示为R的话,那么这里调用R的效果其实就是:
C\'=R*C。
那么将上面所有的串起来就是:
C\'=R*(T*I)。
如果顶点单纯地只经过视图模型变换,那么对于物体其中一个顶点v而言,变换后的顶点v\'=v*(R*(T*I))。
我们这里实际上是先对物体做绕z轴旋转,然后再平移。
但是调用的时候必须先调平移函数,再调旋转函数。
下面在介绍投影变换前再介绍几个对矩阵操作的函数,首先是glLoadMatrix,下面先给出函数原型:
voidglLoadMatrixd(constGLdouble*m);//只能用于OpenGL
voidglLoadMatrixf(constGLfloat*m);
voidglLoadMatrixx(constGLfixed*m);//只能用于OpenGLES
该函数表示在当前变换矩阵模式下将矩阵m取代当前的变换矩阵。
比如,glLoadIdentity可以用一下代码替换:
glLoadMatrixf((constGLfloat[]){
1.0f,0.0f,0.0f,0.0f,
0.0f,1.0f,0.0f,0.0f,
0.0f,0.0f,1.0f,0.0f,
0.0f,0.0f,0.0f,1.0f});
然后是再介绍一下glMultMatrix函数,请参看函数原型:
voidglMultMatrixd(constGLdouble*m);//仅用于OpenGL
voidglMultMatrixf(constGLfloat*m);
voidglMultMatrixx(constGLfixed*m);//仅用于OpenGLES
这个函数就很直白地表示用矩阵m与当前矩阵相乘,然后将结果再给回当前矩阵。
即:
C\'=M*C。
我们可以用一下代码替换glTranslatef:
glMultMatrixf((constGLfloat[]){
1.0f,0.0f,0.0f,0.0f,
0.0f,1.0f,0.0f,0.0f,
0.0f,0.0f,1.0f,0.0f,
0.3f,0.2f,0.0f,1.0f});
将上述代码替换掉3楼的第14行,效果不变。
下面介绍投影变换。
投影变换就好像为照相机选择镜头。
我们可以认为这种变换的目的就是确定视野(或视景体)。
并且确定哪些物体位于视野之内以及它们能够被看到的程度。
投影变换主要有两种,一种是透视投影(PerspectiveProjection)。
这种投影效果就像是玩第一人称游戏或是第一人称的赛车类游戏。
你向前看具有透视效果,远处的物体显得小,近处的物体显得大。
还有一种投影是正交投影(OrthographicProjection)。
这种投影是将顶点以平行于视野的角度垂直地映射到屏幕上。
物体不管离你多远,其大小都是一样的。
透视投影对应的函数为:
glFrustum,函数原型如下:
voidglFrustum(GLdoubleleft,
GLdoubleright,
GLdoublebottom,
GLdoubletop,
GLdoublenearVal,
GLdoublefarVal);//OpenGL
voidglFrustumf(GLfloatleft,GLfloatright,GLfloatbottom,GLfloattop,GLfloatnear,GLfloatfar);//OpenGLES voidglFrustumx(GLfixedleft,GLfixedright,GLfixedbottom,GLfixedtop,GLfixednear,GLfixedfar);//OpenGLES
其中,left表示x轴坐标最左端的值,right表示x坐标最右端的值,bottom表示y轴坐标最下端的值,top表示y轴最上端的值,near表示与摄像机或你的眼镜最近的距离,far表示摄像机或离你的眼镜最远的距离。
near和far都大于0方有效。
而正交投影的定义与透视投影的类似,下面给出函数原型:
voidglOrtho(GLdoubleleft,
GLdoubleright,
GLdoublebottom,
GLdoubletop,
GLdoublenearVal,
GLdoublefarVal);//OpenGL
voidglOrthof(GLfloatleft,
GLfloatright,
GLfloatbottom,
GLfloattop,
GLfloatnear,
GLfloatfar);//OpenGLES
voidglOrthox(GLfixedleft,
GLfixedright,
GLfixedbottom,
GLfixedtop,
GLfixednear,
GLfixedfar);//OpenGLES
首先,各位可以先用glFrustum来替换glOrtho,看看效果,呵呵。
然后我们看3楼代码29到32行,为什么这里z坐标为-1和-2?
因为我们调用了glOrtho(-1.0,1.0,-1.0,1.0,1.0,5.0);也就是说你的视野最近看到的距离为1,最远是5。
由于你的视野是正对屏幕的,也就是朝z轴的负方向。
因此这里必须把物体沿z轴移到你的视野范围内才能看到物体。
如果我们把物体顶点的z轴值改为大于-1或小于-5,那偶们就看不见该顶点了,呵呵。
好。
那么我们现在结合模型视图变换和投影变换。
那么它们的调用顺序是何如的呢?
我们在1楼知道,视图的变换步骤是先经过视图模型变换,然后再经过投影变换。
因此对于一个顶点v来说,若视图模型变换矩阵是M,而投影变换矩阵为P,那么变换后的顶点v\'=v*(M*P)。
我们不妨可以做个实验。
为了能证明这个步骤,我们将投影变换改为透视投影,即,将glOrtho改为glFrustum,观察图形。
然后我们做以下修改:
复制代码
1.-(void)prepareOpenGL
2.{
3. glShadeModel(GL_SMOOTH);
4.
5. glClearColor(0.0,0.0,0.0,0.0);
6. glViewport(0,0,320,320);
7.
8. glMatrixMode(GL_PROJECTION);
9. glLoadIdentity();
10. //glFrustum(-1.0,1.0,-1.0,1.0,1.0,5.0);
11.
12. glMatrixMode(GL_MODELVIEW);
13. glLoadIdentity();
14.
15. glMultMatrixf((constGLfloat[]){
16. 1.0f,0.0f,0.0f,0.0f,
17. 0.0f,1.0f,0.0f,0.0f,
18. 0.0f,0.0f,-1.5f,-1.0f,
19. 0.0f,0.0f,-2.5f,0.0f
20. });
21.
22. glTranslatef(0.3f,0.2f,0.0f);
23. glRotatef(-30.0f,0.0f,0.0f,1.0f);
24.
25. glFrontFace(GL_CCW);
26. glEnable(GL_CULL_FACE|GL_DEPTH_TEST);
27.}
28.
29.-(void)drawRect:
(NSRect)dirtyRect{
30.
31. staticstruct
32. {
33. GLubytecolours[4];
34. GLfloatvertices[3];
35.
36. }vertexInfoList[]={
37. {{255,0,0,255},{-0.5f,0.5f,-1.0f}},
38. {{0,255,0,255},{-0.5f,-0.5f,-1.0f}},
39. {{0,0,255,255},{0.5f,0.5f,-2.0f}},
40. {{255,0,255,255},{0.5f,-0.5f,-2.0f}}
41. };
42.
43. //Drawingcodehere.
44. glClear(GL_COLOR_BUFFER_BIT);
45.
46. glInterleavedArrays(GL_C4UB_V3F,0,vertexInfoList);
47.
48. glCullFace(GL_BACK);
49. glDrawArrays(GL_TRIANGLE_STRIP,0,4);
50.
51. glFlush();
52.}
我们会发现这个形状与调用glFrustum的形状完全一样,看上去像个直角梯形。
而如果我们将上面15到20行的代码搬到第24行,那么我们将会看到一个接近于等腰梯形的图形,显然与原来的不符,呵呵。
而上述代码中:
glMultMatrixf((constGLfloat[]){
1.0f,0.0f,0.0f,0.0f,
0.0f,1.0f,0.0f,0.0f,
0.0f,0.0f,-1.5f,-1.0f,
0.0f,0.0f,-2.5f,0.0f
});
其实就等价于调用:
glFrustum(-1.0,1.0,-1.0,1.0,1.0,5.0);
上面那个是与下面glFrustum调用相对应的变换矩阵。
好,接下去我们准备开始话3D图形。
上面忘记说了,投影矩阵与模型视图矩阵是分开来的。
也就是说glMatrixMode(GL_PROJECTION);所指定指定的投影变换矩阵栈与glMatrixMode(GL_MODELVIEW);所指定的模型视图变换矩阵栈是相互独立的。
而投影变换矩阵变换的最终结果我们设为P,而模型视图变换矩阵的最终得出的变换矩阵我们设为M。
所以上述公式中的P和M是这么得到的。
我们在后序章节中将会介绍对矩阵栈的操作。
好。
我们下面将谈谈面的朝向。
这个对于裁剪背面以及光照都有用。
我们知道一张纸有正反两个面。
在OpenGL中,一个平面可以想像为一张纸,也有两个面。
如果你没有指定要裁剪掉看不见的反面,那么反面也会被绘制。
那么我们如何确定一个面是朝向哪里呢?
这取决于我们的初始设定以及绘制顶点的顺序。
在默认情况下,顶点以逆时针方向绘制出来的方向为正面,顺时针方向绘制出来的是反面。
我们也可以用右手拇指定律来确定面的朝向。
函数glFrontFace用于指定是顺时针方向为正面还是逆时针方向为正面。
默认情况下,逆时针方向为正面。
函数原型如下:
voidglFrontFace(GLenummode);
mode只有两个值,要么是GL_CW,指定绘制顺序为顺时针时表示正面;要么是GL_CCW,指定绘制顺序为逆时针方向时为正面。
默认为GL_CCW。
下面我们将举个绘制立方体的例子。
1.-(void)prepareOpenGL
2.{
3. glShadeModel(GL_SMOOTH);
4.
5. glEnable(GL_CULL_FACE);
6. glEnable(GL_DEPTH_TEST);
7. glEnable(GL_NORMALIZE);
8.
9. glClearColor(0.4,0.4,0.4,1.0);
10. glViewport(0,0,320,320);
11.
12. glMatrixMode(GL_PROJECTION);
13. glLoadIdentity();
14. glOrtho(-1.0,1.0,-1.0,1.0,1.0,5.0);
15.
16. glMatrixMode(GL_MODELVIEW);
17. glLoadIdentity();
18.
19. glTranslatef(0.0f,0.0f,-3.0f);
20. glRotatef(10.0f,1.0f,1.0f,1.0f);
21.
22. glFrontFace(GL_CCW);
23.}
24.
25.-(void)drawRect:
(NSRect)dirtyRect{
26.
27. staticconststruct
28. {
29. GLubytecolours[4];
30. GLfloatvertices[3];
31.
32. }vertexInfoList[]={
33. {{255,0,0,255},{-0.5f,-0.5f,-0.5f}}, //v0:
left-bottom-back
34. {{0,255,0,255},{0.5f,-0.5f,-0.5f}}, //v1:
right-bottom-back
35. {{0,0,255,255},{0.5f,0.5f,-0.5f}}, //v2:
right-top-back
36. {{255,0,255,255},{-0.5f,0.5f,-0.5f}}, //v3:
left-top-back
37. {{255,255,0,255},{-0.5f,-0.5f,0.5f}}, //v4:
left-bottom-front
38. {{0,255,255,255},{0.5f,-0.5f,0.5f}}, //v5:
right-bott
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 深入了解OpenGL 深入 了解 OpenGL 第二 顶点 线性 变换