大龙的博客

常用链接

统计

最新评论

opengl es 灯光

这一章将在你的程序中加入灯光,使场景看起来和真实场景一样。

灯光

opengl中灯光分为好几种,都可以加入到你的场景中。

Ambiend Light 环境光
环境光没有确切的来源方向,当环境光照射到物体时,光被反射到各个方向。

Diffuse Light 漫射光
漫射光不同于环境光,它来自某个方向,但和环境光一样,照射到物体时,会被反射到各个方向。

Specular Light 镜面光
镜面光和漫射光一样是有方向的,但它反射方向是一定的,而不像漫射光一样反射到各个方向。所以当镜面光照射到物体时,你会看到物体表面被照射的亮点。

Emissive Light 发射光
它来自于某一物体,该物体散发出大量的光,但不会被任何物体面反射。

为了更好的理解这几种光,我从网络上摘抄了一段定义:
    * 环境光——经过多次反射而来的光称为环境光,无法确定其最初的方向,但当特定的光源关闭后,它们将消失.
    * 全局环境光——它们并非来自特定的光源,这些光经过了多次散射,已经无法确定其光源位于何处.
    * 散射光——来自同一方向,照射到物体表面后,将沿各个方向均匀反射,因此,无论从哪个方向观察,表面的亮度都相同.
    * 镜面反射光——来自特定方向,也被反射到特定方向.镜面反射度与之相关.
    * 材质发射光——用于模拟发光物体.在OpenGL光照模型中,表面的发射光增加了物体的亮度,它不受光源的影响,另外,发射光不会给整个场景中增加光线.

材质

你不光可以设置光的属性,而且还可以指定不同的面对光照作出的反应,这就要指定材质属性。
这就指定了一个面对光源反射多少。

法线

法线是一个向量垂直于(90度)某一特定面,就称这个向量是某个面的法线。法线可以用于光的计算。如果你想让画出的物体对光源产生影响,那么必须指定物体每个面的法线。下面将会说明。

另一个需要注意的一点是,法线要单位化,我们不会深入数学计算,这不是我们这章的目的。如果需要会在将来某章中讲解。简明的说,一个向量的长度等于各个向量分量的平方和的平方根,再把每个向量的分量除以这个值。现在不需要过多担心这个。

程序代码:

下面定义两个颜色数组,一个用于环境光,一个用于漫射光,它们是光源的颜色值。
float lightAmbient[] = { 0.2f, 0.3f, 0.6f, 1.0f };
float lightDiffuse[] = { 0.2f, 0.3f, 0.6f, 1.0f };

下面创建一个材质属性数组,分别用于环境光和漫射光。
用材质属性值乘以光源值得出面的反射颜色值,下面的值将会导致面反射的光失去接收光的百分之四十。每个值表示特定颜色被反射的数量。
float matAmbient[] = { 0.6f, 0.6f, 0.6f, 1.0f };
float matDiffuse[] = { 0.6f, 0.6f, 0.6f, 1.0f };

void init()
{
首先先启用光源,这样光才会在场景中起作用。
glEnable(GL_LIGHTING);

opengl最多允许8个光源,要使用某个光源,需要使用glEnable打开它,光源的编号是GL_LIGHTX,X的值是0---7。
指定材质属性,可以使用glMaterialfv和glMaterialf ,glMaterialfv接受向量数组,而glMaterialf只接受一个向量。第一个参数指定那个面被更新,在opengl es中只可以使用GL_FRONT_AND_BACK,其他参数不起作用。之所以存在这个参数,是因为opengl可以设置多个参数。
第二个参数指定光源的类型,GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR, GL_EMISSION 和 GL_AMBIENT_AND_DIFFUSE. 
最后一个参数指定一个数组或单个值,取决于你使用的哪个函数。

下一行设置面的材质属性:
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, matAmbient);
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, matDiffuse);

灯光的设置和材质的设置相同,使用glLightfv或glLightf函数:
    glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse);

init函数没有发生改变:    
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClearDepthf(1.0f);

    glVertexPointer(3, GL_FLOAT, 0, box);
    glEnableClientState(GL_VERTEX_ARRAY);

    glEnable(GL_CULL_FACE);
    glShadeModel(GL_SMOOTH);
}

display函数的开头部分没有发生改变:
void display()
{
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   glLoadIdentity();

   gluLookAtf(
        0.0f, 0.0f, 3.0f,
        0.0f, 0.0f, 0.0f,
        0.0f, 1.0f, 0.0f);

   glRotatef(xrot, 1.0f, 0.0f, 0.0f);
   glRotatef(yrot, 0.0f, 1.0f, 0.0f);

前面我们讨论了法线,法线是垂直于面的,所以前平面的法线是(0, 0, 1),后平面的法线是(0, 0, -1),两个法线的长度为1,所以不用再单位化。
法线由glNormal3f 函数指定,并在渲染时调用。这个函数由3个float类型的数据组成单位化的向量。
   // FRONT AND BACK
   glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
   glNormal3f(0.0f, 0.0f, 1.0f);
   glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
   glNormal3f(0.0f, 0.0f, -1.0f);
   glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);
   
   其他页面设置也同上,

   // LEFT AND RIGHT
   glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
   glNormal3f(-1.0f, 0.0f, 0.0f);
   glDrawArrays(GL_TRIANGLE_STRIP, 8, 4);
   glNormal3f(1.0f, 0.0f, 0.0f);
   glDrawArrays(GL_TRIANGLE_STRIP, 12, 4);

   // TOP AND BOTTOM
   glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
   glNormal3f(0.0f, 1.0f, 0.0f);
   glDrawArrays(GL_TRIANGLE_STRIP, 16, 4);
   glNormal3f(0.0f, -1.0f, 0.0f);
   glDrawArrays(GL_TRIANGLE_STRIP, 20, 4);

   glFlush();
   glutSwapBuffers();
}

最后菜单增加一项彩色材质,这项选择打开或关闭色彩跟踪。色彩跟踪根据当前面的色彩反色不同色的光。

    case 2 : 
        if (glIsEnabled(GL_COLOR_MATERIAL))
            glDisable(GL_COLOR_MATERIAL);
        else
            glEnable(GL_COLOR_MATERIAL);
        break;

下面两张图是程序的运行结果,分别是普通灯光和色彩追踪的效果。
   普通灯光         色彩跟踪
15-Lighting.gif

   15-Lighting01.jpg



现在学会了在场景中添加灯光,它提供了灵活的设置,使得场景更加真实。

posted on 2011-01-10 18:39 大龙 阅读(1808) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理