[TOC]
渲染总是围绕着一个基础问题:我们如何决定一个像素的颜色?从宏观上来说,渲染包括了两大部分:决定一个像素的可见性,决定这个像素上的光照计算。而光照模型用于决定在一个像素上进行怎样的光照计算。

1. 我们是如何看到这个世界的

我们可能常会问类似这样的问题:这个物体是什么颜色的?如果读者对小学自然课还有印象的话,可能还会记得这个问题是没有任何意义的:当我们在描述这个物体是红色时,实际上是因为这个物体会反射更多的红光波长,而吸收了其他波长。而如果一个物体在我们看来是黑色的,实际上是因为它吸收了绝大部分的波长。这种物理现象就是本节要讨论的内容。
通常来讲,我们要模拟真实的光照环境来生成一张图像,需要考虑3种物理现象。
(1)首先,光从光源(light source)中被发射出来
(2)然后,光线和场景中的一些物体相交:一些光线被物体吸收了,而另一些光线被散射到其它方向
(3)最后摄像机吸收了一些光,产生了一张图像
下面,我们将对每个部分做更加详细的讲解。

1.1 光源

光不是从石头里蹦出来的,而是由光源发射出来的。在实时渲染中,我们通常把光源当成一个没有体积的点,用l来表示它的方向。那么我们如何测一个光源发出了多少光呢?也就是说,我们如何量化光呢?在光学里,我们使用辐照度(irradiance)来量化光。对于平行光来说,它的辐照度可通过计算在垂直于l的单位面积上单位时间内穿过的能量来得到。在计算光照模型时,我们需要知道一个物体表面的辐照度,而物体表面往往是和l不垂直的,那么如何计算这样的表面辐照度呢?我们可以使用光源方向l和表面法线n之间的夹角余弦值来得到。需要注意的是,这里默认方向矢量的模都为1,下图显示了使用余弦来计算的原因。

因为辐照度是和照射到物体表面时光线之间的距离d/cosθ成反比的,因此辐照度就和cosθ成正比。cosθ可以使用光源方向l和表面法线n的点积来得到。这就是使用点积来计算辐照度的由来。

1.2 吸收和散射

光线由光源发射出来后,就会与一些物体相交。通常相交的结果有两个:散射(scattering)和吸收(absorption)。
散射只能改变光线的方向,但不改变光线的密度和颜色。而吸收只改变光线的密度和颜色,但不改变光线方向。光线在物体表面经过散射后,有两种方向:一种将会散射到物体内部,这种现象被称为折射(refraction)或透射(transmission);另一种被散射到外部,这种现象被称为反射(reflection)。对于不透明物体,折射进入物体内部的光线还会与内部的颗粒进行相交,其中一些光线最后会重新发射出物体表面,而另一些则被物体吸收。那些从物体表面重新发射出的光线将具有和入射光线不同的方向分布和颜色。下图给出了这样的一个例子:

为了区分这两种不同的散射方向,我们在光照模型中使用了不同的部分来计算它们:高光反射(specular)部分表示物体的表面是如何反射光线的,而漫反射(diffuse)部分则表示有多少光线会被折射、吸收和散射出表面。根据入射光线的数量和方向,我们可以计算出射光线和方向,我们通常使用出射度(exitance)来描述它辐照度和出射度之间是满足线性关系的,而它们之间的比值就是材质的漫反射和高光反射属性。
在本章中,我们假设漫反射部分是没有方向性的,也就是说光线在所有方向上都是平均分布的。同时我们也只考虑一个特定方向上的高光反射。

1.3 着色

着色(shading)指的是,根据材质属性(如漫反射属性等)、光源信息(如光源方向、辐照度等),使用一个等式去计算沿某个观察方向的出射度过程。我们也把这个等式称为光照模型(Lighting Model)。不同的光照模型有着不同的目的。例如,一些用于描述粗超的物理表面,一些用于描述金属表面等。

1.4 BRDF光照模型

我们已经了解了光线和物体表面相交时会发生哪些现象。现已知光源位置和方向、视角方向时,我们就需要知道一个表面是如何和光照进行交互的。例如,当光线从某个方向照射到一个表面时,有多少光线被反射?反射方向有哪些?而BRDF(Bidirectional Reflectance Distribution Function)就是用来回答这些问题的。当给定模型表面一个点时,BRDF包含了对该点外观的完整描述。在图形学中,BRDF大多使用一个数学公式来表示,并且提供了一些参数来调整材质属性。通俗来讲,当给定入射光线的方向和辐照度后,BRDF可以给出在某个出射方向上的光照能量分布。本章涉及的BRDF都是对真实场景进行理想化和简化后的模型,也就是说,它们并不能真实地反映物体和光线之间的交互,这些光照模型被称为是经验模型。尽管如此,这些经验模型仍然在实时渲染领域被应用了多年。
计算机图形学的第一定律:如果它看起来是对的,那么它就是对的。

2. 标准光照模型

虽然光照模型有很多种类,但在早期的游戏引擎中往往只使用了一个光照模型,这个模型被称为标准光照模型。实际上,在BRDF理论被提出之前,标准光照模型就已经被广泛使用了。
在1975年,著名学者裴祥风(Bui Tuong Phong)提出了标准光照模型背后的基本理念。标准光照模型只关心直接光照(direct light),也就是那些直接从光源发射出来照射到物体表面后,经过物体表面的一次反射直接进入摄像机的光线。
它的基本方法是,把进入到摄像机的光线分为4个部分,每个部分都使用一种方法来计算它的贡献度。这4个部分是:
(1)自发光(emissive)部分,本书使用Cemissive来表示。这个部分用于描述当给定一个方向时,一个表面本身会向该方向发射多少辐射量。需要注意的是,如果没有使用全局光照(global illumination)技术,这些自发光的表面并不会真的照亮周围的物体,而是它本身看起来更亮了而已。
(2)高光反射(specular)部分,本书使用Cspecular来表示。这个部分用于描述当光线从光源照射到模型表面时,该表面会在完全镜面反射方向散射多少辐射量。
(3)漫反射(diffuse)部分,本书使用Cdiffuse来表示。这个部分用于描述,当光线从光源照射到模型表面时,该表面会向每个方向散射多少辐射量
(4)环境光(ambient)部分,本书使用Cambient来表示。它用于描述其他所有的间接光照。

2.1 环境光

虽然标准光照模型的重点在于描述直接光照,但在真实世界中,物体也可以被间接光照(indirect light)所照亮。间接光照指的是,光线通常会在多个物体之间反射,最后进入摄像机,也就是说,在光线进入摄像机之前,经过了不止一次的物体反射。例如在红地毯上放置一个浅灰色沙发,那么沙发底部也会有红色,这些红色是由红地毯反射了一部分光线,再反弹到沙发上的。
在标准光照模型中,我们使用了一种被称为环境光的部分来近似模拟间接光照。环境光的计算非常简单,它通常是一个全局变量,即场景中的所有物体都是用了这个环境光。下面的等式给出了计算环境光的部分:

2.2 自发光

光线也可以由光源发射进入摄像机,而不需要经过任何物体的反射。标准光照模型使用自发光来计算这个部分的贡献度。它的计算也很简单,就是使用了该材质的自发光颜色:

通常在实时渲染中,自发光的表面往往不会照亮周围的表面,也就是说这个物体并不会被当成一个光源。Unity5引入的全新的全局光照系统则可以模拟这类自发光物体对周围物体的影响。

2.3 漫反射

漫反射光照是用于对那些被物体表面随机散射到各个方向的辐射度进行建模的。在漫反射中,视角的位置是不重要的,因为反射是完全随机的,因此可以认为在任何反射方向上的分布都是一样的。但是入射光线的角度很重要。
漫反射光照符合兰伯特定律(Lambert’s law):反射光线的强度与表面法线和光源方向之间夹角的余弦值成正比。因此,漫反射部分的计算如下:

其中n是指表面法线,I是指向光源的单位矢量,mdiffuse是材质的漫反射颜色,clight是光源颜色。需要注意的是,我们需要防止法线和光源方向点乘的结果为负值,为此,我们使用取最大值的函数来将其截取到0,这可以防止物体被后面的光源照亮。

2.4 高光反射

这里的高光反射是一种经验模型,也就是说,它并不完全符合真实世界中的高光反射现象。它可以用于计算那些沿着完全镜面反射方向被反射的光线,这可以让物体看起来是有光泽的,例如金属材质。
计算高光反射需要知道的信息比较多,如表面法线、视角方向、光源方向、反射方向等。在本节中,我们假设这些矢量都是单位矢量。下图给出了这些单位矢量:

在这四个矢量中,我们实际上只需要知道其中3个即可,而第四个矢量——反射方向可以通过其他信息计算得到:

这样我们就可以用Phong模型来计算高光反射的部分:

其中mgloss是材质的光泽度(gloss),也被称为反光度(shininess)。它用于控制高光区域的“亮点”有多宽,mgloss越大,亮点就越小。mspecular是材质的高光反射颜色,它用于控制该材质对于高光反射的强度和颜色,Clight则是光源的颜色和强度。同样这里也需要防止v·r为负数。
和上述的Phong模型相比,Blinn提出了一个简单的修改方法来得到类似的效果。它的基本思想是,避免计算反射反向的r。为此,Blinn模型引入了一个新的矢量h,它是通过对v和l取平均后在归一化得到的。即

然后,使用n和h之间的夹角进行计算,而非v和r之间的夹角,如下图所示:

总结一下,Blinn模型的公式如下:

在硬件实现时,如果摄像机和光源距离模型足够远的话,Blinn模型就会快于Phong模型,这是因为此时可以认为v和l都是定值,因此h将是一个常量,但是,当v或者l不是定值时,Phong模型可能反而更快一些。需要注意的是,这两种模型都是经验模型,也就是说,我们不应该认为Blinn模型是对“正确的”Phong模型的近似。实际上,一些情况下,Blinn模型更符合实验结果。

2.5 逐像素还是逐顶点

上面,我们给出了基本光照模型使用的数学公式,那么我们在哪里计算这些光照模型呢?通常来讲,我们有两种选择:在片元着色器中进行计算,也被称为逐像素光照(per-pixel lighting);在顶点着色器中进行计算,也被称为逐顶点光照(per-vertex lighting)。
在逐像素光照中,我们会以每个像素为基础,得到它的法线(可以是对顶点法线插值得到的,也可以是从法线纹理中采样得到的)然后进行光照模型计算。这种在面片之间对顶点法线进行插值的技术被称为Phong着色(Phong Shading),也被称为Phong插值或法线插值着色技术。这不同于我们之前讲到的Phong光照模型。
与之对应的是逐顶点光照,也被称为高洛德着色(Gouraud shading)。在逐顶点光照中,我们在每个顶点上计算光照,然后会在渲染图元内部进行线性插值,最后输出成像素颜色。由于顶点数目远远小于像素数目,因此逐顶点光照的计算量往往要小于逐像素光照。但是,由于逐顶点光照依赖于线性插值来得到像素光照,因此,当光照模型中有非线性的计算(例如计算高光反射时)时,逐顶点光照就会出现问题。在后面的情况中我们将会看到这种情况。而且,由于逐顶点光照会在渲染图元内部对顶点颜色进行插值,这会导致渲染图源内部的颜色总是暗于顶点处的最高颜色值,这在某些情况下会产生明显的棱角现象。

3. 总结

虽然标准光照模型仅仅是一个经验模型,也就是说,它并不完全符合真实世界中的光照现象。但由于它的易用性、计算速度和得到的效果都比较好,因此让被广泛使用。而也是由于它的广泛使用性,这种标准光照模型有很多不同的叫法。例如,一些资料中称它为Phong光照模型,因为裴祥风(Bui Tuong Phong)首先提出了使用漫反射和高光反射的和来对反射光照进行建模的基本思想,并且提出了基于经验的计算高光反射的方法(用于计算漫反射光照的兰伯特模型在那时已经被提出了)。而后,由于Blinn的方法简化了计算而且在某些情况下计算更快,我们把这种模型称为Blinn-Phong光照模型。
但这种模型有很多局限性。首先,有很多重要的物理现象多无法用Blinn-Phong模型表现出来,例如菲涅尔反射(Fresnel reflection)。齐次,Blinn-Phong模型时各项同性的(isotropic)的。也就是说,当我们固定视角和光源方向旋转这个表面时,反射不会发生任何改变。但有些表面是具有各向异性(anisotropic)反射性质的,例如拉丝金属、毛发等。在后面我们将学习基于物理的光照模型,这些光照模型更加复杂,同时也可以更加真实的反映光和物体的交互。

第五章 Unity中的基础光照(1)的更多相关文章

  1. 第五章 Unity中的基础光照(3)

    目录 1. 在Unity Shader中实现高光反射光照模型 1.1 实践:逐顶点光照 1.2 逐像素光照 1.3 Blinn-Phong光照模型 2. 召唤神龙:使用Unity内置的函数 @ 1. ...

  2. 第五章 Unity中的基础光照(2)

    目录 1. Unity中的环境光和自发光 2. 在UnityShader中实现漫反射光照模型 2.1 实践:逐顶点光照 2.2 实践:逐像素光照 2.3 半兰伯特模型 1. Unity中的环境光和自发 ...

  3. Unity Shader入门精要学习笔记 - 第6章 开始 Unity 中的基础光照

    转自冯乐乐的<Unity Shader入门精要> 通常来讲,我们要模拟真实的光照环境来生成一张图像,需要考虑3种物理现象. 首先,光线从光源中被发射出来. 然后,光线和场景中的一些物体相交 ...

  4. Unity中的基础光照

    渲染包含了两大部分:决定一个像素的可见性,决定这个像素上的光照计算. 光照模型就是用于决定在一个像素上进行怎样的光照计算. 一.光源 在实时渲染中我们通常把光源当做一个没有体积的点. 1.1 辐照度 ...

  5. 【Unity】第6章 Unity脚本开发基础

    分类:Unity.C#.VS2015 创建日期:2016-04-16 一.简介 游戏吸引人的地方在于它的可交互性.如果游戏没有交互,场景做得再美观和精致,也难以称其为游戏. 在Unity中,游戏交互通 ...

  6. Unity Shader入门精要学习笔记 - 第16章 Unity中的渲染优化技术

    转自冯乐乐的 <Unity Shader 入门精要> 移动平台的特点 为了尽可能一处那些隐藏的表面,减少overdraw(即一个像素被绘制多次),PowerVR芯片(通常用于ios设备和某 ...

  7. 第五章 Java中锁

    Lock接口 锁是用来控制多个线程访问共享资源的方式,一般来说,一个锁能够防止多个线程同时访问共享资源(但是有些锁可以允许多个线程并发的访问共享资源,比如读写锁).在Lock接口出现之前,Java程序 ...

  8. 【Unity Shader】---基础光照

    一.[标准光照模型]1.自发光emissve:描述一个表面本身会发散多少光.在没有使用全局光照时,这些自发光是不会真正照亮周围物体. 自发光就是直接由发光体发射进入摄像机,不经过任何反射,在标准光照模 ...

  9. java并发学习第五章--线程中的锁

    一.公平锁与非公平锁 线程所谓的公平,就是指的是线程是否按照锁的申请顺序来获取锁,如果是遵守顺序来获取,这就是个公平锁,反之为非公平锁. 非公平锁的优点在于吞吐量大,但是由于其不是遵循申请锁的顺序来获 ...

随机推荐

  1. MySQL批量插入的分析以及注意事项

    目录 1.背景 2.两种方式对比 2.1.一次插入一条数据 2.2.一次插入多条数据 3.拓展一下 4.Other 1.背景 我们在工作中基本都会碰到批量插入数据到DB的情况,这个时候我们就需要根据不 ...

  2. Django学习day5——创建app

    app应用与project项目的区别 一个app实现某个功能,比如博客.公共档案数据库或者简单的投票系统 一个project是配置文件和多个app的集合,这些app组合成整个站点 一个project可 ...

  3. NOIP模拟 17

    这次.. 考场刚了T1T2两题的正解 然后T2A了,T1被毒瘤卡常 让skyh得了rank1,QMQ 话说这次考试体(r)验(p)极(yong)佳(jin) 也许是昨晚没玩狼人杀睡的比较好?(现场%苗 ...

  4. 持续集成Gitlab CICD Runner&Jenkins

    目录 使用Gitlab Runner实现 再要部署的服务器上安装 gitlab runner 下载可执行文件 设置可执行权限权限 创建用户 运行服务 注册 Runner 到gitlab上找到需要用的U ...

  5. 开启docker远程访问

    开启docker远程访问 进入到/lib/systemd/system/docker.service vim /lib/systemd/system/docker.service 找到ExecStar ...

  6. MySQL-配置环境变量及修改密码(附-mysql安装教程)

    MySQL-配置环境变量和修改密码 mysql的安装教程:链接:https://pan.baidu.com/s/1rrPT2X0yRF58kN8jZZx-Mg 密码:55dh 一. 闪退问题 1.1. ...

  7. 删除Linux的依赖库并进入救援模式恢复

    删除Linux的依赖库并进入救援模式恢复 模拟一下依赖库文件被删,并进入救援模式恢复 系统:CentOS7 一.删除mv的依赖库文件 删除/lib64/libc.so.6 [root@centos7 ...

  8. netty服务端的创建

    服务端的创建 示例代码 netty源码中有一个netty-example项目,不妨以经典的EchoServer作为楔子. // 步骤1 EventLoopGroup bossGroup = new N ...

  9. 面试总问的jvm调优到底是要干什么?

    1. 压力测试的理解,xxx的性能10w/s,对你有意义么? 没有那家卖瓜的会说自己家的不甜,同样,没有哪个开源项目愿意告诉你在对它条件最苛刻的时候压力情况是多少,一般官网号称给你看的性能指标都是在最 ...

  10. [干货]AspNetCore熟练应用CancellationToken,CTO会对你刮目相看

    背景 已经有很多文章记录了 web程序中采用异步编程的优势和.Net异步编程的用法, 异步编程虽然不能解决查询数据库的瓶颈, 但是利用线程切换,能最大限度的弹性利用工作线程, 提高了web服务的响应能 ...