OpenGL的计算机图形学

一、Gobal VS Local Lighting

局部光照(Local illumation) 简单说就是只考虑光源到模型表面的照射效果。

特点:速度快,真实度欠佳。

全局光照(Global illumination) 简单的说就是考虑到环境中所有表面和光源相互作用的照射效果。

特点:速度慢,真实度高。

相关课件截图:



二、Ray Tracing

A.简介:

采用Ray Tracing和Radiosity两种技术来计算全局光照

Ray Tracing用于计算镜面反射,灯光透过玻璃的效果和阴影,还有模拟精确的直接照射光产生的阴影。

Radiosity计算每个表面的元素发射的光照量进行计算,并保存到网格当中。

光线跟踪是一种真实地显示物体的方法。

光线跟踪方法沿着到达视点的光线的反方向跟踪,经过屏幕上每一个象素,找出与视线相交的物体表面点Po,并继续跟踪,找出影响Po点光强的所有光源,

从而算出P0点上精确的光线强度,在材质编辑中经常用来表现镜面效果。光线跟踪或称光迹追踪是计算机图形学的核心算法之一。

B.原理:

1)相机的胶片被分成离散的网格(即像素点),我们的目标是确定每一个像素点的颜色值。


2)对于每一个像素,从相机位置追踪一条光线,指向该像素点。

3)对于这束光线,判断其是否和场景中的物体相交。如果相交,则继续步骤4;否则,将背景色填充到当前像素中去,回到步骤2,继续处理下一个像素。

4)如果光线和物体相交,计算物体表面交点的颜色值。该点的颜色值即为该像素的颜色值。

a、首先检查每个光源在该交点的贡献值。追踪一条新光线去光源,用来确定交点是被全部照亮、部分照亮还是没有被照亮,同时确定了阴影。

b、如果物体表面具有反射性质,计算初始光线的反射光线,然后追踪这条反射光线,转到步骤3。

c、如果物体表面具有折射性质,计算初始光线的折射光线,然后追踪这条折射光线,转到步骤3。

d、最终,根据表面性质(反射率、折射率),和不同类型光线计算得出的颜色值,来确定交点的颜色值,即当前像素点的颜色值。

5)回到步骤2,继续下一个像素点。重复这个过程直到像素点都遍历完成。

C.光线跟踪的优点:

        光线跟踪的流行来源于它比其它渲染方法如扫描线渲染或者光线投射更加能够现实地模拟光线,像反射和阴影这样一些对于其它的算法来说都很难实现的效果,

却是光线跟踪算法的一种自然结果。光线跟踪易于实现并且视觉效果很好。实际上可能对于简单的物体如多边形曲面与简单的点源比较简单,原则上,可以产生全球照明效果,阴影和多重反射。

D.光线跟踪的缺点:

  光线跟踪的一个最大的缺点就是性能。光线跟踪是缓慢的,不适合交互应用



相关课件截图:






三、Synthetic Camera Model

合成照相机模式:观察者,物体,光源。

相关课件截图:



四、Practical Approach

按照应用加工的顺序依次访问物体,可以只考虑局部照明

管道结构:

所有步骤可以在硬件的显卡中实现。

1、Vertex Processing:

许多工作在管道转换对象表示从一个坐标系到另一个坐标系:

1)物体坐标

2)相机(眼)坐标

3)屏幕坐标

每一个坐标变换相当于一个矩阵变换。顶点处理器还计算顶点颜色。

2、Clipping&Primitive Assembly

Clipping:就像一个真正的相机不能“看到”整个世界,虚拟摄像头所能看到的只是世界的一部分或对象空间。

Primitive Assembly:顶点必须收集到的几何对象前剪切和光栅化可以发生。线段、多边形、曲线和曲面。

3、Rasterization

如果一个对象没有被裁剪,适当的像素在帧缓冲区必须被指定颜色,光栅产生每个对象的一组片段。

碎片是“潜在的像素”:1.在帧缓冲区中有坐标。2.有颜色和深度的属性。

顶点属性在光栅化过程中插入。

4.Fragment Processing
分段处理:片断被处理去确定帧缓冲中相应像素的颜色。颜色可通过纹理映射或插值顶点的颜色来确定。
颜色可能会被其他更接近于相机的片段阻塞。


相关课件截图:






五、GL_TRIANGLE_STRIP vs GL_TRIANGLE_FAN

一般情况下有三种绘制一系列三角形的方式,
1).GL_TRIANGLES
以每三个顶点绘制一个三角形。

第一个三角形使用顶点v0,v1,v2,第二个使用v3,v4,v5,以此类推。

如果顶点的个数n不是3的倍数,那么最后的1个或者2个顶点会被忽略。

2)GL_TRIANGLE_STRIP

稍微有点复杂,其规律是:

构建当前三角形的顶点的连接顺序依赖于要和前面已经出现过的2个顶点组成三角形的当前顶点的序号的奇偶性(如果从0开始):

如果当前顶点是奇数:

组成三角形的顶点排列顺序:T = [n-1 n-2 n].

如果当前顶点是偶数:

组成三角形的顶点排列顺序:T = [n-2 n-1 n].

以上图为例,第一个三角形,顶点v2序号是2,是偶数,则顶点排列顺序是v0,v1,v2。第二个三角形,顶点v3序号是3,是奇数,则顶点排列顺序是v2,v1,v3,

第三个三角形,顶点v4序号是4,是偶数,则顶点排列顺序是v2,v3,v4,以此类推。

这个顺序是为了保证所有的三角形都是按照相同的方向绘制的,使这个三角形串能够正确形成表面的一部分。

对于某些操作,维持方向是很重要的,比如剔除。

注意:顶点个数n至少要大于3,否则不能绘制任何三角形。

3)GL_TRIANGLE_FAN
与GL_TRIANGLE_STRIP类似,不过它的三角形的顶点排列顺序是T = [0 n-1 n].各三角形形成一个扇形序列。

举个栗子:

顶点v1,v2,v3,v4

GL_TRIANGLE_FAN

先v1,v2,v3再v1,v3,v4这样可以形成一个扇形状得图形。

GL_TRIANGLE_STRIP

先v1, v2, v3, 再 v3, v2, v4 并且确定这些顶点可以形成某个多边形。

 

效率总结:用GL_TRIANGLE_STRIP的效率是远远高于GL_TRIANGLES。

相关课件截图:




六、Polygon Issues:Simple & Convex & Flat
OpenGL只能在以下情况正确的画出多边形:
1)Simple: 多边形的边不能有交错
2)Convex: 任意两点之间的连线必须也在多边形内
3)Flat: 所有的点都必须在同一个平面

相关课件截图:





七、 Z-Buffer/Depth-BufferAlgorithm
该算法使用一个额外的缓冲区,Z-Buffer,像几何物体通过管道一样存储深度信息。
在main.cpp中必须有以下语句:
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH)
初始化的init.cpp中必须有以下语句:
glEnable(GL_DEPTH_TEST)
在Display的回调函数中清除深度缓存:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

相关课件截图:






八、Double Buffering
除了单缓冲,我们也可以采用双缓冲技术:
Front Buffer: 展示画面但是没写入
Back Buffer: 有输入但是并不展示
程序在main.cpp中需要一个双缓冲:
glutInitDisplayMode(GL_RGB | GL_DOUBLE)
在Display方法中交换双缓存:

[cpp]  view plain copy
  1. void mydisplay()  
  2. {  
  3.     glClear(GL_COLOR_BUFFER_BIT|….);  
  4.     /* draw graphics here */  
  5.     glutSwapBuffers();  
  6. }  






九、Display List
概念上讲类似于一个图形文件,
1)必须创建
2)必须添加内容
3)关闭
在CS框架下,显示列表一般是在服务器端。可以重新显示而不用每次都发送原来的数据。
可以用以下方法使用一个显示列表:
Step1:Creating a display list

[cpp]  view plain copy
  1. GLuint id;  
  2. void init()  
  3. {  
  4.    id = glGenLists( 1 );  
  5.    glNewList( id, GL_COMPILE );  
  6.    /* other OpenGL routines */  
  7.    glEndList();  
  8. }  




Step2:Call a created list

[cpp]  view plain copy
  1. void display()  
  2. {  
  3.    glCallList( id );  
  4. }  

 

大部分的OpenGL函数可以放在显示列表里。
内部状态的改变在执行后将会持续。
为了避免不希望出现的结果,我们可以使用
glPushAttrib and glPushMatrix 在输入一个显示列表之前,
glPopAttrib and glPopMatrix 在退出之前。
相关课件截图:




十、Homogeneous Coordinates

对于三维空间里的齐次坐标系是这样的:

p =[x’ y’ z’ w] T =[wx wy wz w] T

w=0时,我们通过以下方式返回一个三维坐标的点:

x<-x’/w

y<-y’/w

z<-z’/w

如果w=0, 代表的是一个向量。

注意齐次坐标通过穿过四维空间原点的线取代三维空间。

当w=1,它所代表的顶点是:[x y z 1]

齐次坐标表示是计算机图形学的重要手段之一,它既能够用来明确区分向量和点,同时也更易用于进行仿射(线性)几何变换。

对于一个向量v以及基oabc可以找到一组坐标(v1,v2,v3),使得

v = v1*a + v2*b +v3*c           1

而对于一个点p,则可以找到一组坐标(p1,p2,p3),使得

p–o = p1*a +p2*b + p3*c        2

从上面对向量和点的表达,我们可以看出为了在坐标系中表示一个点(如p),我们把点的位置看作是对这个基的原点o所进行的一个位移,

即一个向量——p – o

有的书中把这样的向量叫做位置向量——起始于坐标原点的特殊向量。

我们在表达这个向量的同时用等价的方式表达出了点

p:p= o + p1*a +p2*b + p3*c       (3)

下面看下这个向量和点的不同表达方式:

v:v = v1*a + v2*b +v3*c           1

p:p= o + p1*a +p2*b + p3*c       (3)

这里可以看出,虽然都是用代数分量的形式表达向量和点,但表达一个点比一个向量需要额外的信息。如果我写出一个代数分量表达(1, 4, 7),谁特么知道它是个向量还是个点?!

    我们现在把(1)(3)写成矩阵的形式:v = (v1 v2 v3 0) X (a b c o)

p = (p1 p2 p3 1) X (a b c o),这里(a,b,c,o)是坐标基矩阵,右边的列向量分别是向量v和点p在基下的坐标。这样,向量和点在同一个基下就有了不同的表达:3D向量的第4个代数分量是0,而3D点的第4个代数分量是1。像这种这种用4个代数分量表示3D几何概念的方式是一种齐次坐标表示。

这样,上面的(1, 4, 7)如果写成(1,4,7,0),它就是个向量;

如果是(1,4,7,1),它就是个点。

下面是如何在普通坐标(Ordinary Coordinate)和齐次坐标(Homogeneous Coordinate)之间进行转换:

(1)从普通坐标转换成齐次坐标时
如果(x,y,z)是个点,则变为(x,y,z,1);
如果(x,y,z)是个向量,则变为(x,y,z,0)

(2)从齐次坐标转换成普通坐标时

如果是(x,y,z,1),则知道它是个点,变成(x,y,z);
如果是(x,y,z,0),则知道它是个向量,仍然变成(x,y,z)

以上是通过齐次坐标来区分向量和点的方式。从中可以思考得知,对于平移T、旋转R、缩放S3个最常见的仿射变换,平移变换只对于点才有意义,因为普通向量没有位置概念,只有大小和方向.

而旋转和缩放对于向量和点都有意义,你可以用类似上面齐次表示来检测。从中可以看出,齐次坐标用于仿射变换非常方便。

此外,对于一个普通坐标的点P=(Px, Py, Pz),有对应的一族齐次坐标(wPx, wPy, wPz, w),其中w不等于零。比如,P(1, 4, 7)的齐次坐标有(1, 4, 7, 1)、(2, 8, 14, 2)、(-0.1, -0.4, -0.7, -0.1)等等。因此,如果把一个点从普通坐标变成齐次坐标,给x,y,z乘上同一个非零数w,然后增加第4个分量w;如果把一个齐次坐标转换成普通坐标,把前三个坐标同时除以第4个坐标,然后去掉第4个分量。

齐次坐标是所有图形学系统的关键。

所有标准变换(旋转,平移,缩放)可以使用4×4矩阵实现矩阵乘法

Hardware pipeline使用四维表示

为了视角我们需要透视除法。

相关课件截图:






十一、Affine Transform

Rotation/Translation/Scaling/Shear:旋转/平移/拉伸/剪切

推导齐次坐标变换矩阵学习建立任意变换矩阵的简单变换

请注意,矩阵的右边是第一个进行运算的。数学上来讲,下列是等价的

   p’ = ABCp = A(B(Cp))

注意到许多地方使用列矩阵代表点。在列矩阵中:

  p'T = pTCTBTAT

相关课件截图:







十二、Vertex Arrays

OpenGL提供了一种称为顶点数组”,使我们能够在接口中存储数组数据。

六种类型的数组支持

VerticesColorsColor indicesNormalsTexture coordinatesEdge flags

我们只需要了解颜色和顶点就可以了。

举个栗子:

首先来回顾下之前画直线的函数

示例1

[cpp]  view plain copy
  1. void drawOneLine(GLfloat x1,GLfloat y1,GLfloat x2,GLfloat y2)  
  2.   
  3. {  
  4.   
  5.    glBegin(GL_LINES);  
  6.   
  7.    glVertex2f ((x1),(y1));   
  8.   
  9.    glVertex2f ((x2),(y2));  
  10.   
  11.    glEnd();  
  12.   
  13. }  



需要调用两次glVertex2f 并且输入两个顶点,现在我们来看另一种做法将坐标存在一个数组中

示例2


[cpp]  view plain copy
  1. void drawLineWithArray()  
  2.   
  3. {  
  4.   
  5.     GLint vertices[]={25,25,100,100};  
  6.   
  7.     glEnableClientState(GL_VERTEX_ARRAY);  
  8.   
  9.     glVertexPointer(2,GL_INT,0,vertices);  
  10.   
  11.     glBegin(GL_LINES);  
  12.   
  13.     glArrayElement(0);  
  14.   
  15.     glArrayElement(1);  
  16.   
  17. glEnd();  
  18.   
  19. }  



vertices记录了两个坐标(x1,y1)=(25,25),(x2,y2)=(100,100)
但是其索引值却是0,1,2,3为了识别一个坐标点,需要一个函数将数组进行切分
glVertexPointer则用于这个功能可以称这种数组为混合数组参数指定了顶点数组的配对点(如坐标定位为2个颜色则为3个)数据类型及数组
注意在使用顶点数组时必须先调用glEnableClientState开启顶点数组功能在不用的时候调用glDisableClientState来禁用

glArrayElement则根据顶点数组来调用相应的函数每次只调用1个顶点

相关课件截图:



十三、Perspective vs Parallel

计算机图形学的对待所有的投影都一样将它们使用一个单一的管道来实现。

传统视角对于每一种不同类型的投影发展了不同的技术。

根本的区别是平行和角度的区别。虽然从从数学的角度来看平行是角度的极限情况。

相关课件截图:







十四、Default Projection

要求:拉距离,做变化,会写矩阵方程。

我们先来简单的介绍一下OpenGL中的缺省相机。

在OpenGL中,最初物体和相机的帧是完全一样的,缺省的模型矩阵是一个单位矩阵。摄像机定位于远点并且指向Z轴的负方向。OpenGL中还制定了一个位于原点边长为2的正方体为缺省视角。默认的正交投影

我们可以通过一系列的旋转和移动变换把相机移到任何所需位置。

举个栗子:

侧视图:

旋转相机->移动它远离原点->模型变换矩阵C = TR

相关课件截图:



十五、gluLookAt

gluLookAt通过一个简单的接口形成所需的投影矩阵注意到需要设置一个向上的方向。依旧需要初始化可以模型转换联系起来。

例如:等角视图对准轴立方体

[cpp]  view plain copy
  1. glMatrixMode(GL_MODELVIEW):  
  2.   
  3. glLoadIdentity();  
  4.   
  5. gluLookAt(1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0. 0.0);  



函数定义:

void gluLookAt(GLdouble eyex,GLdouble eyey,GLdouble eyez,GLdouble centerx,GLdouble centery,GLdouble centerz,GLdouble upx,GLdouble upy,GLdouble upz); 

第一组eyex, eyey,eyez 相机在世界坐标的位置。

第二组centerx,centery,centerz 相机镜头对准的物体在世界坐标的位置

第三组upx,upy,upz 相机向上的方向在世界坐标中的方向





十六、Normalization

相比于为每一个类型的投影生成不同的投影变换矩阵,我们可以使用默认的视图体积把所有的投影变换转化成正交投影。这个策略允许我们使用处理流程管道中的标准变换从而实现更有效的剪切。

我们通过模型和投影变换呆在四维齐次坐标中,

这些变化是非奇异的默认是单位矩阵(正交视图)。

Normalization 让我们对简单的立方体不用顾及投影类型的进行裁剪,一直推迟最终的投影直到结束的时候。尽可能的保存隐藏面的深度信息。

相关课件截图:






十七、Projection Normalization

相比于为每个类型的投影都单独的生成对应的投影矩阵

我们可以将所有投影都转换成正交投影

这个策略允许我们使用标准的转变的管道,进行有效的裁剪








十八、Share Lighting Equation

相关博文:[OpenGL]计算机图形学:明暗处理的基本算法

需要记住核心的两个方程。



十九、Gouraud and Phong Shading

相关博文:[OpenGL]计算机图形学:明暗处理的基本算法

Gouraud Shading

寻找每个点的平均nomal值;

把修改的Phong模型应用在每个顶点上;

在每个多边形中插入顶点的遮挡。

Phong shading

找到顶点的nomal向量;

穿过边来插入顶点;

通过多边形来插入边向量;

把修改后的Phong模型应用到每个片段上。

相关课件截图:



二十、Cohen-Sutherland Algorithm




二十一、Liang-Barsky Clipping



二十二、Pipeline Clipping of Polygons

三个方面:添加前后剪切->策略中使用SGI几何引擎->小量的增加潜伏期



二十三、BSP-TREE决定渲染顺序



二十四、DDA Algorithm  &  Bresenham’s Algorithm

DDA Algorithm

DDA称为数值微分画线算法,是直线生成算法中最简单的一种。原理相当简单,就是最直观的根据斜率的偏移程度,决定是以x为步进方向还是以y为步进方向。 然后在相应的步进方向上,步进变量每次增加一个像素,而另一个相关坐标变量则为Y=kx+m(以x为步进变量为例,m为斜率).当斜率在-1到1之间是让 X增长值为1,Y的增长值为K。当斜率大于1时。让Y增长值为1,X的增量为1/K。这样做的目的是让每次变化的值不能大于1。这样可以让像素点更加整 齐。
这个程序对于我的意义在于:可以在VC中利用GDI的画点函数--- SetPixel来实现它

Bresenham’s Algorithm:

过各行、各列像素中心构造一组虚拟网格线,按直线从起点到终点的顺序计算直线各垂直网格线的交点,然后确定该列像素中与此交点最近   的像素。   该算法的优点在于可以采用增量计算,使得对于每一列,只要检查一个误差项的符号,就可以确定该列所求的像素。 

相关课件:




二十五、Polygon Scan Conversion

多边形内外判断的方法 Inside/Outside test for polygon
奇偶测试/winding

相关课件截图:



二十六、Robot Arm

机械手臂是考试的重点,所以另开博文来写。

相关课件截图:


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值