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

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)的更多相关文章

  1. 第07课 OpenGL 光照和键盘(1)

    光照和键盘控制: 在这一课里,我们将添加光照和键盘控制,它让程序看起来更美观. 这一课我会教您如何使用三种不同的纹理滤波方式.教您如何使用键盘来移动场景中的对象,还会教您在OpenGL场景中应用简单的 ...

  2. NeHe OpenGL教程 第七课:光照和键盘

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  3. OpenGL光照3:光源

    本文是个人学习记录,学习建议看教程 https://learnopengl-cn.github.io/ 非常感谢原作者JoeyDeVries和多为中文翻译者提供的优质教程 的内容为插入注释,可以先跳过 ...

  4. OpenGL光照和颜色

    OpenGL光照和颜色 转自:http://www.cnblogs.com/kekec/archive/2011/08/16/2140789.html OpenGL场景中模型颜色的产生,大致为如下的流 ...

  5. OpenGL光照测试

    OpenGL光照测试 花了大概半个月,研究了OpenGL的光照.请注意是固定管线渲染的光照,如果使用着色器的高手们请飘过.这个程序是通过光照对模型的照射,来研究OpenGL光照的性质.以后可以通过这个 ...

  6. 浅析OpenGL光照

    浅析OpenGL光照 之前从来都没有涉及光照的内容,心想只要能通过常规的方法渲染出几何体甚至是模型就可以了,然而没有光照的日子注定是苦涩的,因为仅凭几何体和模型的颜色无法达到真是渲染的效果,在实际中有 ...

  7. QT5 OpenGL (六, 键盘事件, 开关灯,放大缩小综合运用)

    概要 实例效果图 立体图放大图 立体图缩小图 不加矢量开灯图 不加矢量关灯图 加矢量关灯图1 加矢量关灯图2 部分代码展示 主要内容解析 QT键盘事件 立体图形的放大和缩小 上下左右键以及A键D争键控 ...

  8. 第12课 OpenGL 显示列表

    显示列表: 想知道如何加速你的OpenGL程序么?这一课将告诉你如何使用OpenGL的显示列表,它通过预编译OpenGL命令来加速你的程序,并可以为你省去很多重复的代码. 这次我将教你如何使用显示列表 ...

  9. 第09课 OpenGL 移动图像

    3D空间中移动图像: 你想知道如何在3D空间中移动物体,你想知道如何在屏幕上绘制一个图像,而让图像的背景色变为透明,你希望有一个简单的动画.这一课将教会你所有的一切.前面的课程涵盖了基础的OpenGL ...

随机推荐

  1. 最小生成树-普利姆(Prim)算法

    最小生成树-普利姆(Prim)算法 最小生成树 概念:将给出的所有点连接起来(即从一个点可到任意一个点),且连接路径之和最小的图叫最小生成树.最小生成树属于一种树形结构(树形结构是一种特殊的图),或者 ...

  2. 深入浅出 BPF TCP 拥塞算法实现原理

    本文地址:https://www.ebpf.top/post/ebpf_struct_ops 1. 前言 eBPF 的飞轮仍然在快速转动,自从 Linux 内核 5.6 版本支持 eBPF 程序修改 ...

  3. PHP的Hash信息摘要扩展框架

    今天我们主要学习的是 PHP 中一些 Hash 散列加密相关的扩展函数的使用,而不是 Hash 算法,这种加密其实也只是一种更复杂一些的密钥算法,与 Hash 算法类似的是,我们输入的一串字符串,就像 ...

  4. PHP的命令行扩展Readline相关函数学习

    PHP 作为一个 Web 开发语言,相对来说,命令行程序并不是它的主战场.所以很多年轻的 PHP 开发者可能连命令行脚本都没有写过,更别提交互式的命令操作了.而今天,我们带来的这个扩展就是针对 PHP ...

  5. nginx使用用户真实IP做hash(解决经过CND后ip_hash失效问题)

    在nginx中常用的有以下四种负载均衡的算法,分别是:round-robin.ip-hash.least-connected和weighted.当然在实际生产中或许使用最多的就是ip-hash了,一般 ...

  6. 探究java的intern方法

    本文主要解释java的intern方法的作用和原理,同时会解释一下经常问的String面试题. 首先先说一下结论,后面会实际操作,验证一下结论.intern方法在不同的Java版本中的实现是不一样的. ...

  7. P4201-[NOI2008]设计路线【结论,树形dp】

    正题 题目链接:https://www.luogu.com.cn/problem/P4201 题目大意 给出\(n\)个点的一棵树开始所有边都是白色,选出若干条没有公共点的路径将上面所有边变为黑色. ...

  8. Unittest 框架之断言,你学会了吗??

    unittest断言 Python在 unittest.TestCase 类中提供了很多断言方法.断言方法检查你认为应该满足的条件是否确实满足.如果该条件确实满足,你对程序行为的假设就得到了确认,你就 ...

  9. .NET跨平台实践:.NetCore、.Net5/6 Linux守护进程设计

    之前,我写过两篇关于用C#开发Linux守护进程的技术文章,分别是<.NET跨平台实践:用C#开发Linux守护进程>和<.NET跨平台实践:再谈用C#开发Linux守护进程 - 完 ...

  10. PaddlePaddle:在 Serverless 架构上十几行代码实现 OCR 能力

    ​ 飞桨 (PaddlePaddle) 以百度多年的深度学习技术研究和业务应用为基础,是中国首个自主研发.功能完备. 开源开放的产业级深度学习平台,集深度学习核心训练和推理框架.基础模型库.端到端开发 ...