✠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的值是场景环境光与材质环境光分量的乘积: 以上是关于✠OpenGL-7-光照的主要内容,如果未能解决你的问题,请参考以下文章 Cg入门16:Fragment shader - 片段级光照
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=Lightambient∗Materialambient
光与材质亮度都是 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=Lightambientred∗Materialambientred□Iambientgreen=Lightambientgreen∗Materialambientgreen□Iambientblue=Lightambientblue∗Materialambientblue
❷漫反射分量Idiffuse,朗伯余弦定律确定了表面反射的光量与光入射角的余弦成正比:
I
d
i