OGL(教程17)——环境光

原文地址:http://ogldev.atspace.co.uk/www/tutorial17/tutorial17.html

背景知识:
光照在3D图形学中很重要。如果把光照加入到模型渲染中会增加场景的逼真程度。使用模拟,是因为它不能100%模拟现实世界。现实的光,是由大量的粒子称之为光子构成的,表现出粒子特性和波特性。如果你试图计算每个光子的影响,你的计算机无法做到这个。

因此,几个光照模型被开发出来,他们模拟光的核心效果,当光照照射到物体的时候。光照模型跟着计算机的升级而变得越来越复杂。在下一节教程开始,我们将会学习基础的光照模型,他们很简答。但是对于所有的大气层中的场景都影响很大。

最基本的光照模型是Ambient/Diffuse/Specular。环境光是光照的一种类型,当你走出室内你就会看到环境光。即使是太阳落下了,它的光射线撞击到不同的物体,不同的物体就会被看到,即使是在影子里。由于光在每个它撞击到物体之间进行多次反射,他不是一条笔直的路径。甚一个灯泡在室内,看起来像是太阳的感觉。把它的环境的光向四周发射,如果房间足够大,每个东西都是均等的。环境光模拟的是没有起点、没有方向、对每个物体都有相同效果。

漫反射重点强调的是,光是从哪个角度照射到物体表面的,强调的是照射物体的亮度。当光和物体的一个面撞击,那么此面就比另外一个面亮一些。我们仅仅看到太阳传播环境光,没有特定的方向。但是,太阳也有漫反射属性。当他撞击到高的建筑物是,你会看到建筑物的一面比另外一面亮。漫反射光的最重要的属性是方向。

镜面光,是物体的另外一个属性,而不是光本身的。它使得物体某个部分看起来特别刺眼。这个还和视角的方向有关。金属物体经常有一些反射属性,比如一辆车在晴天会有边缘刺眼的效果。计算镜面反射的时候要考虑光的入射方向,以及观察者的观察方向。

在3D应用中,你清楚不需要创建环境光、漫反射、镜面反射。反而,你使用光源,比如户外的太阳,室内的灯泡,或者是岩洞中的聚光灯。这些光源类型会把环境光、漫反射、镜面反射的效果结合起来。比如,聚光灯,能够照射特定范围的物体,在范围之外的则不会照亮。

在接下来的教程中,我们开发几种有用的光源类型,学习基本的光照模型。

我们从平行光开始。一个平行光有方向,但是没有起点。这就意味着,所有的光射线都是平行的。光的方向用一个向量表示,所有的物体都是使用这个向量计算光,而不考虑起点位置。太阳就是平行光。如果你试图计算两个相邻的建筑和太阳光的夹角的话,两个角度是几乎相同的。因为太阳在150百万千米之外。因此,我们只考虑其方向。

平行光的另外一个重要的属性是,它的亮度和距离无关。这个亮度是一个常量值。而点光源,则是距离越远,其亮度越低。

下图展示了平行光:
在这里插入图片描述

我们已经看到太阳既有环境光也有漫反射属性。我们本节将介绍如何计算环境光,而漫反射下一节再计算。

上一节中我们介绍了如何从一个贴图中采样颜色。颜色又三个通道(RGB),每个通道都有一个字节。那么意味着,颜色范围在0到255之间。不同的颜色通道结合起来,就得到不同的颜色。都为0则为黑色,都为255则为白色。其他的东西都在黑白之间。可以对分量做等比缩放,其亮度根据缩放值变暗或者变亮。

当白光撞击到表面,表面就会反射白色。这个亮度或明或暗,取决于光源的亮度。但是反射都是同样的基础色。如果光源是纯红(255,0,0),那么反射的颜色只能是红色。这是因为光源没有绿色和蓝色用来反射。如果物体表面是纯蓝,那么结果是黑色。基础的原则是,光源只能反射物体的颜色,但是不能对其着色。

我们定义光源的颜色在[0,1]之间。把这个值乘以物体的颜色,就得到了反射的颜色。但是,我们需要考虑环境光强度。因此,环境光强度会被单做一个因子,用来乘以每个分量。这个就是最终颜色。等式如下:
在这里插入图片描述

在本节的教程中,你可以按住a和s键来增加和减少环境光的强度。这个只是平行光的环境光部分。这个在下一节的漫反射中会有所变化。目前,我们从任何角度看物体都是一样的。

环境光的效果大多不被使用。因为,它看起来很假,和过于简单,其实现不太符合实际。使用高级的方法,例如全局光照,因为灯光被物体反射又撞击其他的物体,这些间接光也会被考虑进去。我们不能完全避免环境光,因为要避免某个物体的表面完全黑的情况。因此在最后,可以通参数用来微调下。

代码注释:

我们的代码例子,变得越来越复杂。本节,为了实现环境光,我们做了很多的重构工作。我们把代码放在更合适的位置,最主要的变化是:

  1. 封装了shader的管理类。这个包含了编译和链接。从现在开始,我们会在在Technique的子类中实现效果。
  2. 移动GLUT的初始化和回调管理到GLUTBackend组件中。这个组件注册自身以及接受来自GLUT的回调,然后把他们转向c++应用,这些回调采样接口编程。
  3. 把main文件中的全局的函数和变量移动到单独类中。

本节的大多数代码,处理灯光的代码,不是新的,只是重新组织了下。因此新的头文件需要重新定义下。

(glut_backend.h:24)

void GLUTBackendInit(int argc, char** argv);

bool GLUTBackendCreateWindow(unsigned int Width, unsigned int Height, unsigned int bpp, bool isFullScreen, const char* pTitle);

GLUT的特定的代码大多数移动到GLUT backend组件中,这个是GLUT很方便得到初始化,并且创建一个窗口。


(glut_backend.h:28)

void GLUTBackendRun(ICallbacks* pCallbacks);

在GLUT初始化,一个窗口被创建之后,下一步就是执行上面的主循环。

猜你喜欢

转载自blog.csdn.net/wodownload2/article/details/83032572