欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

OpenGL光源光照基础

程序员文章站 2022-07-04 12:02:59
...

光照模型

在OpenGL光照模型中,除非一个物体自己会发光,否则它将受到3种不同类型的光的照射:环境光(ambient)、散射光(diffuse)和镜面光(specular)。

  • 全局环境光
    在我们没有用到光照之前,我们也能够看到场景中绘制的物体,原因是每个场景都有这个全局环境光,它不来自任何特定的光源。
  • 环境光(ambient)
    环境光并不来自任何特定的方向。它来自某个光源,但光线却是在房间或场景中四处反射,没有方向可言。由环境光所照射的物体在所有方向的表面都是均匀照亮的。
  • 散射光(diffuse)
    散射光具有方向性,来自于一个特定的方向,它根据入射光线的角度在表面上均匀地反射开来。
  • 镜面光(specular)
    镜面光具有很强的方向性,但它的反射角度很锐利,只沿一个特定的方向反射。

光源与材料

光源

OpenGL可以同时为我们提供8个有效的光源。也就是说,我们最多可以同时启用8个光源。它们分别是GL_LIGHT0,GL_LIGHT1,GL_LIGHT2 ……
其中,GL_LIGHT0是最特殊的一个光源,我们可以为GL_LIGHT0指定环境光成分。在默认情况下,GL_LIGHT0光源的颜色为白光,其他7个光源在默认情况下是没有颜色的,也即为黑色。

光源种类

  • 环境光
    环境光是一种无处不在的光。环境光源放出的光线被认为来自任何方向。因此,当你仅为场景指定环境光时,所有的物体无论法向量如何,都将表现为同样的明暗程度。
  • 点光源
    由这种光源放出的光线来自同一点,且方向辐射自四面八方。
  • 平行光
    平行光又称镜面光,这种光线是互相平行的。从手电筒、太阳等物体射出的光线都属于平行光。
  • 聚光灯
    这种光源的光线从一个锥体中射出,在被照射的物体上产生聚光的效果。使用这种光源需要指定光的射出方向以及锥体的顶角α。

设置光源

1.设置环境光成分

对于GL_LIGHT0,我们可以为其指定环境光成分。调用

glLightfv(GL_LIGHT0,GL_AMBIENT,@AmbientLight);

在上述函数调用中,第一个参数表示我们要对GL_LIGHT0进行设置;
第二个参数表示我们要设置的是环境光成分;
第三个参数则是一个数组,它有4个值,分别表示光源中含有红、绿、蓝三种光线的成分,一般情况下都为1;
第四个参数为透明度值,一般也为1。
完整的代码是这样的:

void SetLight
{
    int AmbientLight[4]={1,1,1,1};
    glLightfv(GL_LIGHT0,GL_AMBIENT,@AmbientLight);
    glEnable(GL_LIGHT0);       //开启GL_LIGHT0光源
    glEnable(GL_LIGHTING);     //开启光照系统
}  

2.设置漫射光成分

通过对漫射光成分的设置,我们可以产生一个点光源。方法和设置环境光成分相似,只需调用

 glLightfv(GL_LIGHT0,GL_DIFFUSE,@DiffuseLight);

其中DiffuseLight是漫射光的颜色成分。可以根据不同需要指定不同的颜色,一般情况下也为(1,1,1,1)。

3.设置镜面光成分

通过对镜面光成分的设置,我们可以产生一个平行光源。方法和设置漫射光成分相似,只需调用

glLightfv(GL_LIGHT0,GL_SPECULAR,@SpecularLight);

其中SpecularLight是镜面反射光的颜色成分。可以根据不同需要指定不同的颜色。

4.设置光源的位置

对于点光源和平行光源,我们常常需要指定光源的位置来产生需要的效果。方法仍然是调用glLightfv函数,仅仅是换换参数而已:

glLightfv(GL_LIGHT0,GL_POSITION,@LightPosition);

其中,LightPosition也是一个四维数组,四维数组的前3项依次为光源位置的X,Y,Z分量。
第四个值很特殊,一般为1或0。当LightPosition[4]=0的时候,表示光源位于距离场景无限远的地方,相当于平行光;而当LightPosition[4]=1时,光源的位置就是前三项所指定的位置,也就是成为了点光源。

材料

材料是针对场景中的物体而言的,但并不是指现实生活中构成物体的那种有质感材料,这里的材料只与物体的颜色有关(你可能会想到怎样表现类似金属、玻璃等物质质感,除了要使用光照系统并为它们指定合适的材质外,还要使用纹理贴图来表现)。OpenGL用材料对光的红、绿、蓝三原色的反射率来近似定义材料的颜色。当一束光照到物体上时,光本身有颜色(可以自己指定。通常情况下的白光是RGB三色光的混合),光照到物体上时,物体的材料会根据自身对光中的各种成分的反射参数决定反射哪种光,也就是物体所呈现出的颜色啦。所以并不是什么颜色的光照过来,物体就会呈现出什么颜色的光。

设置材料

材质的设置与光源的设置类似。其函数为:

void glMaterial{if}[v](GLenum face,GLenum pname,TYPE param);

face可以是GL_FRONT、GL_BACK、GL_FRONT_AND_BACK,它表明当前材质应该应用到物体的哪一个面上;
pname说明一个特定的材质;
param是材质的具体数值,若函数为向量形式,则param是一组值的指针,反之为参数值本身。非向量形式仅用于设置GL_SHINESS。pname参数值具体内容见下表。另外,参数GL_AMBIENT_AND_DIFFUSE表示可以用相同的RGB值设置环境光颜色和漫反射光颜色。

![](http://image-for-blog.qiniudn.com/屏幕快照 2015-03-13 下午12.15.49.png)
完整代码如下:

void SetMaterialAndLight
{
   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 };  //最后一个参数为0表示该光源是directional的
 //GLfloat light_position[] = { 1.0, 1.0, 1.0, 1.0 };  //最后一个参数非0表示该光源是positional的

   GLfloat light_ambient[] = { 0.0, 1.0, 0.0, 1.0 };
   GLfloat light_diffuse[] = { 0.0, 1.0, 0.0, 1.0 };
   GLfloat light_specular[] = { 0.0, 1.0, 0.0, 1.0 };

   glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
   glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);

   glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
   glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
   glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
   glLightfv(GL_LIGHT0, GL_POSITION, light_position);

   glEnable(GL_LIGHT0);       //开启GL_LIGHT0光源
   glEnable(GL_LIGHTING);     //开启光照系统
}  

颜色追踪

在没有学习光照之前,我们是通过glColor()函数来指定物体颜色的,前面已经说过,这时候我们能看到我们所设置的物体颜色是因为有全局环境光的原因。当开启光照功能后,如果用上述的设置材料属性的方法进行设置,则原来设置的颜色与被光照到后物体呈现的颜色毫无关系,即原来的 glColor*()命令失去原有的作用。
但还有一种方法设置材料属性的方法,称为颜色追踪,即用颜色指定代替材料属性指定。使用颜色追踪,可以告诉OpenGL仅仅通过调用glColor来设置材料属性。为了启用颜色追踪,需要以GL_COLOR_MATERIAL为参数调用glEnable。接着,glColorMaterial函数根据glColor所设置的值来指定材料参数。

glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT,GL_AMBIENT_AND_DIFFUSE);

如果调用了 glEnable(GL_COLOR_MATERIAL),那么就会使光照模型中的几种光根据glColor*()中的指定确定颜色;
void glColorMaterial(GLenum face, GLenum mode);
– face的取值GL_FRONT, GL_BACK与GL_FRONT_AND_BACK(默认值)
– mode的取值为GL_EMISSION, GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR与 GL_AMBIENT_AND_DIFFUSE(默认值)
例如:

glEnable(GL_COLOR_MATERIAL); 
glColorMaterial(GL_FRONT, GL_DIFFUSE); /* now glColor* changes diffuse reflection */ 
glColor3f(0.2, 0.5, 0.8);

/* draw some objects here */ 

glColorMaterial(GL_FRONT, GL_SPECULAR); /* now glColor* changes specular reflection */
glColor3f(0.9, 0.0, 0.2);

/* draw other objects here */ 
glDisable(GL_COLOR_MATERIAL); 

另外,不需要使用glColorMaterial()时,确保禁用。

材质RGB值和光源RGB值的关系

材质的颜色与光源的颜色有些不同。对于光源,R、G、B值等于R、G、B对其最大强度的百分比。若光源颜色的R、G、B值都是1.0,则是最强的白光;若值变为0.5,颜色仍为白色,但强度为原来的一半,于是表现为灰色;若R=G=1.0,B=0.0,则光源为黄色。对于材质,R、G、B值为材质对光的R、G、B成分的反射率。比如,一种材质的R=1.0,G=0.5,B=0.0,则材质反射全部的红色成分,一半的绿色成分,不反射蓝色成分。也就是说,若OpenGL的光源颜色为(LR,LG,LB),材质颜色为(MR,MG,MB),那么,在忽略所有其他反射效果的情况下,最终到达眼睛的光的颜色为(LR*MR,LG*MG,LB*MB)。同样,如果有两束光,相应的值分别为(R1,G1,B1)和(R2,G2,B2),则OpenGL将各个颜色成分相加,得到(R1+R2,G1+G2,B1+B2),若任一成分的和值大于1(超出了设备所能显示的亮度)则约简到1.0。

OpenGL 光照模效果的原理

OpenGL的光照模型是用来模拟现实生活中的光照的。它根据顶点的法线向量光源的位置决定顶点的明暗程度,根据顶点的材质光源中三原色的成分来决定物体将表现出怎样的颜色

使用OpenGL的光照模型的步骤:

  • 设置光源的种类、位置和方向(对于平行光源)
  1. 为每个图元的每个顶点指定它的法线向量
  2. 为各个图元指定它的材料

步骤1和步骤3上面已经说过了,下面解释一下步骤2。
众所周知,光线是根据物体表面法向来确定其反射方向的,所以想要对开启光照效果就要对物体表面的每个图元指定法向。如果场景中的物体是多边形网格模型,则需要自己计算每个网格的法向量然后再调用glNormal*()为每个网格指定法线向量。当然,如果你用的是类似glutSolidTorus()或者glutSolidSphere()这种GLUT工具包中的函数则不用自己指定图元的法向量。

几个例子

我在github上传了几个例子可以方便理解->我是栗子