opengl从画三角形到画一个立方体(三)

上一节中,我们提到了好多的空间,那么本节重点讲述的是透视空间,之前的博文中也讲到透视矩阵的推导、视图矩阵的推导,这些都讲过,但是每次涉及到坐标的变换,这些又是那么的重要,所以经常的捡起来回忆回忆还是很有意思的,每当你用一行代码搞定坐标转换的时候,如果心里感到阵阵的轻松,仿佛里面的细节你都一清二楚,那么这个时候,你才真正的掌握坐标转换。
下面就是参考的网址中http://www.songho.ca/opengl/gl_projectionmatrix.html
透视矩阵的的推导,其实之前的博文中看到的是另外一个网址的推导过程,本次是参考上面的网址,那么还有很多的书籍,我们之后也会一一给出,但大体的推导步骤都是一样的,只不过本人有个懒惰的习惯,先看看所有的其他人是怎么推导的,再总结一套简单通俗而易懂的理论给大家,这也算是知所有人,而成自己一家之言的习惯了。

上面的网址中,上来解答了我的一个疑惑,当然这也需要读者了解mvp的每个步骤之后得到的是什么空间的坐标,才能懂得这个疑惑。
文中说透视矩阵是用来做透视变换的,那为什么又要做透视变换呢?因为3D的场景必须映射到2D的空间上,因为我们的显示器是2D的呀,综上,由于我们的屏幕是2D的,所以我们要将现实中的物体映射到2D的上才能展示出来。那么怎么转换呢?这就是透视矩阵的作用了。
我们知道MVP中,M矩阵之后得到的世界空间的坐标;V之后就得到了视图空间中的坐标了。那么P之后,得到是什么,是什么?答案是NDC空间中的坐标。
我们再次说明一下,NDC的N是normalized device coordinate. 即是规格化的设备空间坐标。所谓的规格化,就是一个x/y/z是-1到1,或者0到1,这种空间中的坐标,这个根设备有关系。反正就是最多是-1到1空间中的坐标。

接下来在说一下,裁剪区域,我们知道人类的眼睛也是有一个看到范围的限制的,那么在opengl中也是有一个区域是来作此限制的,这个区域叫做视锥体,当然正交投影后面再说。所谓的视锥体,是一个削掉尖头的椎体,我们叫做平截头体,如下图所示:
这里写图片描述

我们看到上面的三角形,以及额外添加的两条红线,那么这个梯形就是所谓的平截头体了。图中还告诉我们短红线是近平面、长红线是远平面、在平截头体外的区域都是不能被看到的。所以到这里你可以清楚的知道了,这个平截头体就是裁剪区域了。

文中还告诉我们,什么是被裁剪,也就是什么时候将超过区域外的点丢弃掉。在做透视除法之前,如果我们发现有的坐标是超过了-Wc到Wc之外的点都丢弃。

那么这里的透视除法又是什么鬼。Wc又是什么玩意。。。

总的来说,我们是把平截头体中的点映射到一个标准的立方体盒子中去。如下图所示:
这里写图片描述
那么如何把平截头体中的点映射到立方体中去呢?首先要把平截头体中的任意一个点映射到平截头体的近平面上去。

这里要注意一下上面两张图中的坐标系。左图是右手坐标系,而右图是左手坐标系。所以平截头体的近平面是-n到-f。
我们画一个自己的图,我觉得非常的清除。
这里写图片描述
上图中两个绿色的三角形是相似的。下面我们就来看看平截头体中的点(x,y,z)映射到近平面上之后的坐标是多少。
已知道OC’ = -n;OC = z;
三角形OB’C’相似于三角形OBC
OC’/OC = B’C’/BC,这里的BC就是点A的x轴坐标,即时x
-n/z = B’C’/x
这样就得到:B’C’ = -nx/z
同样的三角形OB’A’相似于三角形OBA
A’B’/AB = OB’/OB = OC’/OC
AB即是A点y轴。
所以:
A’B’/y = -n/z
所以A’B’ = -ny/z
所以有:
x’ = -nx/z
y’ = -ny/z
z’ = -n
也就是说,平截头体中的任意一点,最终通过相似三角形,映射到了近平面上的点即为(x’,y’,z’)。其中z’始终为-n,这个毫无悬念。然后此时得到的点的坐标叫什么呢?到这里我们会遇到这样的问题,裁剪空间、NDC空间、还有什么CVV空间。
首先给个全称:
CVV——Canonical View Volume, 规则观察体。
NDC——normalized device coordinates,规范化设备坐标。
Clip Space——裁剪空间。
走访各个博客,最终还是区分出这几个术语的不同。
CVV和NDC:规则观察体,无论是正交投影还是透视投影,最终我们都会将立方体(正交投影)或平截头体(透视投影)中的点,映射到一个立方体内(这个立方体的的x从-1到1,y从-1到1,而z可能是-1到1,也可以是0到1)。这个CVV也就是NDC空间。我们经常混淆的是CVV中的C,它不是clip的意思,而是canonical的意思。
Clip Space:并不是NDC空间,我们可以参考网址:
http://www.scratchapixel.com/lessons/3d-basic-rendering/perspective-and-orthographic-projection-matrix/projection-matrix-GPU-rendering-pipeline-clipping
中的这张图:
这里写图片描述
具体的还是要读者自己去看看这个网址中的教程,讲得非常好。在将摄像机坐标系中的点通过透视矩阵变换之后得到的点到底是什么?搞清楚这个就可以明确裁剪空间和NDC空间的区别了。答案是,得到的点是没有经过透视除法的点的,而是一个齐次坐标坐标空间的点,那么裁剪空间又是什么鬼呢?由图可见我们将得到的点,-Wc和Wc进行比较如果不在这个范围之内的就直接去除掉了,所以这就是裁剪。而不是等到透视除法(除以Wc)才去判断点的范围是否则-1到1。说到这里,我们就知道了裁剪空间是在NDC空间之前的一个空间,而且NDC空间是在透视除法之后得到的空间。NDC空间又是一个标准的立方体(x从-1到1,y从-1到1,z从-1到1,或者z从0到1)。标准的立方体又叫什么呢?对的,Canonical View Volume。
接着,我们这样问一个问题,为什么要映射到一个CVV立方体呢?或者NDC空间呢?答案是为了方便裁剪。而裁剪并不是一定要在CVV或者NDC判断,上面的介绍说过了,可以在透视矩阵作用后,不做透视除法就可以进行裁剪了。但总的来说,我们的点从平截头体中的映射到立方体呢?(透视矩阵之后,虽然还没有透视除法,但是依然是一个立方体),而立方体是很容易进行裁剪的,裁剪的线的算法可以参考:Cohen-Sutherland algorithm而裁剪多边形的算法可以参考Sutherland-Hodgman algorithm这两个算法以后我再去讲解。
裁剪空间可以参考网址:https://en.wikipedia.org/wiki/Clip_coordinates

到这里之后我们再去推导透视矩阵是怎么炼成的?为什么会有透视除法?
之前的那个图,我们再次给出:
这里写图片描述
黄色面,在scratchpixel网址(上面的学习网址)中叫做图片平面image plane. 平截头体中的任意一点,最终都会被映射到这个2D的平面上去。上面的已经分析了,通过三角形的相似,可以得到点A(x,y,z)映射到黄色平面上之后得到了A’(-nx/z,-ny/z,-n)。问题好像到这里已经能够告诉计算机去画一个3D的东西了,因为我们已经将3D的点映射到了2D的平面上了。但是我们有没有考虑裁剪呢?我们并没有考虑裁剪呀,那么考虑裁剪的话,是在这个平截头体中做的方便呢,还是在上面说的在立方体中做的方便呢?已经说过了,在立方体中做的方便。

为什么需要ndc空间?
当找寻这个知识点的时候,无意中发现了这个网址http://www.scratchapixel.com/ 写的真的是很好呀,非常的细致,解答了很多之前的疑惑。后面我们还会认真的研读这个网址所写的东西。现在呢?我还是真对目前所了解的继续写博客,后面随着认识的提高,会勘误之前出现的错误。
ndc空间是设备空间,不同的显示器会有不同的显示方式,那么我们不希望我们的程序真对不同的设备就不能正确运行了,所以做一次抽象。只有抽象出一个普适的坐标才能屏蔽各个设备的不同。

下面就来说一下什么是设备坐标、光栅空间坐标、分辨率、电影底片的分辨率,见下一节。只有明白这些概念,才能彻底的了解透视矩阵的推导。

http://www.66acg.com/?post=522
http://blog.csdn.net/popy007/article/details/1797121
http://blog.csdn.net/n5/article/details/9734905
http://www.scratchapixel.com/lessons/3d-basic-rendering/perspective-and-orthographic-projection-matrix/projection-matrix-GPU-rendering-pipeline-clipping
http://www.lxway.com/65981822.htm
http://blog.csdn.net/llwszjj/article/details/24842189
http://www.cnblogs.com/ojo-blogs/p/6754664.html
https://www.tomdalling.com/blog/modern-opengl/explaining-homogenous-coordinates-and-projective-geometry/

齐次坐标:
http://www.songho.ca/math/homogeneous/homogeneous.html

http://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/projection-stage

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用 OpenGL 立方体的基本步骤: 1. 首先,您需要设置 OpenGL 的视口和投影矩阵以确定立方体的位置和大小。 2. 接下来,您需要定义立方体的顶点和面。一个立方体有六个面,每个面由两个三角形组成。每个三角形都由个顶点定义。 3. 接下来,您需要将这些顶点和面传递给 OpenGL,并设置正确的顶点属性和缓冲区对象。 4. 在设置完所有必要的属性后,您可以使用 glDrawElements 或 glDrawArrays 命令来渲染立方体。 下面是一个简单的 OpenGL 程序,用于一个红色的立方体: ```c++ #include <GL/glut.h> GLfloat vertices[] = {-1.0,-1.0,-1.0,1.0,-1.0,-1.0,1.0,1.0,-1.0,-1.0,1.0,-1.0,-1.0,-1.0,1.0,1.0,-1.0,1.0,1.0,-1.0,-1.0,1.0,-1.0,1.0,1.0,1.0,1.0,1.0,-1.0,1.0,-1.0,-1.0,-1.0,-1.0,-1.0,1.0,-1.0,-1.0,1.0,1.0,-1.0,-1.0,1.0,-1.0,1.0,1.0,1.0,1.0,-1.0,1.0,-1.0}; GLfloat colors[] = {1.0,0.0,0.0,1.0,0.0,1.0,0.0,1.0,1.0,0.0,0.0,1.0,0.0,1.0,1.0,1.0,1.0,0.0,1.0,1.0,0.0,1.0,0.0,0.0,1.0,0.0,1.0,1.0,1.0,1.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0,1.0,1.0,0.0,1.0,1.0,0.0,0.0,0.0,1.0,0.0,1.0,1.0,0.0,1.0,0.0,0.0,0.0,1.0,1.0}; GLubyte indices[] = {0,3,2,1,2,3,7,6,0,4,5,6,1,5,4,0,2,6,5,1,7,3,4,7}; void drawCube(void) { glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glVertexPointer(3, GL_FLOAT, 0, vertices); glColorPointer(3, GL_FLOAT, 0, colors); glDrawElements(GL_QUADS, 24, GL_UNSIGNED_BYTE, indices); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_COLOR_ARRAY); } void display(void) { glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); gluLookAt(5,5,5,0,0,0,0,1,0); drawCube(); glFlush(); } void reshape(int w, int h) { glViewport(0, 0, (GLsizei)w, (GLsizei)h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60, (GLfloat)w / (GLfloat)h, 1.0, 100.0); glMatrixMode(GL_MODELVIEW); } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(500, 500); glutInitWindowPosition(100, 100); glutCreateWindow(argv[0]); glClearColor(0.0, 0.0, 0.0, 0.0); glutDisplayFunc(display); glutReshapeFunc(reshape); glutMainLoop(); return 0; } ``` 这个程序创建一个立方体,并使用红色进行着色。它还设置了视口和投影矩阵,使立方体在屏幕上居中并具有透视效果。您可以通过更改顶点数组和颜色数组来更改立方体的外观。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值