基本光照

本文详细介绍了Phong光照模型,包括Lambert和HalfLambert的漫反射计算,以及Phong和BlinnPhong的高光反射计算。同时,探讨了三种常见的光源类型:平行光、点光源和聚光灯,分析了它们的光照计算原理和衰减公式,为图形学和Shader编程提供了基础理论。
摘要由CSDN通过智能技术生成

Phong光照模型

现实世界的光照是极其复杂的,而且会受到诸多因素的影响,这是我们有限的计算能力所无法模拟的,所以我们使用数学和物理工具来简化和描述现实世界的光照,使得最终呈现的效果尽可能的接近现实。

其中,我们最常用的一种称为Phong(冯氏)光照模型。

Phong光照模型使用环境光(ambient),漫反射光(diffuse),高光反射(specular)三个分量来描述一个被光照射的物体的光照颜色。

  • 环境光 (ambient) : 我们现实生活中的某个环境中可能有多个光源,经由一系列其他物体的反射等形成了某种颜色。我们可以通过全局光照逐一计算各个光源、各种反射等,但是那样太消耗性能,于是给定某种颜色常量作为环境光来模拟上述情况。一般来说,环境光都是比较暗的。

  • 漫反射光(difffuse): 可以理解成由于物体表面比较粗糙,光在各个方向反射的现象。一般来说,某一部分越正对着光就越亮。

  • 高光反射(specular) : 就是镜面反射,是模拟非常光滑的物体对光的反射。

所以,最终的光照的输出颜色是:

color ambient = calcAmbient()
color diffuse = calcAmbient()
color specular = calcSpecular()

color result = ambient + diffuse + specular

(ambient是常数)下面介绍不同颜色分类的计算方法。

Lambert和HalfLambert计算漫反射光照

Lambert是一个模拟现实光照的经验模型,即漫反射光的光强仅与入射光的方向和反射点处表面法向夹角的余弦成正比。

这里写图片描述

余弦我们可以很方便的用Dot来计算获得,显然背对我们的光线的地方是得不到光照的,所以要确保Dot的结果大于0.

float diffuseFactor = max(0, dot(normal,lightDir));
color result = lightColor *  diffuseFactor;

Lambert的计算效果有点瑕疵,那是因为在所有背对着光源的部分我们最后得到的结果都是0,那么会让背对光源的地方看起来有点像一个平面,所以有一个改进的HalfLambert模型。

HalfLambert改进了公式:

float diffuseFactor = A * dot(normal,lightDir) + B;
  color = lightColor * diffuseFactor;

其中A和B是两个系数,我们一般都取A = B = 0.5,通过这种方式,我们把dot的结果从[-1, 1]映射到了[0, 1],而且背对着光源的部分也不全都是0,是一些位于[0, 0.5]的值,有了明暗变化,不再是像一个平面。

Phong和BlinnPhong计算高光反射

根据实际生活可知,高光反射(镜面反射)会因为观察者的观察角度不同略有区别:

这里写图片描述

Phong就根据上图构建了高光反射的计算模型:

float specularFactor = pow(max(dot(viewDir,reflectDir),0),shininess);
color result = lightColor * specularFactor;

I是光的入射向量,R是光的反射向量,V是观察者的视线向量(一般是摄像机的视线向量),α是反射向量和视线向量的夹角。

很显然,这个夹角越大我们能看到的东西越小。

在计算specularFactor的算式中,shininess代表了光泽度。

显然,这里有个难点是如何计算出反射光向量。

做一下辅助线我们可以很方便的求出:

这里写图片描述

显然:

(1)  I + R = 2 * P
 =>  R = 2 * P - I
(2)  I + S = P
(3)  S是-INormal方向上的投影
 =>  S = Dot(-I,N) / Length(N) * normalize(N)
     为了简化计算,我们把S和I都标准化,那么
     S = Dot(-I,N) * N = -Dot(I,N) * N
(4)  根据(1)(2)(3),得 R = 2 * (I + S) - I = 2 * (I - Dot(I,N) * N) - I = I - Dot(I,N) * N

所以,Phong高光计算可表示为:

normal = normlize(normal);
lightDir = normlize(lightDir);

vec3 reflectDir = lightDir - Dot(lightDir,normal) * normal;
float specularFactor = pow(max(dot(viewDir,reflectDir),0),shininess);
color result = lightColor * specularFactor;

由于这种方法需要计算反射向量,所以性能开销不算小,改进的BlinnPhong模型不再使用反射向量,而是一引入了一个新的向量h,最终计算的效果和Phong差不多,且有更小的开销。

这里写图片描述

vec3 half = normalize(viewDir + lightDir);
float specularFactor = pow(max(dot(viewDir,half),0),shininess);
color result = lightColor * specularFactor;

光源类型

在上述的讨论中,我们只是很抽象的用lightColor和lightDir来描述了一个光源,实际上显示生活中有多种光源。
像太阳(平行光),点灯泡(点光源),手电筒(聚光),我们也有特定的模型来描述这些光源。

(Directional Light)平行光

当一个光源处于很远的地方时,来自光源的每条光线就会近似于互相平行。不论物体和/或者观察者的位置,看起来好像所有的光都来自于同一个方向。当我们使用一个假设光源处于无限远处的模型时,它就被称为定向光,因为它的所有光线都有着相同的方向,它与光源的位置是没有关系的。

定向光非常好的一个例子就是太阳。太阳距离我们并不是无限远,但它已经远到在光照计算中可以把它视为无限远了。所以来自太阳的所有光线将被模拟为平行光线。

struct DirectLight {

    vec3 direction;//光照方向,就是上述的lightDir,定值

    color ambient;//环境光分量
    color diffuse;//漫反射光lightColor
    color specular;//镜面反射光lightColor
};

(Point Light)点光源

定向光对于照亮整个场景的全局光源是很不错的,但除了定向光之外我们也需要一些分散在场景中的点光源(Point Light)。点光源是处于世界

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值