hyde
路人甲
路人甲
  • 注册日期2003-09-24
  • 发帖数555
  • QQ
  • 铜币1457枚
  • 威望0点
  • 贡献值0点
  • 银元0个
阅读:1315回复:0

OpenGL系列讲座(11)

楼主#
更多 发布于:2004-03-20 10:37
第七章  OpenGL光照
本章是全篇中最重要的一部分,前几章只是基础,在这章里将着重讲述光照模型、明暗

处理、光源设置、材质定义以及有关的计算机图形学概念等内容,介绍如何运用OpenGL函数

来绘制真实感图形,充分展示三维图形世界的魅力。如果读者以前从未接触过这方面的概念,

要直接应用OpenGL函数库编写三维图形程序,是很困难的。因此,我们将结合OpenGL的特

殊性简洁明了地介绍一些有关真实感图形绘制的概念,帮助读者理解基本的OpenGL光照、材

质等函数的应用方式,顺利掌握最基本的真实感图形编程方法,引导读者快速进入OpenGL

三维图形世界。

 

7.1 真实感图形基本概念
真实感图形绘制是计算机图形学的一个重要组成部分,它综合利用数学、物理学、计算

机科学和其它科学知识在计算机图形设备上生成象彩色照片那样的具有真实感的图形。一般

说来,用计算机在图形设备上生成真实感图形必须完成以下四个步骤:一是用建模,即用一

定的数学方法建立所需三维场景的几何描述,场景的几何描述直接影响图形的复杂性和图形

绘制的计算耗费;二是将三维几何模型经过一定变换转为二维平面透视投影图;三是确定场

景中所有可见面,运用隐藏面消隐算法将视域外或被遮挡住的不可见面消去;四是计算场景

中可见面的颜色,即根据基于光学物理的光照模型计算可见面投射到观察者眼中的光亮度大

小和颜色分量,并将它转换成适合图形设备的颜色值,从而确定投影画面上每一象素的颜色,

最终生成图形。

由于真实感图形是通过景物表面的颜色和明暗色调来表现景物的几何形状、空间位置以

及表面材料的,而一个物体表面所呈现的颜色是由表面向视线方向辐射的光能决定的。在计

算机图形学中,常采用一个既能表示光能大小又能表示其颜色组成的物理量即光亮度

(luminance)或光强(intensity of light)来描述物体表面朝某方向辐射光能的颜色。采用这

个物理量可以正确描述光在物体表面的反射、透射和吸收现象,因而可以正确计算处物体表

面在空间给定方向上的光能颜色。

物体表面向空间给定方向辐射的光强可应用光照模型进行计算。简单的光照模型通常假

定物体表面是光滑的且由理想材料构成,因此只考虑光源照射在物体表面产生的反射光,所

生成的图形可以模拟处不透明物体表面的明暗过渡,具有一定的真实感效果。复杂的光照模

型除了考虑上述因素外,还要考虑周围环境的光对物体表面的影响。如光亮平滑的物体表面

会将环境中其它物体映像在表面上,而通过透明物体也可看到其后的环境景象。这类光照模

型称为整体光照模型,它能模拟出镜面映像、透明等较精致的光照效果。为了更真实的绘制

图形,还要考虑物体表面的细节纹理,这通常使用一种称为“纹理映射”(texture mapping)

的技术把已有的平面花纹图案映射到物体表面上,并在应用光照模型时将这些花纹的颜色考

虑进去,物体表面细节的模拟使绘制的图形更接近自然景物。

以上内容中,真实感图形绘制的四大步骤前两步在第四、五章已经详细介绍过,这里不

再重复,第三步OpenGL将自动完成所有消隐过程,第四步下面几节详述。另外,部分复杂光

照模型应用将在提高篇中介绍。
7.2  光照模型
7.2.1 简单光照模型

 

当光照射到一个物体表面上时,会出现三种情形。首先,光可以通过物体表面向空间反

射, 产生反射光。其次,对于透明体,光可以穿透该物体并从另一端射出,产生透射光。最

后,部分光将被物体表面吸收而转换成热。在上述三部分光中,仅仅是透射光和反射光能够

进入人眼产生视觉效果。这里介绍的简单光照模型只考虑被照明物体表面的反射光影响,假

定物体表面光滑不透明且由理想材料构成,环境假设为由白光照明。

一般来说,反射光可以分成三个分量,即环境反射、漫反射和镜面反射。环境反射分量

假定入射光均匀地从周围环境入射至景物表面并等量地向各个方向反射出去,通常物体表面

还会受到从周围环境来的反射光(如来自地面、天空、墙壁等的反射光)的照射,这些光常

统称为环境光(Ambient Light);漫反射分量表示特定光源在景物表面的反射光中那些向空间

各方向均匀反射出去的光,这些光常称为漫射光(Diffuse Light);镜面反射光为朝一定方向

的反射光,如一个点光源照射一个金属球时会在球面上形成一块特别亮的区域,呈现所谓“高

光(Highlight)”,它是光源在金属球面上产生的镜面反射光(Specular Light)。对于较光滑

物体,其镜面反射光的高光区域小而亮;相反,粗糙表面的镜面反射光呈发散状态,其高光

区域大而不亮。

 

    下面先看一个简单的光照例子light0.c:

 

例 2-9  简单光照例程 light0.c

 

#include "glos.h"

 

#include <GL/gl.h>

#include <GL/glu.h>

#include <GL/glaux.h>

 

void myinit(void);

void CALLBACK myReshape(GLsizei w, GLsizei h);

void CALLBACK display(void);

 

void myinit(void)

{

    GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };

 

    glLightfv(GL_LIGHT0, GL_POSITION, light_position);

 

    glEnable(GL_LIGHTING);

    glEnable(GL_LIGHT0);

    glDepthFunc(GL_LESS);

    glEnable(GL_DEPTH_TEST);

}

 

void CALLBACK display(void)

{

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    auxSolidSphere(1.0);

    glFlush();

}

 

void CALLBACK myReshape(GLsizei w, GLsizei h)

{

    glViewport(0, 0, w, h);

    glMatrixMode(GL_PROJECTION);

    glLoadIdentity();

    if (w <= h)

    glOrtho (-1.5, 1.5, -1.5*(GLfloat)h/(GLfloat)w,

        1.5*(GLfloat)h/(GLfloat)w, -10.0, 10.0);

    else

    glOrtho (-1.5*(GLfloat)w/(GLfloat)h,

        1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0);

    glMatrixMode(GL_MODELVIEW);

    glLoadIdentity();

}

 

void main(void)

{

    auxInitDisplayMode (AUX_SINGLE | AUX_RGBA);

    auxInitPosition (0, 0, 500, 500);

    auxInitWindow ("Simple Lighting");

    myinit();

    auxReshapeFunc (myReshape);

    auxMainLoop(display);

}

 

   以上程序运行结果是显示一个具有灰色光影的球。其中函数myinit()中包含了关键的设

定光源位置、启动光照等几句,而其它程序语言几乎与以前的没有多大区别,但效果却完全

不一样。下面几个小节将详细介绍有关函数的用法。

 

 

  

 

图 2-7-1 带光影的灰色球体

 

 

7.2.2 OpenGL 光组成

 

在OpenGL简单光照模型中的几种光分为:辐射光(Emitted Light)、环境光(Ambient

Light)、漫射光(Diffuse Light)、镜面光(Specular Light)。

    辐射光是最简单的一种光,它直接从物体发出并且不受任何光源影响。

环境光是由光源发出经环境多次散射而无法确定其方向的光,即似乎来自所有方向。一

般说来,房间里的环境光成分要多些,户外的相反要少得多,因为大部分光按相同方向照射,

而且在户外很少有其他物体反射的光。当环境光照到曲面上时,它在各个方向上均等地发散

(类似于无影灯光)。

漫射光来自一个方向,它垂直于物体时比倾斜时更明亮。一旦它照射到物体上,则在各

个方向上均匀地发散出去。于是,无论视点在哪里它都一样亮。来自特定位置和特定方向的

任何光,都可能有散射成分。

镜面光来自特定方向并沿另一方向反射出去,一个平行激光束在高质量的镜面上产生

100%的镜面反射。光亮的金属和塑料具有很高非反射成分,而象粉笔和地毯等几乎没有反射

成分。因此,三某种意义上讲,物体的反射程度等同于其上的光强(或光亮度)。

 

7.2.3 创建光源(Light Source)

 

光源有许多特性,如颜色、位置、方向等。选择不同的特性值,则对应的光源作用在物

体上的效果也不一样,这在以后的章节中会逐步介绍的。下面详细讲述定义光源特性的函数

glLight*():

 

void glLight{if}[v](GLenum light , GLenum pname, TYPE param)

 

     创建具有某种特性的光源。其中第一个参数light指定所创建的光源号,如GL_LIGHT0、

GL_LIGHT1、...、GL_LIGHT7。第二个参数pname指定光源特性,这个参数的辅助信息见表

2-7-1所示。最后一个参数设置相应的光源特性值。

 

 

______________________________________________________________________________

   pname 参 数 名              缺 省 值            说      明

______________________________________________________________________________

 

GL_AMBIENT                 (0.0,0.0,0.0,1.0)     RGBA模式下环境光

GL_DIFFUSE                 (1.0,1.0,1.0,1.0)     RGBA模式下漫反射光

GL_SPECULAR                (1.0,1.0,1.0,1.0)     RGBA模式下镜面光

GL_POSITION                (0.0,0.0,1.0,0.0)     光源位置齐次坐标(x,y,z,w)

GL_SPOT_DIRECTION          (0.0,0.0,-1.0)        点光源聚光方向矢量(x,y,z)

GL_SPOT_EXPONENT            0.0                  点光源聚光指数

GL_SPOT_CUTOFF              180.0                点光源聚光截止角

GL_CONSTANT_ATTENUATION     1.0                  常数衰减因子

GL_LINER_ATTENUATION        0.0                  线性衰减因子

GL_QUADRATIC_ATTENUATION    0.0                  平方衰减因子

______________________________________________________________________________

 

                   表2-7-1  函数glLight*()参数pname说明

 

注意,以上列出的GL_DIFFUSE和GL_SPECULAR的缺省值只能用于GL_LIGHT0,其他几个

光源的GL_DIFFUSE和GL_SPECULAR缺省值为(0.0,0.0,0.0,1.0)。另外,表中后六个参数的

应用放在下一篇中介绍。

    在上面例程light0.c中,光源的创建为:

 

    GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };

    glLightfv(GL_LIGHT0, GL_POSITION, light_position);

 

其中light_position是一个指针,指向定义的光源位置齐次坐标数组。其它几个光源特

性都为缺省值。同样,我们也可用类似的方式定义光源的其他几个特性值,例如:

 

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

    GLfloat light_diffuse [] = { 1.0, 1.0, 1.0, 1.0 };

    GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };

    glLightfv(GL_LIGHT0, GL_AMBIENT , light_ambient );

    glLightfv(GL_LIGHT0, GL_DIFFUSE , light_diffuse );

    glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);

 

7.2.4 启动光照

 

在OpenGL中,必须明确指出光照是否有效或无效。如果光照无效,则只是简单地将当前

颜色映射到当前顶点上去,不进行法向、光源、材质等复杂计算,那么显示的图形就没有真

实感,如前几章例程运行结果显示。

    要使光照有效,首先得启动光照,即:

 

 glEnable(GL_LIGHTING);

 

若使光照无效,则调用 gDisable(GL_LIGHTING) 可关闭当前光照。然后,必须使所定义的每

个光源有效,例light0.c中只用了一个光源,即:

 

 glEnable(GL_LIGHT0);

 

其它光源类似,只是光源号不同而已。

 

 

 

 

 

 
喜欢0 评分0
夜落了,风静了,我喜欢一本书,一杯茶,一粒摇曳的烛光...
游客

返回顶部