请选择 进入手机版 | 继续访问电脑版
前言

大概在一年前(2017)的西山居开发者大会上,我当时挺疑惑一个问题:怎么理解Unity内置的Screen Space Shadow Mapping?最近我顿悟到一些理解,记录分享一下。
分析

首先,普通的Shadow Mapping的实现分为2个步骤:
1.从光源的视角渲染整个场景,获得Shadow Map,Shadow Map存放的是场景中距离光源最近的深度信息。
2.实际摄像机渲染物体,将物体从世界坐标转换到光源视角下,得到的深度与Shadow Map采样得到的深度信息比较,根据是否在阴影中做明暗渲染处理。
准确来说,上述第2步的描述只是针对Foward渲染模式——对物体做光照计算的同时做阴影计算。
Foward渲染模式是存在overdraw的,对片段的光照计算(包括阴影)可能会被浪费掉。后来的Deferred渲染模式消除了overdraw,渲染计算只对会在屏幕上显示的像素进行,也就是说光照计算是在屏幕空间里进行的,包括阴影。
The expensive lighting calculations have to execute for each visible fragment of every polygon on the screen, regardless if it overlaps or is hidden by another polygon’s fragments. If your screen has a resolution of 1024x768 (which is, by all means, not very high-res) you have nearly 800,000 pixels that need to be rendered. You could easily reach a million fragment operations every frame. Also, many of the fragments will never make it to the screen because they were removed with depth testing, and thus the lighting calculation was wasted on them.
The formula for estimating this forward rendering complexity can be written, in big O notation, as O(num_geometry_fragments * num_lights).

Deferred Rendering is a very interesting approach that reduces the object count, and in particular the total fragment count, and performs the lighting calculations on the pixels on the screen, thereby using the resolution size instead of the total fragment count.
The complexity of deferred rendering, in big O notation, is: O(screen_resolution * num_lights).
Screen Space Shadow Mapping正是Deferred渲染的阴影计算方式。那它可不可以在Foward渲染模式下用呢?答案是可以的,而且Unity的Forward渲染中默认的阴影方式也是Screen Space Shadow Mapping。
主要的步骤如下:
1.首先得到从当前摄像机处观察到的深度纹理。在延迟渲染里这张深度图本来就有,如果是前向渲染的话就需要把场景整个渲染一遍,把深度渲染到深度图中。

2.然后再从光源出发得到从该光源处观察到的深度纹理,也被称为这个光源的ShadowMap。

3.然后在屏幕空间做一次阴影收集计算(Shadows Collector),这次计算会得到一张屏幕空间阴影纹理,也就是说这张图里面需要有阴影的部分已经显示在图上了。这个过程概括来说就是把每一个像素根据它在摄像机深度纹理中的深度值得到世界空间坐标,再把它的坐标从世界空间转换到光源空间中,和光源的ShadowMap里面的深度值对比,如果大于ShadowMap中的深度距离,那么就说明光源无法照到,在阴影内。

4.最后,在正常渲染物体为它计算阴影的时候,只需要按照当前处理的fragment在屏幕空间中的位置对步骤3得到的屏幕空间阴影图采样就可以了。
实现

1.在当前摄像机出创建深度相机,得到深度纹理:
C#代码:
  1. [/code]shader代码:
  2. [code]
复制代码
Unity实时阴影实现——Screen Space Shadow Mapping-1.jpg
当前摄像机处观察到的深度纹理
2.在光源处创建深度相机,得到光源的ShadowMap:
C#代码:
  1. [/code]
  2. Unity实时阴影实现——Screen Space Shadow Mapping-2.jpg
  3. 光源摄像机处观察到的深度纹理
  4. 3.在屏幕空间做一次阴影收集:
  5. C#代码:
  6. [code]
复制代码
shader代码:
  1. [/code]其中,关键代码为[b]用深度信息重建世界坐标[/b],变换矩阵_inverseVP在C#生成传入:
  2. [code]
复制代码
接下来把世界坐标转换到光源空间中,和光源的ShadowMap里面的深度值对比,获得是否在阴影中跟普通的Shadow Mapping类似。
最后把结果输出到屏幕空间阴影纹理:
  1. [/code] Unity实时阴影实现——Screen Space Shadow Mapping-3.jpg
  2. Shadows Collector得到的屏幕空间阴影纹理
  3. 4.最后在正常渲染物体时,用[b]屏幕坐标采样屏幕空间阴影纹理[/b]:
  4. [code]
复制代码
Unity实时阴影实现——Screen Space Shadow Mapping-4.jpg
正常渲染物体时,采样屏幕空间阴影图
结论

Screen Space Shadow Mapping继承了Deferred渲染的思想——计算复杂度与场景的复杂程度无关,前提是要获得场景深度。在场景复杂且深度已有的情况下,具有很大的效率优势。

完整实现代码:
chenyong2github/ScreenSpaceShadowMapping​github.com Unity实时阴影实现——Screen Space Shadow Mapping-5.jpg 参考文献:
http://geekfaner.com/unity/blog3_ShadowMap.html
Unity基础6 Shadow Map 阴影实现
Forward Rendering vs. Deferred Rendering <br />
Unity5.X中屏幕空间阴影投射技术(Screenspace ShadowMap)如何产生阴影图?
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|翁笔

© 2001-2018 Wengbi.com

返回顶部