---
title: "【光照】光线衰减与阴影"
author: "Perrin Yong"
author_profile: https://www.pystone.net/profile/
published_by: "Perrin Yong"
canonical: https://www.pystone.net/notes/rendering-light-attenuation-shadows/
type: note
content_role: unspecified
visibility: public
id_stability: rename-stable
source_path: "10-计算机、信息技术与工程/05-游戏图形与运行时/渲染基础/【光照】光线衰减与阴影.md"
content_hash: 9afd0f95f756e0935c4917e7c938fa6c76c237205f4313328e72812909b5c86b
knowledge_version: 224c990773de.5fa8af6e39fa
site_commit: 224c990773de166d23a886306577dd90379529ce
notes_commit: 5fa8af6e39fa3891d1b9b4832bfa6c4e0ecaaf0a
---
# 【光照】光线衰减与阴影

> 创建时间：2021/3/13 21:12

  * 【光照】光线衰减与阴影
    * 光照衰减
      * 光照衰减纹理(_LightTexture0)
        * 采样方法
      * Cookie
        * 题外话——浏览器中的Cookie
        * Unity Light Cookies
    * 阴影
      * Shadow Map技术
        * 传统ShadowMap技术
        * 屏幕空间阴影映射技术(Screenspace Shadow Map)
      * 关于SubShader、Pass与Fallback的理解
      * Unity Shader实现Receive Shadow
    * Ref

## 光照衰减

### 光照衰减纹理(_LightTexture0)

使用一张纹理作为查找表，来在Fragment Shader中计算逐像素光照的衰减。

> 如果该光源使用cookie，那么衰减查找纹理是 _LightTextureB0

好处——计算衰减不依赖于数学公式的复杂性

弊端:

  * 需要预处理得到采样纹理

  * 纹理的大小会影响衰减的精度

  * 不直观，不方便

#### 采样方法

通常只关心 _LightTexture0对角线上的纹理颜色值。(0, 0) ~ (1, 1)表明光源位置点到光照的最远点的衰减。

```cpp
string s = String.Empty;

```

使用顶点距离的平方进行采样，避免开方操作。
使用宏 UNITY_ATTEN_CHANNEL得到衰减纹理中衰减值所在的分量。

### Cookie

#### 题外话——浏览器中的Cookie

Cookies - 小型文本文件。是某些网站为了辨别用户身份，进行Session跟踪而储存在用户本地终端上的数据（通常经过加密），由用户客户端计算机暂时或永久保存的信息。
特性：

  * Cookie 是浏览器访问服务器后，服务器传给浏览器的一段数据。

  * 浏览器需要保存这段数据，不得轻易删除。

  * 此后每次浏览器访问该服务器，都必须带上这段数据。

作用：

  * 识别用户身份。

  * 记录历史——当 A 在上面将商品 A1 、A2 加入购物车时，JS 可以改写 Cookie，改为「uid=1; cart=A1,A2」，表示购物车里有 A1 和 A2 两样商品了。这样一来，当用户关闭网页，过三天再打开网页的时候，依然可以看到 A1、A2 躺在购物车里，因为浏览器并不会无缘无故地删除这个 Cookie。借此，就达到里记录用户操作历史的目的了。

#### Unity Light Cookies

A cookie is a mask that you place on a Light to create a shadow with a specific shape or color, which changes the appearance and intensity of the Light. Cookies are an efficient way of simulating complex lighting effects with minimal or no runtime performance impact.

Effects you can simulate with cookies include caustics, soft shadows, and light shapes.

<https://docs.unity3d.com/Manual/Cookies.html>

## 阴影

### Shadow Map技术

原理——把摄像机的位置放在与光源重合的位置上，那么场景中该光源的阴影区域就是哪些摄像机看不到的地方。

ShadowMap（阴影映射纹理）——本质上是一张深度图——该光源的位置触发，能看到的场景中距离他最近的表面位置

ShadowCaster Pass —— Unity使用一个额外的Pass来更新Shadow Map。这个Pass的RenderTarget是ShadowMap（或深度纹理）

当开启了光源的阴影效果后，引擎会先在当前渲染物体的Unity Shader中找到LightMode为ShadowCaster的Pass，如果没有，就会在Fallback指定的UnityShader中继续寻找，如果仍然没有找到，该物体就无法像其他物体投射阴影（该物体可以接受来自其他物体的阴影）。

> 一个物体的Shader的Shadow Caster Pass影响的是其他物体

当找到一个ShadowCaster Pass后，Unity会使用该Pass来更新光源的ShaderMap

  * 物体接收阴影 - 在Shader中对阴影纹理进行进行采样，把采样结果和光照结果相乘。（Mesh Renderer开启Receive Shadows）

  * 物体投射阴影 - 把物体加入到光源的ShadowMap计算中（ShadowCaster Pass）（Mesh Renderer开启Cast Shadows）

#### 传统ShadowMap技术

顶点位置变换到光源空间下
xy分量对ShadowMap进行采样 => 得到该位置的深度信息
如果深度值效于该顶点的深度值（z） =>（能看到的地方比该顶点距相机的距离更进，更浅） => 该点位于阴影中

#### 屏幕空间阴影映射技术(Screenspace Shadow Map)

要求：显卡支持MRT(有些移动平台不支持)

Shader Caster Pass

  * 得到可投射阴影的光源的Shadow Map 与 摄像机（实际看场景的摄像机）的深度纹理

  * Shadow Map + 摄像机的深度纹理 -> 屏幕空间的阴影图

> 摄像机的深度图中记录的点转换到世界空间，再转换到光照空间，得到光照空间的深度，如果深度大于ShadowMap上对应点的深度，就说明该表面虽然是可见的，但是却处于该光源的阴影中

  * 阴影图包含了屏幕空间中所有有阴影的区域。如果我们想要一个物体接收来自其他物体的阴影，只需要再Shader中对阴影图进行采样（表面坐标转换到屏幕空间中）

### 关于SubShader、Pass与Fallback的理解

现象：Mesh Renderer开启Cast Shadows，如果Shader中没有ShadowCaster Pass，会在Fallback “Specular”中找是否有合适的Pass来执行。原Shader文件的SubShader为 #0，且只有一个SubShader，那么第一个Fallback中的SubShader的序号为#1，如果这个SubShader中也没有ShadowCaster Pass，则会在这个Shader的Fallback “VertexLit”中继续寻找，找到的下一个SubShader序号为#2。而渲染路径中的其他Pass仍然使用SubShader #0中的 （如Base Pass 与 Add Pass）。因此，Fallback只是找到合适的Pass来执行，并不会替换整个Subshader，其他Pass还用该Shader中定义的Pass。

总结：一个Shader文件中，会有多个SubShader，还会有Fallback，Fallback中的Shader也属于这个Shader文件的SubShader。渲染的任务是以Pass为核心的，引擎根据渲染路径，按顺序执行不同的Pass 。在渲染时，引擎会根据硬件、API的支持性、渲染路径等，在第一个SubShader(#0)中找到合适的Pass来执行，如果第一个SubShader中没有，则会在后面的SubShader中继续寻找。总之，所有的SubShader中，只要有一个Pass能用就行，引擎会使用第一个SubShader的Pass来进行渲染工作。如果一直找不到，该Pass就无法执行。

### Unity Shader实现Receive Shadow

![Alt text](/media/099001890d7caa2f795c.png)

![Alt text](/media/95d7f5e26b005f397d35.png)

![Alt text](/media/37df27d08d859ec4b556.png)

这些宏的实现会根据宏定义的不同而不同。

![Alt text](/media/2b8a1fb785f922dc8110.png)

![Alt text](/media/1bfc2041221d00a2de1c.png)

## Ref

<https://zhuanlan.zhihu.com/p/22396872>
