首页 > 代码库 > 第07课 OpenGL 光照和键盘(2)

第07课 OpenGL 光照和键盘(2)

下一段代码绘制贴图立方体。我只对新增的代码进行注解。如果您对没有注解的代码有疑问,回头看看第六课。

int DrawGLScene(GLvoid)                                // 从这里开始进行所有的绘制{    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);            // 清除屏幕和深度缓存    glLoadIdentity();                            // 重置当前的模型观察矩阵

下三行代码放置并旋转贴图立方体。glTranslatef(0.0f,0.0f,z)将立方体沿着Z轴移动Z单位。glRotatef(xrot,1.0f,0.0f,0.0f)将立方体绕X轴旋转xrot。glRotatef(yrot,0.0f,1.0f,0.0f)将立方体绕Y轴旋转yrot。

    glTranslatef(0.0f,0.0f,z);                        // 移入/移出屏幕 z 个单位    glRotatef(xrot,1.0f,0.0f,0.0f);                        // 绕X轴旋转    glRotatef(yrot,0.0f,1.0f,0.0f);                        // 绕Y轴旋转

下一行与我们在第六课中的类似。有所不同的是,这次我们绑定的纹理是texture[filter],而不是上一课中的texture[0]。任何时候,我们按下F键,filter 的值就会增加。如果这个数值大于2,变量filter 将被重置为0。程序初始时,变量filter 的值也将设为0。使用变量filter 我们就可以选择三种纹理中的任意一种。

    glBindTexture(GL_TEXTURE_2D, texture[filter]);                // 选择由filter决定的纹理    glBegin(GL_QUADS);                            // 开始绘制四边形

glNormal3f是这一课的新东西。Normal就是法线的意思,所谓法线是指经过面(多边形)上的一点且垂直于这个面(多边形)的直线。使用光源的时候必须指定一条法线。法线告诉OpenGL这个多边形的朝向,并指明多边形的正面和背面。如果没有指定法线,什么怪事情都可能发生:不该照亮的面被照亮了,多边形的背面也被照亮....。对了,法线应该指向多边形的外侧。

看着木箱的前面您会注意到法线与Z轴正向同向。这意味着法线正指向观察者-您自己。这正是我们所希望的。对于木箱的背面,也正如我们所要的,法线背对着观察者。如果立方体沿着X或Y轴转个180度的话,前侧面的法线仍然朝着观察者,背面的法线也还是背对着观察者。换句话说,不管是哪个面,只要它朝着观察者这个面的法线就指向观察者。由于光源紧邻观察者,任何时候法线对着观察者时,这个面就会被照亮。并且法线越朝着光源,就显得越亮一些。如果您把观察点放到立方体内部,你就会法线里面一片漆黑。因为法线是向外指的。如果立方体内部没有光源的话,当然是一片漆黑。

        // 前侧面        glNormal3f( 0.0f, 0.0f, 1.0f);                    // 法线指向观察者        glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);            glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);            glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);            glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);            // 后侧面        glNormal3f( 0.0f, 0.0f,-1.0f);                    // 法线背向观察者        glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);            glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);            glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);            glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);            // 顶面        glNormal3f( 0.0f, 1.0f, 0.0f);                    // 法线向上        glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);            glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,  1.0f,  1.0f);            glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,  1.0f,  1.0f);            glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);            // 底面        glNormal3f( 0.0f,-1.0f, 0.0f);                    // 法线朝下        glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);            glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);            glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);            glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);            // 右侧面        glNormal3f( 1.0f, 0.0f, 0.0f);                    // 法线朝右        glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);            glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);            glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);            glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);            // 左侧面        glNormal3f(-1.0f, 0.0f, 0.0f);                    // 法线朝左        glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);            glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);            glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);            glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);        glEnd();                                // 四边形绘制结束

下两行代码将xot和yrot的旋转值分别增加xspeed和yspeed个单位。xspeed和yspeed的值越大,立方体转得就越快。

    xrot+=xspeed;                                // xrot 增加 xspeed 单位    yrot+=yspeed;                                // yrot 增加 yspeed 单位    return TRUE;    }

现在转入WinMain()主函数。我们将在这里增加开关光源、旋转木箱、切换过滤方式以及将木箱移近移远的控制代码。在接近WinMain()函数结束的地方你会看到SwapBuffers(hDC)这行代码。然后就在这一行后面添加如下的代码。
代码将检查L键是否按下过。如果L键已按下,但lp的值不是false的话,意味着L键还没有松开,这时什么都不会发生。

                SwapBuffers(hDC);                // 交换缓存                if (keys[L] && !lp)                // L 键已按下并且松开了?                {

如果lp的值是false的话,意味着L键还没按下,或者已经松开了,接着lp将被设为TRUE。同时检查这两个条件的原因是为了防止L键被按住后,这段代码被反复执行,并导致窗体不停闪烁。
lp设为true之后,计算机就知道L键按过了,我们则据此可以切换光源的开/关:布尔变量light控制光源的开关。

                    lp=TRUE;                // lp 设为 TRUE                    light=!light;                // 切换光源的 TRUE/FALSE

Now we check to see what light ended up being. The first line translated to english means: If light equals false. So if you put it all together, the lines do the following: If light equals false, disable lighting. This turns all lighting off. The command ‘else‘ translates to: if it wasn‘t false. So if light wasn‘t false, it must have been true, so we turn lighting on.

                    if (!light)                // 如果没有光源                    {                        glDisable(GL_LIGHTING);        // 禁用光源                    }                    else                    // 否则                    {                        glEnable(GL_LIGHTING);        // 启用光源                    }                }

下面的代码查看是否松开了"L"键。如果松开,变量lp将设为false。这意味着"L"键没有按下。如果不作此检查,光源第一次打开之后,就无法再关掉了。计算机会以为"L"键一直按着呢。

                if (!keys[L])                    // L键松开了么?                {                    lp=FALSE;                // 若是,则将lp设为FALSE                }

然后对"F"键作相似的检查。如果有按下"F"键并且"F"键没有处于按着的状态或者它就从没有按下过,将变量fp设为true。这意味着这个键正被按着呢。接着将filter变量加一。如果filter变量大于2(因为这里我们的使用的数组是texture[3],大于2的纹理不存在),我们重置filter变量为0。

                if (keys[F] && !fp)                // F键按下了么?                {                    fp=TRUE;                // fp 设为 TRUE                    filter+=1;                // filter的值加一                    if (filter>2)                // 大于2了么?                    {                        filter=0;            // 若是重置为0                    }                }                if (!keys[F])                    // F键放开了么?                {                    fp=FALSE;                // 若是fp设为FALSE                }

这四行检查是否按下了PageUp键。若是的话,减少z变量的值。这样DrawGLScene函数中包含的glTranslatef(0.0f,0.0f,z)调用将使木箱离观察者更远一点。

                if (keys[VK_PRIOR])                // PageUp按下了?                {                    z-=0.02f;                // 若按下,将木箱移向屏幕内部                }

接着四行检查PageDown键是否按下,若是的话,增加z变量的值。这样DrawGLScene函数中包含的glTranslatef(0.0f,0.0f,z)调用将使木箱向着观察者移近一点。

                if (keys[VK_NEXT])                // PageDown按下了么                {                    z+=0.02f;                // 若按下的话,将木箱移向观察者                }

现在检查方向键。按下左右方向键xspeed相应减少或增加。按下上下方向键yspeed相应减少或增加。记住在以后的教程中如果xspeed、yspeed的值增加的话,立方体就转的更快。如果一直按着某个方向键,立方体会在那个方向上转的越快。

                if (keys[VK_UP])                // Up方向键按下了么?                {                    xspeed-=0.01f;                // 若是,减少xspeed                }                if (keys[VK_DOWN])                // Down方向键按下了么?                {                    xspeed+=0.01f;                // 若是,增加xspeed                }                if (keys[VK_RIGHT])                // Right方向键按下了么?                {                    yspeed+=0.01f;                // 若是,增加yspeed                }                if (keys[VK_LEFT])                // Left方向键按下了么?                {                    yspeed-=0.01f;                // 若是, 减少yspeed                }

像前几课一样,我们最后还需要更正窗体的标题。

                if (keys[VK_F1])                // F1按下了么?                {                    keys[VK_F1]=FALSE;            // 若是将其设为FALSE                    KillGLWindow();                // 销毁当前窗口                    fullscreen=!fullscreen;            // 切换全屏/窗口模式                    // 重建GL窗口                    if (!CreateGLWindow("NeHe‘s Textures, Lighting & Keyboard tutorial",640,480,16,fullscreen))                    {                        return 0;            // 若无法创建窗口,程序退出                    }                }            }        }    }    // 关闭    KillGLWindow();                                // 销毁窗口    return (msg.wParam);                            // 退出程序}

 

这一课完了之后,您应该学会创建和使用这三种不同的纹理映射过滤方式。并使用键盘和场景中的对象交互。最后,您应该学会在场景中应用简单的光源,使得场景看起来更逼真。

 

第07课 OpenGL 光照和键盘(2)