1. 光照模型建立
OPenGL处理光照采用的是一种近似:
把光照系统分为三部分:
光源、材质和光照环境。
光源: 光的来源
材质:指接受光照的各种物体的表面,由于物体如何反射光线只有物体表面决定
材质特点就决定了物体反射光线的特点。
光照环境:是指一些额外的参数,它们将影响最终的光照画面。
比如一些光线经过多次反射后,已经无法分清它究竟是由哪个光源发出,这
时指定一个环境亮度参数,可以使最后形成的画面更接近真实情况。
2. 法线向量
OpenGL中,法线方向是用一个向量表示。
为了实现光照效果,则需要在代码中为每个顶点指定其法线向量。
在指定法线向量时,只需要指定每一个顶点的法线向量,OpenGL会自行计算顶点
之间的其他点的法线向量,并且,法相向量一旦被指定,除非再指定新的法线向量
,否则以后指定的所有点都将以这一向量作为自己的法线向量。
glColor* 函数用于指定颜色
glNormal* 函数则可以指定法线向量
注意:使用glTranslate* 函数 或者glRotate* 函数可以改变物体的外观,但是法线向
量并不会随之改变。
使用glScale* 函数,对每一坐标进行不同程度缩放,很有可能导致法线向量不
正确,所以应该尽量避免使用glScale* 函数,即使使用,最好保证各坐标等比例
缩放。
3. 控制光源
OpenGL中,支持有限数量光源。 至少会支持8个光源
GL_LIGHT0 0号光源
GL_LIGHT1 1号光源
...
GL_LIGHT7 7号光源
glEnable 函数用于开启它们。
例如: glEnable(GL_LIGHT0); 表示开启0号光源
glDisable 函数用于关闭光源。
每一个光源都可以设置其属性
这一动作是通过glLight* 函数完成的
该函数j具有三个参数
1. 指明是哪一个光源的属性
2. 指明设置该光源的哪一个属性
3. 指明该属性的值设置为多少
(1)GL_AMBIENT、GL_DIFFUSE、GL_SPECULAR 属性表示了光源所发出的
光的反射特性,每个属性由四个值表示,分别代表了颜色的RGBA值。
GL_AMBIENT 表示该光源所发出的光,经过非常多次的反射后,最终
遗留在整个光照环境中的强度。
GL_DIFFUSE 表示该光源所发出的光,照射到粗糙表面时经过漫反射,所
得到的光照强度。
GL_SPECULAR 表示该光源所发出的光,照射到光滑表面时经过镜面反射,
所得到的光的强度。
(2)GL_POSITION 属性。表示光源所在的位置。由四个值(X, Y, Z, W)表示。
如果第四个值W为零,则表示光源位于无限远处,前三个值表示了它所在的
方向。这种光源成为方向性光源。
如果第四个值W不为零,则X/W, Y/W, Z/W 表示了光源的位置。这种光源称为
位置性光源。对于位置性光源,设置其位置与设置多边形顶点的方式相似,
可以使用各种矩阵变换函数:glTranslate*,glRotate*
注意:方向性光源在计算时比位置性光源快了不少,因此在视觉效果允许的
情况下,应该尽可能的使用方向性光源。
(3)GL_SPOT_DIRECTION、GL_SPOT_EXPONENT、GL_SPOT_CUTOFF 属
性。表示将光源作为聚光灯使用。只针对位置性光源有效。
GL_SPOT_DIRECTION 属性有三个值,表示一个向量,即光源发射的方向。
GL_SPOT_EXPONENT 属性只有一个值,表示聚光的程度。
为零表示光照范围内各个方向发射的光线强度相同。
为正数表示向中央集中,正对发射方向位置受更多光照,其他位置
受到较少光照,数值越大,聚光效果就越明显。
GL_SPOT_CUTOFF 属性也只有一个值,表示一个角度,它是光源发射光线
所覆盖角度的一半(如图),其取值范围在0 - 90之间 ,也可以取 180 这个
特殊值。取值为 180 时表示光源发射光线覆盖 360 度,即不使用聚光灯,向
全周围发射。
(4)GL_CONSTANT_ATTENUATION、GL_LINEAR_ATTENUATION、
GL_QUADRATIC_ATTENUATION 属性。这三
个属性表示了光源所发出的光线的直线传播特性。
现实生活中 ,光线的强度随着距离的增加而减弱。
OpenGL 把这个减弱的趋势抽象成函数:
衰减因子 = 1 / (k1 + k2 * d + k3 * k3 * d)
其中 d 表示距离,光线的初始强度乘以衰减因子,就得到对应距离的光线
强度。
k1, k2, k3 分别就是
GL_CONSTANT_ATTENUATION, GL_LINEAR_ATTENUATION,
GL_QUADRATIC_ATTENUATION。
通过设置这三个常
数,就可以控制光线在传播过程中的减弱趋势。
4. 控制材质
材质则是通过 glMaterial*函数来设置的
glMaterial*函数有三个参数。
第一个参数表示指定哪一面的属性。
可以是 GL_FRONT、GL_BACK 或者
GL_FRONT_AND_BACK
第二、第三个参数与 glLight*函数的第二、三个参数作用类似。
(1)GL_AMBIENT、GL_DIFFUSE、GL_SPECULAR 属性。
这三个属性与光源的三个对应属性类似,每一属性都由四
个值组成。
GL_AMBIENT 表示各种光线照射到该材质上,经过很多次反射后最终遗留
在环境中的光线强度(颜色)。
GL_DIFFUSE 表示光线照射到该材质上,经过漫反射后形成的光线强度
(颜色)。
GL_SPECULAR 表示光线照射到该材质上,经过镜面反射后形成的光线强
度(颜色)。
通常,GL_AMBIENT 和 GL_DIFFUSE 都取相同的值,可以达到比较真
实的效果。
使用 GL_AMBIENT_AND_DIFFUSE 可以同时设置 GL_AMBIENT 和
GL_DIFFUSE 属性。
(2)GL_SHININESS 属性。
该属性只有一个值,称为“镜面指数”,取值范围是 0 到 128。该值越小,表示
材质越粗糙,点光源发射的光线照射到上面,也可以产生较大的亮点。
该值越大,表示材质越类似于镜面,光源照射到上面后,产生
较小的亮点。
(3)GL_EMISSION 属性。
该属性由四个值组成,表示一种颜色。
OpenGL 认为该材质本身就微微的向外发射光线,以
至于眼睛感觉到它有这样的颜色,但这光线又比较微弱,以至于不会影响到其
它物体的颜色。
(4)GL_COLOR_INDEXES 属性。
该属性仅在颜色索引模式下使用,由于颜色索引模式下的光照比 RGBA 模式要
复杂,并且使用范围较小。
5. 选择关照模型
这里的光照模型就是前面提到的 光照环境。
光照模型包括四个部分:
全局环境光线(即那些充分散射,无法分清究竟来自哪个光源的光线)的强度
观察点位置是在较近位置还是在无限远处、
物体正面与背面是否分别计算光照、
镜面颜色(即 GL_SPECULAR 属性所指定的颜色)的计算是否从其它光照计算
中分离出来,并在纹理操作以后在进行应用。
以上使用同一个函数glLightModel*来进行设置。有两个参数
第一个表示要设置的项目,
第二个参数表示要设置成的值。
GL_LIGHT_MODEL_AMBIENT 表示全局环境光线强度,由四个值组成。
GL_LIGHT_MODEL_LOCAL_VIEWER 表示是否在近处观看,若是则设置为
GL_TRUE,否则(即在无限远处观看)设置为GL_FALSE。
GL_LIGHT_MODEL_TWO_SIDE 表示是否执行双面光照计算。如果设置为
GL_TRUE,则 OpenGL 不仅将根据法线向量计算正面的光照,也会将法线向量反
转并计算背面的光照。
GL_LIGHT_MODEL_COLOR_CONTROL 表示颜色计算方式。如果设置为
GL_SINGLE_COLOR,表示按通常顺序操作,先计算光照,再计算纹理。如果设置
为 GL_SEPARATE_SPECULAR_COLOR,表示将 GL_SPECULAR 属性分离出
来,先计算光照的其它部分,待纹理操作完成后再计算 GL_SPECULAR。后者通常
可以使画面效果更为逼真(当然,如果本身就没有执行任何纹理操作,这样的分离
就没有任何意义)。
6. 注意:
OpenGL 默认是关闭光照处理的。要打开光照处理功能,使用下面的语句:
glEnable(GL_LIGHTING);
要关闭光照处理功能,使用 glDisable(GL_LIGHTING);即可
示例代码
#include
#define WIDTH 400
#define HEIGHT 400
static GLfloat angle = 0.0f;
void myDisplay(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(90.0f, 1.0f, 1.0f, 20.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0, 5.0, -10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
{
GLfloat sun_light_position[] = {0.0f, 0.0f, 0.0f, 1.0f};
GLfloat sun_light_ambient[] = {0.0f, 0.0f, 0.0f, 1.0f};
GLfloat sun_light_diffuse[] = {1.0f, 1.0f, 1.0f, 1.0f};
GLfloat sun_light_specular[] = {1.0f, 1.0f, 1.0f, 1.0f};
glLightfv(GL_LIGHT0, GL_POSITION, sun_light_position);
glLightfv(GL_LIGHT0, GL_AMBIENT, sun_light_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, sun_light_diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, sun_light_specular);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
}
{
GLfloat sun_mat_ambient[] = {0.0f, 0.0f, 0.0f, 1.0f};
GLfloat sun_mat_diffuse[] = {0.0f, 0.0f, 0.0f, 1.0f};
GLfloat sun_mat_specular[] = {0.0f, 0.0f, 0.0f, 1.0f};
GLfloat sun_mat_emission[] = {0.5f, 0.0f, 0.0f, 1.0f};
GLfloat sun_mat_shininess = 0.0f;
glMaterialfv(GL_FRONT, GL_AMBIENT, sun_mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, sun_mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, sun_mat_specular);
glMaterialfv(GL_FRONT, GL_EMISSION, sun_mat_emission);
glMaterialf (GL_FRONT, GL_SHININESS, sun_mat_shininess);
glutSolidSphere(2.0, 40, 32);
}
{
GLfloat earth_mat_ambient[] = {0.0f, 0.0f, 0.5f, 1.0f};
GLfloat earth_mat_diffuse[] = {0.0f, 0.0f, 0.5f, 1.0f};
GLfloat earth_mat_specular[] = {0.0f, 0.0f, 1.0f, 1.0f};
GLfloat earth_mat_emission[] = {0.0f, 0.0f, 0.0f, 1.0f};
GLfloat earth_mat_shininess = 30.0f;
glMaterialfv(GL_FRONT, GL_AMBIENT, earth_mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, earth_mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, earth_mat_specular);
glMaterialfv(GL_FRONT, GL_EMISSION, earth_mat_emission);
glMaterialf (GL_FRONT, GL_SHININESS, earth_mat_shininess);
glRotatef(angle, 0.0f, -1.0f, 0.0f);
glTranslatef(5.0f, 0.0f, 0.0f);
glutSolidSphere(2.0, 40, 32);
}
glutSwapBuffers();
}
void myIdle(void)
{
angle += 1.0f;
if( angle >= 360.0f )
angle = 0.0f;
myDisplay();
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowPosition(200, 200);
glutInitWindowSize(WIDTH, HEIGHT);
glutCreateWindow("OpenGL 光照演示");
glutDisplayFunc(&myDisplay);
glutIdleFunc(&myIdle);
glutMainLoop();
return 0;
}
三维光照绘制总结:
1. 指定光源参数
2. 指定材质参数
3. 指定环境光参数
4. 最后绘制实体