Unity3D 基于ShadowMap的平滑硬阴影
前言
传统的ShadowMap在明暗边缘处都会有很难看的锯齿,因此一般得到的结果会比较难看,常规的解决办法都会在使用ShadowMap渲染阴影的时候通过背面剔除把这种缺陷隐藏掉,最后剩下一个影子。但是这样一来,自阴影就会丢失,因而传统的做法又会通过局部光照来重新为这个物体添加上部分自阴影,也就是咱们常见的Phone光照模型、Blinn-Phone光照模型。而本文决定通过文献[1]的一个平滑方法把ShadowMap在明暗边缘处的锯齿消除,并和光照模型求并,最后得到了一个包含丰富平滑自阴影效果。
本文读者默认为有图形学基础、编写Shader基础和ShadowMap基本原理,若没有请先去把这些基础学习一下,再来阅读本文,否则可能会有阅读障碍。
一、ShadowMap和局部光照模型的缺陷
(1) 传统ShadowMap能产生丰富的自阴影,但在明暗边缘处都会有很难看的锯齿和走样问题,如图1.1。
图1.1 ShadowMap的缺陷
(2) 局部光照模型在在明暗边缘处能产生非常平滑的阴影,但只能产生有限的自阴影,缺陷表现为丢失部分自阴影和投射阴影,如图1.2。
图1.2 局部光照模型的缺陷
二、ShadowMap缺陷分析
ShadowMap产生这种锯齿缺陷的原因是,光照摄像机的方向和模型中边缘处的三角面接近平行,导致这些三角面没有映射到任何像素点。如下图2.1、图2.2
图2.1 以光照摄像机的正交投影视角观察需产生阴影的物体
图2.2 以自由的正交投影视角观察需产生阴影的物体
图2.1渲染出来的图像就是ShadowMap,图2.2是以不同的角度观察ShadowMap渲染的物体。
两张图都是是同一个渲染方法,只是观察角度不同。即:把光照方向和三角面法向量的点乘结果大于0的三角面渲染出来的结果,也就是隐藏对于光源摄像机不可见部分。这样大致上能模拟得出构成ShadowMap的所有必须的三角面。
从人的视觉上看图2.1很完美,理论上也就认为能产生完美的阴影,但实际操作的时候就会发现结果和自己想的不一样。结合两张图得出,造成ShadowMap明暗边缘锯齿的罪魁祸首是模型的三角化导致的。因此不管怎么增加ShadowMap的分辨率都是没用的。
图2.3 ShadowMap的缺陷指示图
目前市面上的模型基本都是三角面构成的,不可能因为这个问题就废弃掉。虽然学术上有一种把三角面模型体素化的方法把模型转换成控件中的一个个有体积微小正方体,但貌似并不常用。因此问题怎么消除这些锯齿是本文的重点。
三、ShadowMap和局部光照模型的并集
那么仔细观察两种模型产生的阴影缺陷后,把两者求并集后是否就能即拥有局部光照般的边缘平滑度,又有ShadowMap丰富的阴影呢?立马动手实现,如下图3.1
图3.1 ShadowMap和局部光照模型求并
如果这样的效果能接受的话,那么到此就结束了。本人在翻了一番国内学术后发现,也是到这一步就结束了,后续貌似没人再做更多的工作。但其实还可以进一步把平滑做得更完美。
四、ShadowMap明暗边界的平滑
4.1 构造明暗边界线
ShadowMap的锯齿原因是由于在明暗边界的地方三角面不完整,导致深度呈锯齿状起伏,因此只要把明暗交接的地方的深度值(像素值)用同一个深度值覆盖就能获取到非常平滑的明暗边界。
在文献[1]~文献[3]中都阐述到了同一种,方向向量与模型网格(Mesh)在边缘处求边缘线的方法。由于本人未对其做深入研究,仅知道通过其提供的公式即可求出边缘线,进而可构造出比较完美的明暗边界边,理论就不多说了免得班门弄斧,建议直接去看原文,不看那就直接抄本人写的代码。效果如下图4.1~图4.3:
图4.1 ShadowMap+明暗交界线(红色)
图4.2 局部光照+明暗交界线(红色)
图4.2 复杂模型+局部光照+明暗交界线(红色)
此边缘线基本上就是局部光照模型的明暗交接的比较完美的逼近了,甚至还比局部光照还能更平滑,这都是得益于数学上的赫米特(Hiemite)插值法。
4.2 平滑ShadowMap明暗边界的深度值
实际上通过文献[1]~文献[3]求出来的是一个一个轮廓三角形上的一条线段,最后把所有这些线段合并起来就得到了明暗边界线,那么我们就可以通过这些点构造出一条针对于光照摄像机的可控粗细的线条。如下图4.2.1、图4.2.2
图 4.2.1 其他视角
图 4.2.2 光照摄像机视角
具体实现步骤如下:
1.通过明暗边界线的2个点的位置及其单位法向量(注:这2个数据都可以通过文献[1]~文献[3]计算得到)构造出2个各自沿着单位法向量负方向位移一段距离的点,以及2个各自沿着单位法向量正方向位移一段距离的点。
2.通过步骤1得到的4个点构造出2个三角面,进而构造出1个四角面。
这样我们就得到了一个针对于光照摄像机的可控粗细的明暗边界线,接下来就是如何进行正确的覆盖ShadowMap中锯齿状起伏的深度值。
4.3 覆盖ShadowMap中锯齿状起伏的深度值
关于这一块本人目前没有想到太好的办法,目前的做法是把这些明暗边界线往光照方向的负方向位移一段距离来覆盖锯齿状起伏的深度值,效果看起来还不错。
图4.3.1 ShadowMap+平滑明暗边界的深度值
图4.3.2 ShadowMap+平滑明暗边界的深度值+局部光照
图4.3.3 ShadowMap+平滑明暗边界的深度值+局部光照+复杂模型
图4.3.4 平滑明暗边界的ShadowMap
可以看到仅仅使用ShadowMap就非常接近局部光照阴影的平滑程度了,并且还拥有丰富的全局阴影。但由于只是简单的把这些明暗边界线往光照方向的负方向位移一段距离来覆盖锯齿状起伏的深度值,因此还是有一点点的小缺陷。如果到这里已经满足了的话,我建议再把局部光照加上去,因为局部光照算法非常简单,1次点乘+1次step即可得到结果,再与本文方法求并,就能得到效果很不错的阴影了,如图4.3.2和图4.3.3。
五、实现以及用途
说了这么多,真正动手去实现的时候会发现,并没有增加多少复杂度,仅仅在传统的ShadowMap的基础上,在渲染ShadowMap的时候增加几何着色器即可。代码部分不过多说明,后面会给出基于Unity3D的源码。
那么这种硬阴影有什么用呢,甚至不惜增加一定的复杂度?\本文认为这种阴影在卡通渲染上是十分有用的,因为卡通的颜色并不需要过多的渐变,一般只需要明暗2种颜色即可,而卡通渲染又需要丰富阴影,因此将其运用到卡通渲染上是用途之一,如下图5.1。
图5.1 本文算法+明暗贴图+复杂模型
六、结束语
虽然是这么说,但实际上二次元精美的插画都有一定程度的渐变,这是人为主观意识来添加的。在计算机上要实时实现这中渐变,并且任何角度观察都能达到插画级的精美程度是很困难的,这是因为插画的绘画人自己也说不出这个数学模型,在计算机里没有数学模型就不存在合理性,没有合理性就很难模拟,因此一般都只能用大量人力一帧一帧地把画面画出来。
目前顶尖水平的卡通渲染是以GuiltyGearXrd为首的渲染方法,使用局部光照阴影,并通过大量人力物力对某个物体在各个角度做类似法线贴图的贴图。这是无法复制的,一次创作需要消耗大量人力物力。这种方式产生的阴影在物体形变大的时候会产生凌乱的阴影。因此制作人一般都会想办法遮掩这部分缺陷,比如减少物体的形变动作,或者把镜头放到你看不到这些缺陷的地方。如果哪天实时全局光照烂大街了,那么插画级卡通渲染或许就会来临吧。
感谢学术界大佬们的精彩文章,本文到此结束,谢谢。
附:源码
参考文献
Silhouette Smoothing for Real-time Rendering of Mesh Surfaces
基于GPU的网格模型平滑阴影的实时绘制
三角网格模型平滑阴影的实时绘制
Unity3D 基于ShadowMap的平滑硬阴影的更多相关文章
- Unity3d 基于物理渲染Physically-Based Rendering之最终篇
前情提要: 讲求基本算法 Unity3d 基于物理渲染Physically-Based Rendering之specular BRDF plus篇 Unity3d 基于物理渲染Physically-B ...
- Unity3D 基于预设(Prefab)的泛型对象池实现
背景 在研究Inventory Pro插件的时候,发现老外实现的一个泛型对象池,觉得设计的小巧实用,不敢私藏,特此共享出来. 以前也看过很多博友关于对象池的总结分享,但是世界这么大,这么复杂到底什么样 ...
- 【转】Unity3D 关于贝赛尔曲线,平滑曲线,平滑路径,动态曲线
http://tieba.baidu.com/p/2460036481 很多时候我们需要的并不是直线和折线,而是平滑的曲线,比如寻路系统,某些物体的曲线运动,都需要平滑曲线来保证效果,今天试了一下,通 ...
- unity3d 基于物理渲染的问题解决
最近1个月做了unity 次世代开发的一些程序方面的支持工作,当然也是基于物理渲染相关的,主要还是skyshop marmoset的使用吧,他算是unity4.x版本 PBR的优秀方案之一了但在使用以 ...
- Unity3d 基于物理渲染Physically-Based Rendering之specular BRDF
在实时渲染中Physically-Based Rendering(PBR)中文为基于物理的渲染它能为渲染的物体带来更真实的效果,而且能量守恒 稍微解释一下字母的意思,为对后文的理解有帮助,从右到左L为 ...
- Unity3d 基于物理渲染Physically-Based Rendering之实现
根据前文的例子http://blog.csdn.net/wolf96/article/details/44172243(不弄超链接了审核太慢)弄一下真正的基于物理的渲染逃了节课= =,弄了一下.公式和 ...
- GJM: Unity3D基于Socket通讯例子 [转载]
首先创建一个C# 控制台应用程序, 直接服务器端代码丢进去,然后再到Unity 里面建立一个工程,把客户端代码挂到相机上,运行服务端,再运行客户端. 高手勿喷!~! 完全源码已经奉上,大家开始研究吧! ...
- Unity3d基于Socket通讯例子(转)
按语:按照下文,服务端利用网络测试工具,把下面客户端代码放到U3D中摄像机上,运行结果正确. http://www.manew.com/thread-102109-1-1.html 在一个网站上看到有 ...
- unity3d 制造自己的水体water effect(二)
前篇:unity3d 制造自己的水体water effect(一) 曲面细分:Unity3d 使用DX11的曲面细分 PBR: 讲求基本算法 Unity3d 基于物理渲染Physically-Base ...
随机推荐
- @GetMapping、@PostMapping和@RequestMapping的区别
@GetMapping 用于将Http Get 请求映射到特定处理程序方法的注释.具体来说就是:@GetMapping是一个作为快捷方式的组合注释 @RequestMapping(method = R ...
- windos10专业版激活(可用)
电脑提示Windows许可证即将到期,于是自己就在网上找了一些教程,但是并没有激活成功,反而由即将到期变为了通知状态,尝试了各种密钥都不行,也下载了激活工具如暴风激活工具,KMS都不管用,尝试了好多方 ...
- 【iOS】Xcode 使用 CocoaPods 导入第三方库后没有提示
Github 上下载的开源项目,运行后出现的 [iOS]build diff: /../Podfile.lock: No such file or directory 解决后,又出现了这个问题. 解决 ...
- 利用dockerfile 安装一个tomcat7
FROM docker.io/centos #定义自己的说明 MAINTAINER jim ming "107420988@qq.com" #切换镜像目录,进入/usr/local ...
- 用大白话告诉你 :Java 后端到底是在做什么?
阅读本文大概需要 6 分钟. 作者:黄小斜 新手程序员通常会走入一个误区,就是认为学习了一门语言,就可以称为是某某语言工程师了.但事实上真的是这样吗?其实并非如此. 今天我们就来聊一聊,Java 开发 ...
- 定时延时设计FPGA
以50MHZ时钟为例,进行1秒钟延时,并输出延时使能信号. 首先计算需要多少次计时,MHZ=10的六次方HZ.T=20ns 一秒钟需要计时次数为5的七次方即5000_0000. 然后计算需要几位的寄存 ...
- Maven安装配置及其插件m2e(Eclipse Indigo 和 MyEclipse8.5)的安装配置
Maven安装配置及其插件m2e(Eclipse Indigo 和 MyEclipse8.5)的安装配置 系统:Windows7 使用软件: Maven3.0.3 + Eclipse Indigo ...
- mysql中防止sql注入
什么是sql注入 图片来源:百度百科 python 操作mysql产生sql注入问题 不用ORM框架,框架中已经集成了防范sql注入的功能,使用pymysql实践一下: # 导入pymysql模块 i ...
- 【POJ - 2139】Six Degrees of Cowvin Bacon (Floyd算法求最短路)
Six Degrees of Cowvin Bacon Descriptions 数学课上,WNJXYK忽然发现人缘也是可以被量化的,我们用一个人到其他所有人的平均距离来量化计算. 在这里定义人与人的 ...
- 实现API优先设计的重要性和实现方式
应用API优先的方法意味着设计API时,使其具有一致性和适应性,无论应用于哪些开发项目.对API使用API描述语言(如OpenAPI)是关键,因为它有助于建立API与其他程序通信的枢纽,即使这些系 ...