<commit_before>= 光线模拟器
:hp-tags: graphic
:hp-alt-title: a light simulator

上一章节我们最终提到光线追踪在模拟着色和灯光效果（例如反射、柔和阴影等）上比栅格化更好。没有这些效果，会使图像缺乏真实感。在深入此话题前，先来看看一些真实世界中的图像，以便更好的理解这些效果。

== 反射

image:http://www.scratchapixel.com/images/upload/rendering-3d-scene-overview/reflection.png[alt="完美镜面"]

当光线和一个完美的镜面所接触时，所得到的反射光角度是可预知的。可以通过反射法则计算出新的方向。该法则类似从墙面弹回的网球，一条光线在接触到表面后会改变方向，光线的反射方向是入射方向在入射点相对于法线的反射。更普遍的说法是，反射光线总是射离材质表面，且反射角等于入射角。如下图所示，法线和入射向量所组成的角度，等于法线和出射向量所组成的角度。注意例图中以水面作为反射表面，而实际上水和玻璃比起金属在反射上的表现要差很多。

image:http://www.scratchapixel.com/images/upload/rendering-3d-scene-overview/mirror.svg[alt="反射"]


== 透明度

光线在碰到透明物体（比如玻璃橱柜）时会发生反射和折射。

image:http://www.scratchapixel.com/images/upload/rendering-3d-scene-overview/transparency.png[alt="透明物体"]

传输作为折射中常用的术语，与其字面意思大相径庭。在此传输所指的是，一小部分入射光线进入物体的一边，而从另一边离开（这也就是为什么我们能够透过窗户看到东西）。不管怎样，在光线接触到透明物体表面的那一瞬间，方向变了，所以我们称为折射。当光线从一个透明媒介比如空气，进入另一个比如水或玻璃时，会发生弯曲效果（不管是从空气到水，还是水到空气，光线都会发生弯曲）。连同反射，折射方向可以使用**Snell法则**进行计算。多少光被反射、多少被折射则由**Fresnel等式**给定。在渲染中这两个公式非常重要。下方的图，显示了一条光线进入一块玻璃产生了折射，在离开玻璃后再次产生折射，直到最后碰到下方的表面。如果表面是一个物体，我们就可以透过玻璃看到它。


image:http://www.scratchapixel.com/images/upload/rendering-3d-scene-overview/transparent-raygraph.png[alt="折射"]
<commit_msg>Update 2015-11-14-a-light-simulator.adoc<commit_after>= 光线模拟器
:hp-tags: graphic
:hp-alt-title: a light simulator

http://www.scratchapixel.com/lessons/3d-basic-rendering/rendering-3d-scene-overview/light-simulator[原文出处]

上一章节我们最终提到光线追踪在模拟着色和灯光效果（例如反射、柔和阴影等）上比栅格化更好。没有这些效果，会使图像缺乏真实感。在深入此话题前，先来看看一些真实世界中的图像，以便更好的理解这些效果。

== 反射

image:http://www.scratchapixel.com/images/upload/rendering-3d-scene-overview/reflection.png[alt="完美镜面"]

当光线和一个完美的镜面所接触时，所得到的反射光角度是可预知的。可以通过反射法则计算出新的方向。该法则类似从墙面弹回的网球，一条光线在接触到表面后会改变方向，光线的反射方向是入射方向在入射点相对于法线的反射。更普遍的说法是，反射光线总是射离材质表面，且反射角等于入射角。如下图所示，法线和入射向量所组成的角度，等于法线和出射向量所组成的角度。注意例图中以水面作为反射表面，而实际上水和玻璃比起金属在反射上的表现要差很多。

image:http://www.scratchapixel.com/images/upload/rendering-3d-scene-overview/mirror.svg[alt="反射"]

== 透明

光线在碰到透明物体（比如玻璃橱柜）时会发生反射和折射。

image:http://www.scratchapixel.com/images/upload/rendering-3d-scene-overview/transparency.png[alt="透明物体"]

传输作为折射中常用的术语，与其字面意思大相径庭。在此传输所指的是，一小部分入射光线进入物体的一边，而从另一边离开（这也就是为什么我们能够透过窗户看到东西）。不管怎样，在光线接触到透明物体表面的那一瞬间，方向变了，所以我们称为折射。当光线从一个透明媒介比如空气，进入另一个比如水或玻璃时，会发生弯曲效果（不管是从空气到水，还是水到空气，光线都会发生弯曲）。连同反射，折射方向可以使用**Snell法则**进行计算。多少光被反射、多少被折射则由**Fresnel等式**给定。在渲染中这两个公式非常重要。下方的图，显示了一条光线进入一块玻璃产生了折射，在离开玻璃后再次产生折射，直到最后碰到下方的表面。如果表面是一个物体，我们就可以透过玻璃看到它。

image:http://www.scratchapixel.com/images/upload/rendering-3d-scene-overview/transparent-raygraph.png[alt="折射"]

== 次表面散射

image:http://www.scratchapixel.com/images/upload/rendering-3d-scene-overview/subsurface.png[alt="次表面散射"]

次表面散射是半透明在技术上的称谓。半透明表面是既不完全透明也不完全不透明的表面，但实际上，半透明物体和透明没什么关系。半透明效果在从背后观察蜡、或者由玉或大理石组成的小东西，或者一些比较薄的组织（比如皮肤，树叶）的时候特别明显。半透明就是光线在穿过材质的时候，方向发生了改变，以至于光线离开物体的点和入射点不在同一个方向上。次表面散射非常难以模拟。

== 间接漫反射

image:http://www.scratchapixel.com/images/upload/rendering-3d-scene-overview/indirectdiffuse.png[alt="间接漫反射"]

如上图所示，一些装饰用的物体表面，不朝向任何光线。它们既不朝向太远，也不朝向天空（天空可以视作一个超大光源），但它们也不是全黑的。这是怎么回事？这是因为太阳光直接照射到地面后，反弹回环境中的一些光线最终照射到了这部分接受不到阳光的物体。因为表面接受的是间接来自光源的光线，所以称为间接光。术语中的漫反射，意味着反弹的光线是漫反射的。

== 间接光镜或焦散

image:http://www.scratchapixel.com/images/upload/rendering-3d-scene-overview/caustics.png[alt="间接光镜或焦散"]

漫反射物体的反射光会照射其周围部分，类似的反射物体也可以间接的改变环境中的光线照亮其它物体。透镜或水面的波纹可以将光线聚焦成一条直线或者某种样式，这我们称为焦散（）。当光线在迪斯科的玻璃球中发生反射，在夏天的窗户上发生反射，或者强光射在玻璃上时，容易产生焦散。

== 软阴影

image:http://www.scratchapixel.com/images/upload/rendering-3d-scene-overview/softshadows.png[alt="软阴影"]

迄今为止我们讲述的大多数效果都和物体本身材质有关。而软阴影只是一个几何问题，和物体以及光源的形状、大小以及所在空间的位置有关。

大家不用为此好奇，我们之后会持续学习这些。这篇教程的关键点，是观察一些真实世界的图片，分析它们的光影效果，之后我们会重建它们。

> 记住这篇文章所讲的，漫反射在所有视角方向上显示的亮度是相等的，而镜面反射在视角方向的亮度是最亮的（如果移动镜子，会发现到图像的改变）。我们说漫反射的交互和视角无关的，而镜面交互是依赖视角的

== 光线传输和着色：两个相关但又不同的问题

之前快速浏览这些效果的原因是需要了解两件事：

* 物体怎样显示，只取决于光线和材质交互以及在空间中穿越的情况。
* 所有这些效果可以大致分为两类：
** 和显示物体的方式有关。
** 和物体接收光线的多少有关。

  前者包括，反射、透明、镜面反射、漫反射和次表面散射。后者包括间接漫反射、间接光镜和软阴影。对于第一类称为*着色*（显示物体是什么），对于第二类称为*光线传输*（光线如何在不同的材质的表面间传输）。

  关于着色，将会学习光线和材质如何交互。换句话说，就是了解从光线接触到物体的那一刻开始，到离开物体的那一刻结束之间发生的所有事情。

  关于光线传输，将会学习光线在表面之间来回反弹时候发生的事情。反射于不同的表面间是什么情况？不同的反射间（漫反射、镜面反射等）又是什么情况？光线会去哪儿？是否任何几何体都能做遮挡？有遮挡物的情况下会产生什么效果？再透彻点，光线传输感兴趣的是光线从光源到眼睛的路径（称为光线路径）。

注意着色和光线传输之间没有太明显的界限。在真实世界中，它们没有区别，所有关于光的传播都取决于从光源到眼睛之间碰到的物体。但是，在图形学中是有区别的，因为这两件事没法通过一种方法来进行有效的模拟。让我们来解释一下。

假设把整个世界以原子级别复制到计算机程序中，代码就是定义了光线和所有原子的交互规则，为了产生一副完整的图片，只能等到光线弹来弹去直到碰到我们的眼睛。这样的程序创建起来很直观，但不幸的是，以当今的技术无法轻易实现。即使拥有足够的内存装下原子级别的世界模型，也没有足够的计算能力去模拟无数光子和无数物体之间的交互。因此，需要另一种方法，而所要替换的就是最花时间的那一部分。现在比较明确的是，光从一个表面到另一个表面是直线传播的，但接触到表面后发生了什么，就很复杂了（这需要大量时间来模拟）。

因此，在图形学中，我们人为的对着色和光线传输做了区别。着色的意义在于设计一个数学模型来近似模拟光要接触的物体，之后交互部分通过物理模拟只需要花少量时间。另一方面，我们就有条件模拟光线在表面间传播的路径。有了区别，我们就可以分别针对两类问题设计策略。

模拟光线传输比模拟光和物体交互简单一点，然而，我们不能说它就是简单的。一些内部反射显然要更难模拟（焦散就是例子，这将会在下一章解释）。设计一个好的数学模型用于模拟光和表面的交互是困难，设计一个好的光线传输算法也是有挑战的（下一章将会了解）。

== 全局光照

先让我们放慢脚步。你可能会认为（通常是错觉）大多数表面之所以可见是因为收到了来自光源的光，实际上很多情况是通过来自其它表面的间接光照亮的。环绕四周，大致观察一下有多少显示区域来自直接光源（太阳、灯光等），哪些来自间接光源。间接光在真实世界中扮演者至关重要的角色，如果忽略它，照片将很难显得真实。当渲染时同时模拟直接光和间接光的，我们称为全局光照。对于光照渲染，理想上尽可能模拟所有的光照情景。所谓一个情景是指包含在场景中的物体，物体的材质，场景中的光，光的类型（是不是太阳、灯光、火焰），它们的形状以及这些物体的散射情况（这会影响光在表面间的传播）。

在图形学中，直接光和间接光是有区别的。如果不模拟间接光，还是可以通过直接光看到物体，但是不模拟直接光，图像就会一片漆黑（在以前，直接光也被称为局域光，这是为了区别于表面互相影响的全局光）。那为何还要模拟间接光呢？

实质上，看不看见是很容易做到的。光在进入眼睛前会和表面做很多交互，这些细节会在下一章解释。如今我们考虑光线追踪，我们可以解释最耗费时间的是光线和几何体相交测试这一部分。越多相交，渲染越慢。对于直接光，只需要找出（从摄像机射出）光线和几何体的交点，然后再从这些交点投出光线（阴影线）。这就是产生一张图片最基本的步骤（我们似乎可以忽略阴影，但阴影其实是非常重要的视觉线索，可以帮助我们找出物体在空间的具体位置，也能帮助我们认识物体的形状，等等）。如果想模拟间接光，大量的光线会投射进场景中，它们在物体表面间弹来弹去产生大量的信息。在直接光之上模拟间接光，不是把光线计算两遍的事情，而是数量级上升。更糟的是，当计算间接光时，我们从一点P投射出的新的光线的信息来自，场景中其它朝向P的表面反射出的光。注意到这是一个递归过程，这也就是为什么模拟间接光的耗费相当昂贵。

为什么这么困难？实际上，使用光线追踪会非常直观（但非常耗费）。下一章解释的光线追踪是模拟真实世界中光线最自然的方法。因为它提供了一种简单的方法来收集场景中光线的信息。如果系统有能力计算光和几何体的相交，那么就可以解决可视化问题，模拟直接或者间接光照。那使用栅格化，怎么收集信息呢？常见的误解是认为模拟间接光就需要光线追踪。这是不对的。别的方法也是有的（基于海量点、光子图表、抽象点光、阴影图表等。*光能传递*就是另一种计算全局光照的方法。现在已经不太用了，不过在8、90年代很流行）；这些方法在不同的场合下都有各自的优点，是光线追踪的一个不错（谈不上更好）的替代方案。不管怎样，如果系统支持光线追踪，它仍然可以算是“简单”的方法。

如上所述，光线追踪在模拟间接光上比其它方法慢。此外，光线追踪有好多种形式，都有着各自的问题（除了计算昂贵）。其中之一就是噪点。此外，在下一章讲光线传输时我们会了解到有些光照效果是非常难模拟的，还有一些方法，犹豫效率只能用于特殊情况下。比如，单纯的“反向”光线追踪，可以模拟直接光照和间接漫反射，但在模拟一些特殊光照效果，比如间接镜面反射时就不是很有效率。很快你就会认识到，单纯的光线追踪是满足不了要求的，需要发现别的替代方案。光子图表就是很好的例子，它的出现就是为了模拟焦散（一种在漫反射表面发生的镜面反射 —— 属于一种间接镜面反射），如果用光线追踪反而非常难以计算。

== 为什么光线追踪比栅格化好？好在哪里？

其实上一段落，我们已经给出了答案。但是，重说三。光线追踪相对于其它方法，比如栅格化，在真实世界的光线时更自然，更直观。并且不像其它用来解决可视化问题以及光照的方法，光线追踪可以一次性解决这两个问题。如果使用栅格化计算可视化，就需要其它方法来解决全局光照。

单纯的实现一个光线追踪是简单的，但考虑到效率，就需要很多复杂的工作。即使是现在光线追踪的计算还是昂贵的，复杂的场景会大幅增加渲染的时间。

====
image:http://www.scratchapixel.com/images/upload/rendering-3d-scene-overview/blinnlaw.jpg[alt="Blinn's Law"]

Blinn定律或者说效率增长悖论。“什么是Blinn定律？大家应该都熟悉摩尔定律。但对于图形学，Blinn定律的大致意思就是，一个动画工作室为动画的一帧投入10个小时的计算时间，那么十年之后，计算一帧仍然需要投入10个小时，不管处理器有大提升。（转自 http://www.boxtech.com[http://www.boxtech.com/])
====

所以在实际工作中，优化代码也是非常必要的（特别是在产品环境中）。即使暂时撇开技术问题，噪点问题以及模拟焦散的问题仍然存在。解决这两个问题比较粗暴的方法是，直接使用更多的光线，但这会带来更多的消耗。所以，还是有很多研究找出解决这两个问题的渲染方法。光线追踪可以和其它技术混合使用，使得更有效率的去模拟一些很难模拟的光照效果。

















{'fraction_non_alphanumeric': 0.11731843575418995, 'fraction_numerical': 0.004410467509556013, 'mean_word_length': 17.189839572192515, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 4, 'https://': 0, 'lorem ipsum': 0, 'www.': 16, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1872637', 'n_tokens_mistral': 5970, 'n_tokens_neox': 7158, 'n_words': 81}