《OpelGL编程指南第七版》第三章:视图(笔记)

《视图》

标签(空格分隔):OpenGL编程指南第七版


把一个物体的三维坐标变换为屏幕的像素坐标需要完成如下3个步骤:

image_1c3771vdouifkoq1smh1m8n6ot9.png-135.2kB

3.1简介

image_1c377arlo1tmqc8v1m9r1jab1svs1m.png-177.4kB
注意,除了顶点之外,视图和模型变换还会自动作用于表面法线向量(法线向量只作用于视觉坐标 eye coordinate)。这保证了法线向量和顶点数据之间具有正确的对应关系。
image_1c377i78v1rvvf0k1k8csgtbvp23.png-69.4kB
image_1c377jgd08ts1s6u10t5dso15s73g.png-52.2kB
image_1c377ksdg16g7n5kdtomkh17t23t.png-83.1kB

  • 视图变换:固定照相机的位置,将它对准场景
  • 模型变换:设置模型的位置和方向。
    补充: display()的潜在的重复使用性进一步强调了执行视图变换和模型变换之前加载单位矩阵的重要性,特别是在两次display()调用之间还插入了其他变换的情况下。
  • 投影变换:可以认为这种变换的目的是确定视野,由此确定哪些物体位于视野内以及它们能被看到的程度。除了考虑视野外,投影变换还决定了物体是如何投影到屏幕上的。
    • 1)透视投影(perspective projection) :符合现实,近大远小
    • 2)正投影(orthographic projection):直接投射到屏幕上,不改变物体的大小,用于反应物体间的相对大小。
  • 视口变换:视口指定了场景在屏幕上所占据的区域,因此可以把视口变换看成是定义了最终经过处理的照片的大小和位置

3.1.2变换通用函数

void glMatrixMode(GLenum mode);
image_1c39h253h1tuj19971p7fb4fvjd4a.png-123.4kB

void glLoadIdentity(void);
image_1c39h40jr1juc15kccaj38lqko57.png-21.7kB

void glLoadMatrix{fd}(const TYPE* m);
image_1c39h8ba8vu315hlsip1pvu12875k.png-23kB

void glMultMatrix{fd}(const TYPE* m);
image_1c39h8q15qgatjlbi1qsiqse61.png-35.7kB

void glLoadTransposeMatrix{fd}(const TYPE* m);
image_1c39hks4l13vh1kgj1p1h1b6o1mg49.png-56.6kB

void glMultTransposeMatrix{fd}(const TYPE* m);
image_1c39hla6f1tbufb85hqueh10vbm.png-58.8kB

3.2视图和模型变换

3.2.1对变换的思考

注意:
OpenGL中矩阵默认为列优先。C/C++中默认是行优先的。也就是说当年你构建一个二维数组时,在内存中是一行一行存储的,顶部的行在更低的地址区。矩阵相乘时,行优先采用我们数学中正确的方式,即一行和一列对应相乘再求和 Cij = ∑ Ain * Bnj。而列优先中计算为一列与一列对应相乘再求和Cij = ∑ Ani * Bnj
比如一个点(x,y,z,w)先平移变换T:(x1,y1,z1),再按Z轴旋转R:θ,则c/c++中变换矩阵存储为

100001000010x1y1z11 

cosθsinθ00sinθcosθ0000100001 

而OpenGL中存储为

100x1010y1001z10001 

cosθsinθ00sinθcosθ0000100001 

在C/C++中,变换过程为:每行乘每列

M= 100001000010x1y1z11×cosθsinθ00sinθcosθ0000100001=cosθsinθ00sinθcosθ000010x1y1z11

X=MX= cosθsinθ00sinθcosθ000010x1y1z11×xyz1=xcosθysinθ+x1xsinθ+yycosθ+y1z+z11

在OpenGL中,变换过程为:每列乘每列

M= cosθsinθ00sinθcosθ0000100001×100x1010y1001z10001=cosθsinθ0x1sinθcosθ0y1001z10001

X=MX= xyz1×cosθsinθ0x1sinθcosθ0y1001z10001=xcosθysinθ+x1xsinθ+ycosθ+y1z+z11

因此,假设当前矩阵是C,一系列变换函数指定的矩阵是M1,M2……,Mn,在相乘后,最终的矩阵形式总是C×M1×M2……×Mn,作用到点v上的效果是C×M1×M2……×Mn×v(C/C++ 行×列)。等同于v×Mn×Mn-1……×c(OpenGL 列×列)。这个过程意味着程序调用的最后一个变换函数实际上是首先应用于顶点v的

考虑两个简单变换
image_1c39pov75mki1u484qqc8co3q13.png-161.1kB
全局固定坐标系
如果根据全局固定坐标系来考虑问题,就必须注意乘法的出现顺序与它们在代码中出现的顺序相反。
image_1c39q1d1k1fdi1d60nlfaqilo20.png-115.9kB

局部移动坐标系统
所有的操作都相对于这个不断变化的坐标系统进行,矩阵乘法很自然地按照它们在代码中出现的顺序进行。(仍然是3-4左侧的变换。代码不用改,思维方式变了一下)

现在我们再回顾一下,整理下思路。我们想实现3-4左侧的效果,先旋转再移动。如果我们代码变换矩阵顺序也是先旋转再移动的话,OpenGL实现时会是先移动再旋转,最后效果是3-4右侧。我们想正确实现变换后物体在X轴上,必须调整代码顺序,即变化矩阵顺序为先移动再旋转。这时再用全局坐标系思考问题要先将变换矩阵顺序倒一下,然后在根据全局的坐标系变化,这样思考很麻烦。所以我们采用局部坐标系思考,先移动,再按照局部坐标系的Z轴旋转,这样矩阵乘法很自然地按照代码中的顺序进行。方便我们思考。所以总结一下就是:
OpenGL每次变换实现的都是全局坐标系的变换,但是当我们在代码中必须将变换矩阵的顺序反过来,这时按语句顺序来理解的话,实现的是局部变换的效果。

3.2.2模型变换

void glTranslate{fd}(TYPE x, TYPE y, TYPE z);
void glRoate{fd}(TYPE angle, TYPE x, TYPE y, TYPE z);
void glScale{fd}(TYPE x, TYPE y, TYPE z);

3.2.3视图变换

视图变换函数必须在条用任何模型变换函数之前调用,以确保首先作用于物体的是模型变换。
void glLookAt(GLdouble eyex, GLdouble eyey, GLdouble eyez, GLdouble centerx, GLdouble centery, GLdouble centerz, GLdouble upx, GLdouble upy, GLdouble upz);
image_1c3a4jqfq13cgt4p5lg1vtj7cl4a.png-107.4kB

3.3投影变换

3.3.1透视投影

void glFrustum(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far);
image_1c3a5f4fi1qnobjkd7b2u29vb6k.png-115.6kB
image_1c3a5jsig1ji51vtr10s61d2t4h571.png-153.4kB

void glPerspective(GLdouble fovy, GLdouble aspect, GLdouble near, GLdouble far);
image_1c3a5mh9o1fk517ef17ar1s7h1nq58b.png-113.3kB
微信截图_20180108140202.png-155.7kB

3.3.2正投影

void glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far);
image_1c3a5tg4h1fbi1cir1e9n5p1a93bk.png-125.1kB
image_1c3a5tuth1pm61cgugmevvivmac1.png-155.2kB

3.4视口变换

3.4.1定义视口

void glViewport(GLint x, GLint y, GLsizei width, GLsizei height);
image_1c3a6vhlt8ggas01eiu6m91at4cu.png-85.8kB

3.4.2变换深度坐标

void glDepthRange(GLclampd near, GLclampd far);
微信截图_20180108142643.png-63.8kB
image_1c3a7atr2glcdftsoekmuroag7.png-163.6kB

3.6操纵矩阵堆栈

void glPushMatrix(void);
void glPopMatrix(void);
image_1c3a82sf8f7m1poambvrh11dpqj4.png-152.4kB

3.6.1模型视图矩阵堆栈

3.6.2投影矩阵堆栈

image_1c3a8qs251s7bj9r14t21k6r1i5njh.png-274.6kB

3.7其他裁剪平面

void glClipPlane(GLenum plane, const GLdouble* equation);定义一个裁剪平面
image_1c3fa8tl9snrrir1cp915j7gfp26.png-40kB
image_1c3fa8ketce31pg21d7pnli1l5j1p.png-85.5kB
glEnable(GL_CLIP_PLANEi);
glDisable(GL_CLIP_PLANEi);
所有的OpenGL都必须支持至少6个其他裁剪平面

3.9逆变换和模拟变换

int gluUnProject(GLdouble winx, GLdouble winy, GLdouble winz, const GLdouble modelMatrix[16], const GLdouble projMatrix[16], const GLdouble viewport[4], GLdouble* objx, GLdouble* objy, GLdouble* objz);

image_1c3fbesc31urn33j44p1upa1coa2j.png-164.4kB

获取模型、投影和视图矩阵
glGetIntegerv(GL_VIEWPORT,viewport);
glGetDouble(GL_MODELVIEW_MATRIX,modelMatrix);
glGetDouble(GL_PROJECTION_MATRIX,projMatrix);
注意:
OpenGL中视口坐标原点在左下角,而Windows窗口的左边原点在左上角。realy = viewport[3] - (GLint) y - 1。viewport第四个参数为窗口的高度,y为鼠标点击获得的y值

gluUnProject4可以处理非标准的glDepthRange()值,也可以处理w坐标不等于1的情况。
int gluUnProject4(
GLdouble winx, GLdouble winy, GLdouble winz, GLdouble clipw,
const GLdouble modelMatrix[16],
const GLdouble projMatrix[16],
const GLdouble viewport[4],
GLclampd zNear, GLclampd zFar,
GLdouble* objx, GLdouble* objy, GLdouble* objz, GLdouble* objw);
image_1c3fdlp059paljd1usr1n6duds9.png-66.7kB

gluProject与gluUnProject相对应。只要提供三维物体坐标以及对它们产生影响的所有变换,gluProject就会返回经过变换的窗口坐标。
image_1c3fdsk16jc51nup1o7u19971nl7m.png-122.3kB

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值