程序员爱装B

写装A程序 做装C的事情

[搬家文] 第四课 光照

原文地址:http://www.videotutorialsrock.com/opengl_tutorial/lighting/home.php

视频下载:http://www.videotutorialsrock.com/opengl_tutorial/lighting/video.flv

文本格式:http://www.videotutorialsrock.com/opengl_tutorial/lighting/text.php

代码下载:http://www.videotutorialsrock.com/opengl_tutorial/lighting/lighting.zip

OpenGL中的光照

    让我们的场景变的更酷的一种办法就是给他们加上光照,我们基于前一课的代码重新写一个新的。我们将创造一个没有上盖和底部的盒状物体。

    看下源代码。第一个新东西是在initRendering函数中调用的glEnable(GL_LIGHTING)。这个函数激活光照功能。同时我们也可以使用glDisable(GL_LIGHTING)禁用光照功能。之后我们调用glEnable(GL_LIGHT0)和glEnable(GL_LIGHT1)创造两个光源:0和1。(可以分别调用glDisable(GL_LIGHT0)和glDisable(GL_LIGHT1)分别禁用两个光源)。如果需要更多光源可以使用GL_LIGHT2,GL_LIGHT3等。OpenGL至少确保有8个光源可以用。然后调用glEnable(GL_NORMALIZE),具体功能后面提到。

    现在看看drawScene函数。

    //Add ambient light
GLfloat ambientColor[] = {0.2f, 0.2f, 0.2f, 1.0f};
//Color(0.2, 0.2, 0.2)
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientColor);

    首先,我们加一些环境光(ambient light)照射我们场景的每个面。环境光(ambient light)是一种我们可以在任何地方看到的光。在现实世界中并没有这样的光线,但是在计算机图形中模拟一个面完全没有照射非常困难,所以使用环境光(ambient light)来简化我们的场景。

    为了添加环境光,调用glLightModelfv函数,使用GL_LIGHT_MODEL_AMBIENT作为第一个参数,一个GLfloats组成的数组作为第二个参数。编译器会自动将floats转换为GLfloats。

    前三个浮点数表示光源的RGB强度。我们想加并不太强烈的白色背景光,因此我们使用了红,绿,蓝强度为0.2的背景光。注意这些值并不确定的表示一个颜色,他们表示光源的强度。背景光的强度可以是(2,2,2),甚至不是一个颜色。一个没有任何其他光源的强度为(1,1,1)的背景光的效果同我们上课的效果一样。第四个参数是1。

    //Add positioned light
GLfloat lightColor0[] = {0.5f, 0.5f, 0.5f, 1.0f};
//Color (0.5, 0.5, 0.5)
GLfloat lightPos0[] = {4.0f, 0.0f, 8.0f, 1.0f};
//Positioned at (4, 0, 8)
glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor0);
glLightfv(GL_LIGHT0, GL_POSITION, lightPos0);

    这里,我们加了一个光源。我们调用glLightfv(GL_LIGHT0,GL_DIFFUSE,lightColor0)设置光源的颜色/强度。我们想让物体有一点点的强度,因此使用强度值(0.5f,0.5f,0.5f),同样第四个参数为1。同时我们想将这个光源放在相对于当前变换坐标为(4,0,8)的地方,因此调用glLightfv(GL_LIGHT0,GL_POSITION,lightPos0)坐标为(4,0,8,1)。前三个元素是位置的坐标,最后一个为1。

    //Add directed light
GLfloat lightColor1[] = {0.5f, 0.2f, 0.2f, 1.0f};
    //Color (0.5, 0.2, 0.2)
//Coming from the direction (-1, 0.5, 0.5)
GLfloat lightPos1[] = {-1.0f, 0.5f, 0.5f, 0.0f};
glLightfv(GL_LIGHT1, GL_DIFFUSE, lightColor1);
glLightfv(GL_LIGHT1, GL_POSITION, lightPos1);

    现在设置第二个光源,我们将其变为红色,强度为(0.5,0.2,0.2)。和放在一个确定的地点不同,我们想使光源为方向光,这样可以照射这个方向上的整个场景。为了实现方向光,lightPos1中的最后一个参数设为0。这样前面的三个参数不表示一个光源的位置,而是相对当前变换状态的那个方向的光源照射。

    注意glLightfv不可以在一个glBegin-glEnd块中调用。一个基本法则是如果函数不必须写在一个glBegin-glEnd块中,那么就不要写在当中。

    这是drawScene的下面部分:

    glRotatef(_angle, 0.0f, 1.0f, 0.0f);
glColor3f(1.0f, 1.0f, 0.0f);
glBegin(GL_QUADS);

//Front
glNormal3f(0.0f, 0.0f, 1.0f);
glVertex3f(-1.5f, -1.0f, 1.5f);
glVertex3f(1.5f, -1.0f, 1.5f);
glVertex3f(1.5f, 1.0f, 1.5f);
glVertex3f(-1.5f, 1.0f, 1.5f);

//Right
glNormal3f(1.0f, 0.0f, 0.0f);
glVertex3f(1.5f, -1.0f, -1.5f);
glVertex3f(1.5f, 1.0f, -1.5f);
glVertex3f(1.5f, 1.0f, 1.5f);
glVertex3f(1.5f, -1.0f, 1.5f);

//Back
glNormal3f(0.0f, 0.0f, -1.0f);
glVertex3f(-1.5f, -1.0f, -1.5f);
glVertex3f(-1.5f, 1.0f, -1.5f);
glVertex3f(1.5f, 1.0f, -1.5f);
glVertex3f(1.5f, -1.0f, -1.5f);

//Left
glNormal3f(-1.0f, 0.0f, 0.0f);
glVertex3f(-1.5f, -1.0f, -1.5f);
glVertex3f(-1.5f, -1.0f, 1.5f);
glVertex3f(-1.5f, 1.0f, 1.5f);
glVertex3f(-1.5f, 1.0f, -1.5f);

glEnd();

    我们加入一个特殊的函数调用告诉OpenGL在我们场景中不同形状的标准(normal)。一个面的标准(normal)是垂直于这个面的向量(法向量)。OpenGL需要知道法向量来计算一个光源照射一个面上的角度。OpenGL不自己计算这个面的法向量的原因是提前计算出会提高速度。现在还不允许我们将阴影平缓,本课后面将实现。

    这个例子中,我们画的第一个面是平行于x-y平面的。这个面和z轴垂直(正交),因此法向量为(0,0,1)。我们在指出这个面的坐标之前调用glNormal3f(0.0f,0.0f,1.0f)告诉OpenGL这个面的法向量。注意法向量必须指向面的外面,因为如果一束光同一个物体面对的那个方向是同方向的话,那这个物体并不能照射。无论如何,对于封闭的表面就这样处理的。光线会在到达这个面之前找到表面的其他部分。

    在initRendering中我们调用了glEnable(GL_NORMALIZE)函数。这使得OpenGL自动归一化我们的法向量,这是OpenGL使用法向量必须要使得向量长度为1。我们可以自己做这个工作,但是像glScalef这样的函数会影响我们的工作。这降在下课中详细介绍。

    现在程序看起来像这样:


    我们的程序是一个盒子,有一个环绕盒子的镜头。注意我们有一个面是偏红的,因为这个面接受大多数的红色光;一个面是明亮的黄色;一个面是偏暗的黄色,这一面除了背景光没有其他光线照射了。最后一个面如果没有任何背景光照射的话将完全是黑色的。

强大的阴影平滑(smooth shading)

    这里有一个重要的概念,大多数时候,一组多边形需要模拟一个平滑的物体,比如半球。这种情况下,我们需要这个面的阴影平滑,看下面的例子:


    两个图片有同样的多边形,两者都想达到同样的效果。但是左边的没有使用阴影平滑,所以看上去不太平滑;右边的就看上去平滑多了更像一个半球了。注意到右边的多边形还有锯齿装的轮廓,两者实质上仍然是相同的形状。

    阴影平滑是如何实现的呢?我们为每个顶点指定一个不同的法向量,这个法向量和球面在那个顶点的法向量是相同的。然后我们告诉OpenGL应用阴影平滑,当OpenGL绘制一个三角形的时候,取这个顶点的所有法向量的加权均值来决定三角形不同点上的法向量。因为阴影平滑在显卡中处理很快,比增加多边形要快的多,可以通过这个办法我们可以在一定的时间内画出更加好看的形状。正如同你看到的这样,阴影平滑是一个非常强大的工具。

OpenGL中的阴影平滑

    现在我们可以说我们的四个墙(边)逼近一个圆。当然了,这是一个非常差的比较(除非你喝醉了!),但是我们仍然尽力来逼近。首先,在initRendering函数中删掉glShadeModel(GL_SMOOTH)这一行前面的注释,激活阴影平滑这个功能。(如果想要禁用阴影平滑,我们可以调用glShadeModel(GL_FLAT).)删掉drawScene函数中glNormal3f注释,并注释掉这里的glNormal3f函数

    //Front
//glNormal3f(0.0f, 0.0f, 1.0f);
glNormal3f(-1.0f, 0.0f, 1.0f);
glVertex3f(-1.5f, -1.0f, 1.5f);
glNormal3f(1.0f, 0.0f, 1.0f);
glVertex3f(1.5f, -1.0f, 1.5f);
glNormal3f(1.0f, 0.0f, 1.0f);
glVertex3f(1.5f, 1.0f, 1.5f);
glNormal3f(-1.0f, 0.0f, 1.0f);
glVertex3f(-1.5f, 1.0f, 1.5f);

//Right
//glNormal3f(1.0f, 0.0f, 0.0f);
glNormal3f(1.0f, 0.0f, -1.0f);
glVertex3f(1.5f, -1.0f, -1.5f);
glNormal3f(1.0f, 0.0f, -1.0f);
glVertex3f(1.5f, 1.0f, -1.5f);
glNormal3f(1.0f, 0.0f, 1.0f);
glVertex3f(1.5f, 1.0f, 1.5f);
glNormal3f(1.0f, 0.0f, 1.0f);
glVertex3f(1.5f, -1.0f, 1.5f);

//Back
//glNormal3f(0.0f, 0.0f, -1.0f);
glNormal3f(-1.0f, 0.0f, -1.0f);
glVertex3f(-1.5f, -1.0f, -1.5f);
glNormal3f(-1.0f, 0.0f, -1.0f);
glVertex3f(-1.5f, 1.0f, -1.5f);
glNormal3f(1.0f, 0.0f, -1.0f);
glVertex3f(1.5f, 1.0f, -1.5f);
glNormal3f(1.0f, 0.0f, -1.0f);
glVertex3f(1.5f, -1.0f, -1.5f);

//Left
//glNormal3f(-1.0f, 0.0f, 0.0f);
glNormal3f(-1.0f, 0.0f, -1.0f);
glVertex3f(-1.5f, -1.0f, -1.5f);
glNormal3f(-1.0f, 0.0f, 1.0f);
glVertex3f(-1.5f, -1.0f, 1.5f);
glNormal3f(-1.0f, 0.0f, 1.0f);
glVertex3f(-1.5f, 1.0f, 1.5f);
glNormal3f(-1.0f, 0.0f, -1.0f);
glVertex3f(-1.5f, 1.0f, -1.5f);

glEnd();

    这可以让每个顶点的所有法向量和我们想去逼近的圆圈中“真实”的法向量相同。注意我们总是在调用glVertex3f函数之前调用glNormal3f函数。现在我们的程序像下面这样:


    好看吧?这就是OpenGL中颜色的基本操作了。

练习:

  • 使用'l'键来控制光源的开与关。(需要检查key == 'l')
  • 不添加其他的时钟,使用时钟加入第三个光源,绿色,在(-6,0,0)和(6,0,0)来回移动。

posted on 2010-07-19 20:25 camel 阅读(429) 评论(0)  编辑 收藏 引用


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


导航

<2024年9月>
25262728293031
1234567
891011121314
15161718192021
22232425262728
293012345

统计

常用链接

留言簿

随笔档案

文章档案

搜索

最新评论

阅读排行榜

评论排行榜