Chai 3D之灯光与阴影
推荐:将 NSDT场景编辑器 加入你的3D开发工具链
介绍
光是人类可以视觉感知的任何事物的视觉表示背后的最重要的思想。光感知的概念在于,你所看到的不是基于你正在观看的物体,而是基于光源投射并从这些物体反射的光线。重要的是要注意,你的眼睛不会直接看到物体,因为你的眼睛和这些物体之间没有物理相关性。
当然,所有这些都是理论上的。我们使用术语光线只是抽象出更复杂的机制。
光线通常来自能量来源,例如太阳或房间内的灯。重要的是要注意,从理论上讲,光线沿直线传播,当您在视觉上感知物体时,您的眼睛吸收的是该物体反射或散射的光线。
光的抽象类型
以下术语描述了在对需要光源的 3D 应用程序进行编程时必须了解的不同类型的光。了解每种类型的光在渲染的 3D 对象表面上产生的效果非常重要。创建这些术语是因为需要描述光对物体产生的某些效果,以便提炼出光的复杂数学计算。然而,这并不意味着这些确切类型的光实际上存在于自然界中,我们只是将它们视为光投射在不同材料上时可能产生的效果的抽象。计算光的真实机制及其在自然界中的工作方式将非常耗时,因此,OpenGL 通常采用这组常见的光类型:环境光、漫射光和镜面光。发射光与其他光不同,是物体发出的光的类型,而其他三种类型的光通常用于描述光源。让我们详细看看这些类型的光:
将环境、漫反射和镜面反射分量组合在一起环境光
被环境光照亮的 3D 球体看起来只有 2D。环境光是由照明区域周围(或位于照明区域内部)的所有光源发射光而产生的平均光量。当阳光穿过房间的窗户时,它们会打到墙壁上,反射并散射到各个不同的方向,平均照亮整个房间。这种视觉质量由环境光描述。仅环境光无法传达在 3D 空间中设置的对象的完整表示,因为所有顶点都由相同的颜色均匀照明,并且对象看起来像是二维的,如上图所示。尽管显示的对象实际上是一个 3D 球体,但当仅由环境光照亮时,它在屏幕上看起来是平坦的。
漫反射光
红色的漫射光投射到定义其 3D 形状的黑色物体上。
漫射光表示光源投射的定向光。漫射光可以描述为在空间中具有位置并且来自单个方向的光。手电筒稍微高于它所照亮的物体,可以被认为是发射漫射光。在上图中,投射红色漫射光的光源位于物体的左侧。当漫射光接触物体表面时,它会在该表面上均匀地散射和反射。
为了演示环境光和漫射光如何协同工作以创建看起来更逼真的对象,请想象一个 3D 球体,其上散布着深红色的环境光:
现在,通过将漫射光源放置在球体的右侧,我们得到以下结果:
请注意球体现在看起来是 3D 的。
镜面光
除了环境光层和漫反射层外,此处还显示镜面反射(或镜面反射高光)。您可以观察到镜面反射光源属性如何大大增强对象的 3D 表示。 就像漫射光一样,镜面反射光是一种定向光。它来自一个特定的方向。
两者之间的区别在于镜面光以锐利而均匀的方式从表面反射。镜面反射光的渲染取决于观察者和光源之间的角度。从观察者的角度来看,镜面反射光会在被观察对象的表面上创建一个突出显示的区域,称为镜面反射或镜面反射。镜面反射的强度取决于构成物体的材料以及包含镜面反射光分量的光源的强度。
发射光
自发光与之前解释的任何其他光分量略有不同。自发光光组件负责物体材质反射或吸收光的属性。当应用于对象的材质时,自发光光的作用是模拟从物体反射的光。
由于周围没有其他光源,仅应用自发光光分量的物体颜色与仅应用环境光的对象具有相同的视觉质量。然而,任何额外的漫射光或镜面光如何与仅施加自发光光的同一物体的表面反应的机制是不同的。让我们考虑一个平均发出绿色的物体。在下图中,自发光分量应用于球体。如您所见,结果类似于在上面示例中将环境光应用于同一球体所创建的效果。
反射绿色自发光光的 3D 球体。在将其他光源引入场景之前,该效果类似于环境光。
如您所知,光源可以分配所有三个组件,即环境光、漫反射光和镜面反射光组件。让我们看看当我们在上面的场景中应用光源时会发生什么。我们应用的光源具有以下属性:红色环境光、红色漫射光和白色镜面光。
如果上面的球体没有发出绿色的光,它就会呈现红色。但是,自发光的绿色分量被施加到它上面。当光源的“光线”照射到球体表面时,“光源”和“目标”颜色融合在一起,产生淡黄色的表面。光源的镜面反射光分量为白色。镜面反射的中心中心是白色的,但是当它扩散时,它与绿色和红色融合,在黄色(即绿红色)上增加。同样,请注意,如果没有将自发光光应用于球体,它看起来就像上面镜面反射部分下显示的球体一样,全部为红色,带有白色镜面反射。
本教程的以下部分将介绍 OpenGL 着色多边形以模拟光线的方式,以及如何将光源属性分配给光源和材质。
光源
为了计算 3D 物体的阴影,CHAI3D 需要知道落在其上的光线的强度、方向和颜色。这些属性由世界中的光对象提供。所有光源的基色和强度设置相同,但方向取决于您使用的光源类型。此外,光线可能会随着与光源的距离而减弱。下面介绍了 CHAI3D 中可用的三种类型的光源。
CHAI3D 光源位置灯
位置光位于空间中的某个点,并平等地向各个方向发出光。光线照射表面的方向是从接触点回到光对象中心的线。强度随着与光的距离而减小,在指定范围内达到零。点光源可用于模拟场景中的灯和其他局部光源。
using namespace chai3d;
// create a light source
light = new cPositionalLight(world);
// attach light to camera
world->addChild(light);
// enable light source
light->setEnabled(true);
// position the light source
light->setLocalPos(1.0, 1.0, 0.5);
方向灯
定向光源没有任何可识别的光源位置,因此光源对象可以放置在世界任何地方。世界上所有的物体都被照亮,就好像光线总是来自同一个方向一样。光与目标物体的距离未定义,因此光不会减弱。
定向光源表示来自活动工作区范围之外的位置的大而远的光源。在逼真的场景中,它们可以用来模拟太阳或月亮。在抽象的模拟世界中,它们可以成为一种有用的方法,可以为对象添加令人信服的阴影,而无需准确指定光线的来源。在场景视图中检查对象时(例如,查看其网格、着色器和材质的外观),定向光通常是了解其着色显示方式的最快方法。对于这样的测试,您通常对光线来自哪里不感兴趣,而只是想看到物体看起来“固体”并寻找模型中的毛刺。
using namespace chai3d;
// create a directional light source
light = new cDirectionalLight(world);
// insert light source inside world
world->addChild(light);
// enable light source
light->setEnabled(true);
// define direction of light beam
light->setDir(-1.0, 0.0, 0.0);
聚光灯
与位置光源一样,聚光灯具有光源落落的指定位置和范围。但是,聚光灯被限制在一个角度,导致锥形照明区域。圆锥体的中心指向光源对象的向后 (X) 方向。
聚光灯通常用于人造光源,例如可以连接到触觉工具(例如模拟内窥镜)的手电筒。
using namespace chai3d;
// create a light source
light = new cSpotLight(world);
// attach light to camera
world->addChild(light);
// enable light source
light->setEnabled(true);
// position the light source
light->setLocalPos(0.6, 0.6, 0.5);
// define the direction of the light beam
light->setDir(-0.5,-0.5,-0.5);
阴影
灯光与阴影阴影映射或投影阴影是将阴影添加到 3D 计算机图形的过程。阴影是通过测试像素从光源中是否可见来创建的,方法是将其与以纹理形式存储的光源视图的 z 缓冲区或深度图像进行比较。
从光源生成的阴影贴图如果你从光源向外看,你能看到的所有物体都会出现在光线中。然而,这些物体后面的任何东西都会在阴影中。这是用于创建阴影贴图的基本原则。渲染光源的视图,存储它看到的每个表面的深度(阴影贴图)。接下来,渲染常规场景,将绘制的每个点的深度(好像它被光而不是眼睛看到)与此深度图进行比较。
示例
在 CHAI3D 中,聚光灯支持阴影贴图。在下面的示例中,我们说明了如何设置阴影贴图。
using namespace chai3d;
// create a light source
light = new cSpotLight(world);
// attach light to camera
world->addChild(light);
// enable light source
light->setEnabled(true);
// position the light source
light->setLocalPos(0.6, 0.6, 0.5);
// define the direction of the light beam
light->setDir(-0.5,-0.5,-0.5);
// set light cone half angle
light->setCutOffAngleDeg(30);
// enable this light source to generate shadows
light->setShadowMapEnabled(true);
为了最大限度地提高阴影的质量,重要的是 de 分配尽可能小的截止角度。此外,可以通过提高阴影贴图的分辨率来调整阴影贴图的质量。较大的阴影贴图显然需要更长的时间来渲染,并且在低性能图形卡上可能不受支持。
using namespace chai3d;
// (1) set the resolution of the shadow map - low quality
light->m_shadowMap->setQualityLow();
// (2) set the resolution of the shadow map - medium quality
light->m_shadowMap->setQualityMedium();
// (3) set the resolution of the shadow map - high quality
light->m_shadowMap->setQualityHigh();
渲染摄像机场景时,首先更新所有阴影贴图非常重要。如果使用多个摄像机和多个帧缓冲区(窗口显示)渲染场景,则只需计算一次阴影贴图。
using namespace chai3d;
// update shadow maps
world->updateShadowMaps(false, mirroredDisplay);
// render scene
camera->renderView(windowW, windowH);
3D建模学习工作室翻译整理,转载请标明出处!