✠OpenGL-7-光照

Posted itzyjr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了✠OpenGL-7-光照相关的知识,希望对你有一定的参考价值。

光照模型

最常见的光照模型称为“ADS”模型:

  • 环境光反射(Ambient reflection)——模拟低级光照,影响场景中的所有物体。
  • 漫反射(Diffuse reflection)——根据光线的入射角度调整物体亮度。
  • 镜面反射(Specular reflection)——用以展示物体的光泽,通过在物体表面上,光线最直接反射到我们的眼睛的位置,策略性地放置适当大小的高光来实现。


    场景的绘制最终由片段着色器为屏幕上的每个像素输出颜色而实现。使用ADS光照模型需要指定由于像素的RGBA输出值上的光照而产生的分量。因素包括:
  • 光源类型及其环境、漫反射和镜面反射特性;
  • 对象材质的环境、漫反射和镜面反射特性;
  • 对象的材质指定为“光泽”;
  • 光线照射物体的角度;
  • 从中查看场景的角度。
光源

光源有许多类型,每种光源具有不同的特性。常见光源类型有:

  • 全局光(通常称为“全局环境光”,因为它仅包含环境光组件);
  • 定向光(或“远距离光”);
  • 位置光(或“点光源”);
  • 聚光灯。

全局环境光是最简单的光源模型。它没有光源位置——无论场景中的对象在何处,其上的每个像素都有着相同的光照。全球环境光照模拟了现实世界中的一种光线现象,即光线经过很多次反射,其光源和方向都已经无法确定。全局环境光仅具有环境光反射分量,用RGBA 值设定;它没有漫反射或镜面反射分量。

RGBA 的取值范围为 0~1,全局环境光通常被建模为偏暗的白光,其中 RGB 各值设为0~1 的相同的小数, alpha 设置为 1。
例如,全局环境光可以定义如下:

float globalAmbient[4] = {0.6f, 0.6f, 0.6f, 1.0f};

定向光或远距离光也没有源位置,但它具有方向。它可以用来模拟光源距离非常远,以至于光线接近平行的情况,例如阳光。通常在这种情况下,我们可能只对建模光照感兴趣,而对发光的物体不感兴趣。定向光对物体的影响取决于光照角度,物体在朝向定向光的一侧比在切向或相对侧更亮。建模定向光需要指定其方向(以向量形式)及其环境、漫反射和镜面特征(以 RGBA 值)。

在已经有全局环境光的情况下,定向光的环境光分量看起来似乎是多余的。然而,当光源“开启”或“关闭”时,全局环境光和定向光的环境光分量的区别就很明显了。当“开启”时,总环境光分量将如预期的那样增加。下面的示例代码中,我们只使用了很小的环境光分量。在实际场景中,应当根据场景的需要平衡两个环境光分量。

指向 Z 轴负方向的红色定向光可以指定如下:

float dirLightAmbient[4] =  {0.1f, 0.0f, 0.0f, 1.0f};
float dirLightDiffuse[4] =  {1.0f, 0.0f, 0.0f, 1.0f};
float dirLightSpecular[4] = {1.0f, 0.0f, 0.0f, 1.0f};
float dirLightDirection[3] = {0.0f, 0.0f, -1.0f};

位置光在 3D 场景中具有特定位置。靠近场景的光源,例如台灯,蜡烛等。像定向光一样,位置光的效果取决于撞击角度;但是,它没有方向,因为它对场景中的每个顶点的光照方向都不同。位置光还可以包含衰减因子,以模拟它们的强度随距离减小的程度。与我们看到的其他类型的光源一样,位置光具有指定为 RGBA 值的环境光反射、漫反射和镜面反射特性。

位置 (5, 2, −3) 处的红色位置光可以指定如下例:

float posLightAmbient[4] = {0.1f, 0.0f, 0.0f, 1.0f};
float posLightDiffuse[4] = {1.0f, 0.0f, 0.0f, 1.0f};
float posLightSpecular[4] = {1.0f, 0.0f, 0.0f, 1.0f};
float posLightLocation[3] = {5.0f, 2.0f, -3.0f};

衰减因子有多种建模方式。其中一种方式是使用恒定、线性和二次方(分别称为kc、kl和kq)衰减,并引入非负可调参数。

这些参数与离光源的距离(d)结合进行计算:
a t t e n u a t i o n F a c t o r = 1 k c + k l d + K q d 2 attenuationFactor = \\frac { 1 } { k_c+k_ld+K_qd^2 } attenuationFactor=kc+kld+Kqd21
这个因子与光的强度相乘可以使距光更远时,光的强度衰减更多。注意, kc 应当永远设置为大于等于 1 的值,从而使得衰减因子落入[0…1]区间,并当 d 增大时接近于 0。

聚光灯(spotlight)同时具有位置和方向。其“锥形”效果可以使用 0° ~90° 的截光角 θ来模拟,指定光束的半宽度,并使用衰减指数来模拟随光束角度的强度变化。如下图所示,我们确定聚光灯方向与从聚光灯到像素的向量之间的角度 φ。当 φ 小于 θ 时,我们通过将 φ的余弦提高到衰减指数来计算强度因子(当 φ 大于 θ 时,强度因子设置为 0)。结果是强度因子的范围为 0~1。衰减指数会影响当角度 φ 增加时,强度因子趋于 0 的速率。然后将强度因子乘以光的强度以模拟锥形效果。

位于( 5,2,−3)向下照射 Z 轴负方向的红色聚光灯可以表示为:

float spotLightAmbient[4] = { 0.1f, 0.0f, 0.0f, 1.0f };
float spotLightDiffuse[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
float spotLightSpecular[4] = {1.0f, 0.0f, 0.0f, 1.0f };
float spotLightLocation[3] = { 5.0f, 2.0f, -3.0f };
float spotLightDirection[3] = { 0.0f, 0.0f, -1.0f };
float spotLightCutoff = 20.0f;
float spotLightExponent = 10.0f;

聚光灯也可以引入衰减因子。聚光灯衰减因子可以用与前述定向光源相同的方式实现。

当设计拥有许多光源的系统时,程序员应该考虑创建相应的类结构,如定义 Light 类以及其子类 GlobalAmbient、 Directional、 Positional 以及 Spotlight。由于聚光灯同时具有定向光和位置光的特性,这里就值得使用 C++的多继承能力,让 Spotlight 类同时继承于实现位置光和定向光的类。

材质

光照使得我们可以加入表面的反射特性。即对象如何与我们的 ADS 光照模型相互作用。这可以通过将每个对象视为“由某种材质制成”来建模。

一些其他材质的ADS RGBA值见下图:

对于大多数材质而言, 只需要把不透明度设置为 1.0就行了,但是对于某些特定的材质,加入一些透明度是很重要的。例如,上图中材质“玉”和“珍珠”都含有少量透明度(取值略微小于 1.0)以显得更加真实。
放射性有时也包含在 ADS 材质规范中。在模拟自身发光的材质(例如磷光材质)时非常有用。

预定义一些可供选择的材质, 在使用时会很方便。 因此我们需要在 Utils.cpp
文件中添加如下代码:

// 黄金材质
float* Utils::goldAmbient() {// 环境光RGBA
	static float a[4] = { 0.2473f, 0.1995f, 0.0745f, 1 }; 
	return (float*)a; 
}
float* Utils::goldDiffuse() {// 漫反射RGBA
	static float a[4] = { 0.7516f, 0.6065f, 0.2265f, 1 }; 
	return (float*)a; 
}
float* Utils::goldSpecular() {// 镜面反射RGBA
	static float a[4] = { 0.6283f, 0.5559f, 0.3661f, 1 }; 
	return (float*)a; 
}
float Utils::goldShininess() { return 51.2f; }// 光泽

这样在 init()函数中或全局中为物体指定“黄金”材质就非常容易了,如下所示:

float* matAmbient = Utils::goldAmbient();
float* matDiffuse = Utils::goldDiffuse();
float* matSpecular = Utils::goldSpecular();
float matShininess = Utils::goldShininess();
ADS光照计算

当我们绘制场景时,每个顶点坐标都会进行变换以将 3D 世界模拟到 2D 屏幕上。每个像素的颜色都是光栅化、纹理贴图以及插值的结果。现在我们需要加入一个新的步骤来调整这些光栅化之后的像素颜色,以便反应场景中的光照和材质。

我们需要做的基础 ADS 计算是确定每个像素的⓿反射强度(Reflection Intensity, I)。Iobserved计算过程如下:
I o b s e r v e d = I a m b i e n t + I d i f f u s e + I s p e c u l a r I_{observed} = I_{ambient} + I_{diffuse}+I_{specular} Iobserved=Iambient+Idiffuse+Ispecular
我们需要计算每个光源对于每个像素的环境光反射、漫反射和镜面反射分量,并求和。这些计算都基于场景内的光源类型以及渲染中模型的材质类型。

❶环境光分量Iambient的值是场景环境光与材质环境光分量的乘积:
I a m b i e n t = L i g h t a m b i e n t ∗ M a t e r i a l a m b i e n t I_{ambient}=Light_{ambient}*Material_{ambient} Iambient=LightambientMaterialambient
光与材质亮度都是 RGB 值,计算可以更准确地描述为:
I a m b i e n t r e d = L i g h t a m b i e n t r e d ∗ M a t e r i a l a m b i e n t r e d □ I a m b i e n t g r e e n = L i g h t a m b i e n t g r e e n ∗ M a t e r i a l a m b i e n t g r e e n □ I a m b i e n t b l u e = L i g h t a m b i e n t b l u e ∗ M a t e r i a l a m b i e n t b l u e I _ { ambient } ^ { red } = Light_{ambient}^{red}*Material_{ambient}^{red} \\\\□ \\\\ I _ { ambient } ^ { green } = Light_{ambient}^{green}*Material_{ambient}^{green} \\\\□ \\\\ I _ { ambient } ^ { blue} = Light_{ambient}^{blue}*Material_{ambient}^{blue} Iambientred=LightambientredMaterialambientredIambientgreen=LightambientgreenMaterialambientgreenIambientblue=LightambientblueMaterialambientblue
❷漫反射分量Idiffuse,朗伯余弦定律确定了表面反射的光量与光入射角的余弦成正比:
I d i

以上是关于✠OpenGL-7-光照的主要内容,如果未能解决你的问题,请参考以下文章

Cg入门16:Fragment shader - 片段级光照

Cg入门17:Fragment shader - 片段级光照(添加阴影)

光照 漫反射光照

光照 漫反射光照

光照 漫反射光照

光照 漫反射光照