zoukankan      html  css  js  c++  java
  • 第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);                            // 退出程序
    }

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

  • 相关阅读:
    axios+post获取并下载后台返回的二进制流
    vue+ckEditor5
    金额大写转换(改进版)
    vue+axios请求头封装
    移动端h5+vue失焦搜索,ios和android兼容问题
    vue滚动+滑动删除标记(移动端)仿qq/微信
    重置 centos 7 密码
    发现好玩的——github + git 有意思的用法
    github 中使用 issues
    java代理模式与装饰模式
  • 原文地址:https://www.cnblogs.com/fish7/p/4007015.html
Copyright © 2011-2022 走看看