在大自然中,某些物体会发光,例如太阳、电灯等,而其它物体不会发光,反射来自其它物体的光。这些光通过各种方式传播,最后进入眼睛——于是一幅画面就眼前形成了。
OpenGL在处理光照时把光照系统分为三部分,分别是光源、材质和光照环境。
光源就是发光源,如太阳或者电灯等。
材质是指接受光照的物体表面,物体如何反射光线由物体表面决定,材质特点就决定了物体反射光线的特性。
光照环境是指环境亮度。
根据光的反射定律,由光的入射方向和入射点的法线就可以得到光的出射方向。因此,对于指定的物体,在指定了光源后,即可算出光的反射方向,进而计算出光照效果的画面。
在指定法线向量时,只指定每一个顶点的法线向量即可,OpenGL会自行计算顶点之间的其它点的法线向量。
注意:使用glTranslate函数或者glRotate函数可以改变物体的外观,但法线向量并不会随之改变。然而,使用glScale*函数,对每一坐标轴进行不同程度的缩放,很有可能导致法线向量的不正确。
常用设定法向量函数:
glNormal3f (GLfloat nx, GLfloat ny, GLfloat nz);
glNormal3fv(const GLfloat *v);
1、开启光源
在OpenGL中,支持8个以上的光源。以GL_LIGHT0,GL_LIGHT1,…, GL_LIGHT7命名。
开启光源函数:
glEnable(GL_LIGHT0); // 开启第0号光源
关闭光源函数:
glDisable(GL_LIGHT0); // 关闭第0号光源
2、指定光源的属性
(1)GL_AMBIENT、GL_DIFFUSE、GL_SPECULAR属性:
表示了光源所发出的光的反射特性(以及颜色)。每个属性由四个值表示,分别代表了颜色的R, G, B, A值。
GL_AMBIENT表示该光源所发出的光,经过非常多次的反射后,最终遗留在整个光照环境中的强度(颜色)。
GL_DIFFUSE表示该光源所发出的光,照射到粗糙表面时经过漫反射,所得到的光的强度(颜色)。
GL_SPECULAR表示该光源所发出的光,照射到光滑表面时经过镜面反射,所得到的光的强度(颜色)。
(2)GL_POSITION属性。表示光源所在的位置。由四个值(X, Y, Z, W)表示。如果第四个值W为零,则表示该光源位于无限远处,前三个值表示了它所在的方向。这种光源称为方向性光源,太阳可以近似的被认为是方向性光源。如果第四个值W不为零,则X/W, Y/W, Z/W表示了光源的位置。这种光源称为位置性光源。
GLfloat position[] = { 1.0, 1.0, 1.0, 0.0 };
glLightfv(GL_LIGHT0, GL_POSITION, position);
(3)GL_SPOT_DIRECTION、GL_SPOT_EXPONENT、GL_SPOT_CUTOFF属性。表示将光源作为聚光灯使用(这些属性只对位置性光源有效)。
GL_SPOT_DIRECTION属性有三个值,表示一个向量,即光源发射的方向。
GL_SPOT_EXPONENT属性只有一个值,表示聚光的程度,为零时表示光照范围内向各方向发射的光线强度相同,为正数时表示光照向中央集中,正对发射方向的位置受到更多光照,其它位置受到较少光照。数值越大,聚光效果就越明显。
GL_SPOT_CUTOFF属性也只有一个值,表示一个角度,它是光源发射光线所覆盖角度的一半(见图2),其取值范围在0到90之间,也可以取180这个特殊值。取值为180时表示光源发射光线覆盖360度,即不使用聚光灯,向全周围发射。
GLfloat direction[]={ -1.0, 0.0 , -0.5};
glLightfv( GL_LIGHT0 , GL_SPOT_DIRECTION , direction);
4)GL_CONSTANT_ATTENUATION、GL_LINEAR_ATTENUATION、GL_QUADRATIC_ATTENUATION属性。指定光源所发出的光线的直线传播特性。控制光线在传播过程中的减弱趋势。
GLfloat LightRadius=0.5f;
glLightf(GL_LIGHT0 , GL_LINEAR_ATTENUATION, LightRadius);
材质则是通过glMaterial*函数来设置的。
void glMaterialf (GLenum face, GLenum pname, GLfloat param);
void glMaterialfv (GLenum face, GLenum pname, const GLfloat *params);
例:
GLfloat sp[4] = { 1, 1, 1, 1.0 };
glMaterialfv(GL_FRONT, GL_SPECULAR, sp);
GLfloat Diffuse[4]={1,0,0,1};
glMaterialfv(GL_FRONT,GL_DIFFUSE,Diffuse);
在启用光照系统之后,指定颜色变得不太方便。首先创建一个数组,然后调用glMaterial函数将数组传给材质,以此决定物体的颜色。为了简便,可以开启颜色跟踪来简化代码。
// 启动颜色跟踪
glEnable(GL_CORLOR_MATERIAL);
//决定对物体正面还是反面,对环境光、镜面光还是漫射光进行颜色跟踪
glColorMaterial(GL_FRONT,GL_AMBIENT_AND_DIFFUSE);
第一个参数可以取GL_FRONT、GL_BACK、GL_FRONT_AND_BACK中的任意一种,
第二个参数可以取GL_AMBIENT、GL_DIFFUSE、GL_AMBIENT_AND_DIFFUSE、GL_SPECULAR中的任意一种。
启动颜色跟踪之后,我们就可以像以前一样,使用glColor函数来指定图元的颜色了。这时,OpenGL将自动根据从glColor函数传递的颜色来决定物体材质,省去了我们手工指定材质的麻烦。 例如,要在启用光照系统的前提下绘制一个红色的三角形,如下所示:
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT,GL_AMBIENT_AND_DIFFUSE);
glColor3ub(255,0,0);
glBegin(GL_TRIANGLES);
glVertex3f(1,0,1);glVertex3f(1,1,1);glVertex3f(1,0,0);
glEnd();
GLfloat Step6_angle = 0.0f;
void Step6_Lighting(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 创建透视效果视图
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
GLfloat fovy = 90.0f;
GLfloat aspect = 1.0f;
GLfloat zNear = 1.0f;
GLfloat zFar = 20.0f;
gluPerspective(fovy, aspect, zNear, zFar);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
GLfloat eyeX = 0.0;
GLfloat eyeY = 5.0;
GLfloat eyeZ = -10.0;
gluLookAt(eyeX, eyeY, eyeZ, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); // 定义太阳光源,它是一种白色的光源
{
GLfloat sun_position[] = {0.0f, 0.0f, 0.0f, 1.0f};
GLfloat sun_ambient[] = {0.0f, 0.0f, 0.0f, 1.0f};
GLfloat sun_diffuse[] = {1.0f, 1.0f, 1.0f, 1.0f};
GLfloat sun_specular[] = {1.0f, 1.0f, 1.0f, 1.0f};
glLightfv(GL_LIGHT0, GL_POSITION, sun_position);
glLightfv(GL_LIGHT0, GL_AMBIENT, sun_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, sun_diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, sun_specular);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
}
// 定义太阳的材质并绘制太阳
{
GLfloat sun_m_ambient[] = {0.0f, 0.0f, 0.0f, 1.0f};
GLfloat sun_m_diffuse[] = {0.0f, 0.0f, 0.0f, 1.0f};
GLfloat sun_m_specular[] = {0.0f, 0.0f, 0.0f, 1.0f};
GLfloat sun_m_emission[] = {0.5f, 0.0f, 0.0f, 1.0f};
GLfloat sun_m_shininess = 0.0f;
glMaterialfv(GL_FRONT, GL_AMBIENT, sun_m_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, sun_m_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, sun_m_specular);
glMaterialfv(GL_FRONT, GL_EMISSION, sun_m_emission);
glMaterialf (GL_FRONT, GL_SHININESS, sun_m_shininess);
glutSolidSphere(2.0, 40, 32);
}
// 定义地球的材质并绘制地球
{
GLfloat earth_m_ambient[] = {0.0f, 0.0f, 0.5f, 1.0f};
GLfloat earth_m_diffuse[] = {0.0f, 0.0f, 0.5f, 1.0f};
GLfloat earth_m_specular[] = {0.0f, 0.0f, 1.0f, 1.0f};
GLfloat earth_m_emission[] = {0.0f, 0.0f, 0.0f, 1.0f};
GLfloat earth_m_shininess = 30.0f;
glMaterialfv(GL_FRONT, GL_AMBIENT, earth_m_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, earth_m_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, earth_m_specular);
glMaterialfv(GL_FRONT, GL_EMISSION, earth_m_emission);
glMaterialf (GL_FRONT, GL_SHININESS, earth_m_shininess);
glRotatef(Step6_angle, 0.0f, -1.0f, 0.0f);
glTranslatef(5.0f, 0.0f, 0.0f);
glutSolidSphere(1.0, 40, 32);
}
glutSwapBuffers();
}
运行效果演示:
在debug–x86下编译
下载源程序