作为
OpenGLARB
写的
Official Guide,
确实值得学习
OpenGL
的开发者反复研读。我没有买第
5
版,一直都是读的第
4
版,两版的差异我还不得而知,而
OpenGL3
也即将正式发布了。
<!--[if !supportLists]-->
1)<!--[endif]-->
真实感图形学和非真实感图形学
(NPR)
的区别和联系,近几年
NPR
的研究也开始发展起来了,连续几年都在
Siggraph
上有这方面的文章发表。
<!--[if !supportLists]-->
2)<!--[endif]-->
线性代数,微分几何,数值计算等数学知识在图形学的研究中占据十分重要的地位,我这才明白为什么那么多数学系的研究生从事图形学的研究。
<!--[if !supportLists]-->
3)<!--[endif]-->
“OpenGL
是一个状态机
”
,所以时刻注意当前的状态。这点值得好好思考。。。
<!--[if !supportLists]-->
4)<!--[endif]-->
几何数据和像素数据的两条绘制流水线的流程
<!--[if !supportLists]-->
5)<!--[endif]-->
openGL
的矩阵变换是和代码顺序相反的顺序执行的,所以写代码都是先写投影变换,再写模型视图变换,而执行变换是按从后到前的顺序的
<!--[if !supportLists]-->
6)<!--[endif]-->
要利用
glPushMatrix
和
glPopMatrix
,
glPushAttribute()
, glPopAttribute()
来进行矩阵和状态的保存和恢复。
<!--[if !supportLists]-->
7)<!--[endif]-->
glPolygonMode
来设置多边形正面和背面的绘制模式
<!--[if !supportLists]-->
8)<!--[endif]-->
一般来说,顶点以逆时针顺序绘制的多边形为正面,但可以用
glFrontFace
来控制如何确定多边形的正面。不透明多边形构成的封闭面中,背面不可见,若视点处于物体外部,则可以用
glCullFace
来剔除
(culling)
掉背面。若视点处于物体内部,则背面可见。
<!--[if !supportLists]-->
9)<!--[endif]-->
画一个立方体,如果我们一个个面去绘制的话,就会重复绘制两个面的公共点,这种情况下我们可以使用顶点数组,主要用到的函数有
glEnableClientState(),glVertexPointer(),glColorPointe,glArrayElement,glDrawElements,
glMultiDrawElements,glDrawRangeElements,glDrawArrays,glMultiDrawArrays,glIneteleavedArrays
等函数
.
<!--[if !supportLists]-->
10)
绘制曲面时采用的三角形分割,注意分割的递归深度和所需要满足的曲率要求。
<!--[if !supportLists]-->
11)<!--[endif]-->
各种矩阵变换和照相机的类比。移动相机和反向移动物体效果是一样的
<!--[if !supportLists]-->
12) <!--[endif]-->
视口变换期间会对
z
坐标进行编码,然后保存在深度缓存中,可以使用
glDepthRange
将
z
坐标缩放到指定的范围内,默认范围是
[0,1]
。透视投影中会对变换后的深度坐标执行透视除法
(
除以
w),
离近剪裁面越远,变换后的深度坐标度量未知的准确性越低。
<!--[if !supportLists]-->
13)<!--[endif]-->
投影时注意
near
和
far
必须都大于
0
,分别表示近剪裁面和远剪裁面到视点的距离。
<!--[if !supportLists]-->
14)<!--[endif]-->
可以最多定义
6
个附加剪裁面,平面由方程
Ax+By+Cz=0
确定,
OpenGL
会自动对剪裁面进行模型变换和视点变换。被剪裁掉的多边形,
OpenGL
会自动重新生成其边,使用的函数是
glClipPlane()
。
15
)逆变换是从鼠标选中的屏幕位置来确定对应的三维空间中位置,这可以使用
gluUnProject
和
gluUnProject4
来实现。若
glDepthRange()
指定的是默认设置,则
z
为
0.0
时,对应的点位于近剪裁面上,
z
为
1.0
时,对应的点位于远剪裁面上。
gluProject
()是用来模拟绘制流水线的操作用的,给定三维世界坐标和所有的变换,它可以返回变换后得到的窗口坐标。
16
)抖动是使用几种颜色合成其他颜色的技术,它的原理就是覆盖由多个像素组成的区域而不是单个的像素,使用的参数是
GL_DITHER
。
17
)隐藏面消除是利用深度缓存实现的,使用
glClear(GL_DEPTH_BUFFER_BIT)
将所有像素的深度值设置为最大可能距离(一般是远剪裁面),在绘制每个面的时候计算其到观察面的距离,若调用了
glEnable(GL_DEPTH_TEST)
来启用深度缓存,则在绘制每个像素之前,将其深度和原像素中存储的深度进行比较,如果新像素更近,则用其颜色和深度代替原来的值,否则说明新像素被原像素遮住了,直接丢弃其颜色和深度信息就行了。
18
)个人认为光照模型是真实感图形的核心。
OpenGL
光照模型将光照分为
4
个部分:环境光,散射光,镜面反射光和发射光,它们分别被计算,然后叠加起来。
除了光线的
RGB
值外,还要考虑材质对光线的反射比例。材质也有环境色,散射色和镜面反射色,这决定了材质对环境光,散射光和镜面反射光的反射率。将材质对环境光反射率和每个光源的环境光分量相乘,将材质对散射光反射率和每个光源的散射光分量相乘,将材质对镜面反射光反射率和每个光源的镜面反射光分量相乘。材质对环境光和散射光的反射率决定了其颜色,这两种反射率基本是一样的。镜面反射点的颜色为光源中镜面反射光的颜色,例如将白光照到红色球上,球大部分是红色的,但镜面反射点是白色的。
公式:
(LR*MR,LG*MG,LB*MB),L
表示光线,
M
表示材质。
示例:使用不同的光照模型效果
void
init(
void
)
{
GLfloat mat_specular[]
=
{
1.0
,
1.0
,
1.0
,
1.0
};
GLfloat mat_shininess[]
=
{
50.0
};
GLfloat light_position[]
=
{
1.0
,
1.0
,
1.0
,
0.0
};
glClearColor (
0.0
,
0.0
,
0.0
,
0.0
);
glShadeModel (GL_SMOOTH);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
}
void
display(
void
)
{
glClear (GL_COLOR_BUFFER_BIT
|
GL_DEPTH_BUFFER_BIT);
glutSolidSphere (
1.0
,
20
,
16
);
glFlush ();
}
void
init(
void
)
{
GLfloat mat_specular[]
=
{
1.0
,
1.0
,
1.0
,
1.0
};
GLfloat mat_shininess[]
=
{
50.0
};
GLfloat light_position[]
=
{
1.0
,
1.0
,
1.0
,
0.0
};
GLfloat ambient[]
=
{
0.1
,
0.1
,
0.1
,
1.0
};
glClearColor (
0.0
,
0.0
,
0.0
,
0.0
);
glShadeModel (GL_SMOOTH);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glLightModeli(GL_LIGHT_MODEL_AMBIENT,ambient);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
}
void
display(
void
)
{
glClear (GL_COLOR_BUFFER_BIT
|
GL_DEPTH_BUFFER_BIT);
glutSolidSphere (
1.0
,
20
,
16
);
glFlush ();
}
19
)法线决定了物体相对于光源的朝向,从而决定了多少光源的光会照射到顶点上。面发线的长度必须为
1
,模型转换矩阵可能缩放面法线,因此可能要使用参数
GL_NOMALIZE
或
GL_RESCALE_NORMAL
来调用
glEnable()
。
20
)光源
GL_LIGHT0
与其他几个光源不同,
GL_DIFFUSE,GL_SPECULAR
的默认值是(
1.0
,
1.0,1.0,1.0
)
,
而其他光源的默认值是(
0.0,0.0,0.0,1.0
)。
21
)光源的属性
GL_SPECULAR
影响镜面反射区域的颜色,一般物体的镜面反射区域的颜色为入射光线的颜色,要实现真实感,应该将它的值设置成与
GL_DIFFUSE
相同。
22
)光源有两种,定向的(如太阳)和定位的(如台灯),在设置
GL_POSITION
时,若
w
值为
0
,则为定向光源,默认的光源位置就是
(0,0,1,0),
是一个指向
z
轴负方向的定向光源。若
w
不为
0
,则是定位光源,指定的是光源的齐次坐标。
23
)定位光源需要对其发射的光进行衰减,可以设置各种衰减因子。环境光,散射光和镜面反射光的贡献都是衰减的,只有发射光和全局环境光不会衰减。
24
)通过将发射光限定在指定的圆锥体内可以让定位光源成为聚光灯,这只需要指定椎体的角度和光源的位置就可以了,例如:
glLightf(GL_LIGHT0,GL_SPOT_CUTOFF,45.0);
GLfloat direction[]={-1.0,-1.0,0.0};
glLightf(GL_LIGHT0,GL_SPOT_DIRECTION, direction);
光源的默认方向是
(0.0,0.0,-1.0),
即指向
z
轴负方向。还可以设置参数
GL_SPOT_EXPONENT
来控制光线的聚集程度,默认值为
0
,轴线处光强最大,从轴线向母线移动时不断衰减,因此,聚光指数越大,光源的聚集程度越高。
25
)对光源进行平移或旋转,使之相对于静止的物体移动,这可以在指定模型变换后设置光源位置,然后通过修改模型变换来改变光源的位置。
void
display(
void
)
{
GLfloat position[]
=
{
0.0
,
0.0
,
1.0
,
1.0
};
GLfloat ambient[]
=
{
1.0
,
0.0
,
0.0
,
1.0
};
glClear (GL_COLOR_BUFFER_BIT
|
GL_DEPTH_BUFFER_BIT);
glPushMatrix ();
gluLookAt (
0.0
,
0.0
,
5.0
,
0.0
,
0.0
,
0.0
,
0.0
,
1.0
,
0.0
);
glPushMatrix ();
glRotated ((GLdouble) spin,
1.0
,
0.0
,
0.0
);
glLightfv (GL_LIGHT0, GL_POSITION, position);
glLightModelfv(GL_LIGHT_MODEL_AMBIENT,ambient);
glTranslated (
0.0
,
0.0
,
1.5
);
glDisable (GL_LIGHTING);
glColor3f (
0.0
,
1.0
,
0.0
);
glutWireCube (
0.1
);
glEnable (GL_LIGHTING);
glPopMatrix ();
glutSolidTorus (
0.275
,
0.85
,
8
,
15
);
glPopMatrix ();
glFlush ();
}