Unity实时阴影实现图解

Wesley13
• 阅读 898

Unity大中华区技术支持团队近年来为国内Unity用户提供了高质量的技术支持服务,本文将由该团队的技术支持工程师张陈渊,根据自己的丰富经验,为大家分享在Unity中实现实时阴影的方法。

       光照在游戏作品中的重要地位早已不容忽视,阴影与光的实现技术一直在高效与真实之间左右权衡着。下面主要通过一个具体的问题探讨Unity实时阴影的内部实现。

两种阴影实现方式

      Unity有两种阴影实现方式:烘培阴影与实时阴影。

Unity实时阴影实现图解

       烘培是一种离线计算,它采用光线追踪算法来模拟现实世界中光的物理特性,如反射,折射及衰减,光无法到达的地方皆为阴影;实时阴影是一种更加精简的模拟,它忽略掉了光的众多物理特性,利用数学方法人为地去制造阴影。您可以通过下图更形象地了解到两种阴影之间的计算区别。

Unity实时阴影实现图解   

烘焙过程中,光线无法到达的地方成为阴影

Unity实时阴影实现图解  

实时阴影更像人为地绘制,将灯光角度无法看到的区域,绘制上阴影的颜色

        烘培阴影是光线追踪算法的自然产物,准确无误,真实过渡。但由于其计算量巨大,阻碍了它在游戏中的实时运用。不能实时运用并不代表光线追踪不能应用到游戏中,实际上游戏中存在大量静止的物体,如场景中的地形,房屋等,在灯光不变的情况下,这些物体产生的阴影也是固定不变的。

       由于这种需求普遍存在,Unity的烘焙系统随之诞生:通过离线计算,生成记录光照信息的Lightmap,渲染静态物体时采样Lightmap,进而产生较真实的阴影效果。对于动态物体而言,Unity使用Shadow Mapping技术,高效地产生实时阴影。由于篇幅限制,本文我们主要通过一个具体的问题探讨一下Unity实时阴影的内部实现。

实时阴影的内部实现

       有企业级客户提到这样一个问题:半透明物体的阴影效果是如何实现的?

 Unity实时阴影实现图解

       回答这个问题之前,有必要先了解Unity实现不透明物体阴影的基本原理。为了简化问题,假设场景中只有两个物体,石头A和地面B。

 Unity实时阴影实现图解

        为了最快速地分析图像问题,我们列举一下渲染流程:首先,从摄像机角度生成A与B的深度值到Depth Buffer深度缓冲中。

 Unity实时阴影实现图解

      接下来,从灯光视角生成A与B的深度到Shadowmap深度图,这里有四张深度图,可以认为是不同的精度。

  Unity实时阴影实现图解

3,4:近视角高精度;1,2:远视角低精度

        然后进入整个流程的核心部分,利用上面两个步骤的数据生成Screen Space Shadow Mask纹理,基本方法为:通过深度缓冲可将屏幕空间的点还原到世界空间,然后将此世界空间坐标转换到灯光视角空间,投影之后的Z值再与对应深度图的值进行比较,如果大于深度图,说明此屏幕空间上的点是阴影。

 Unity实时阴影实现图解

        最后一步,渲染A和B到Color Buffer颜色缓冲中,将世界空间的点转换到屏幕空间,采样Screen Space Shadow Mask纹理得出阴影,最后可根据自定义需求,与材质颜色进行相应的混合得出最终的阴影显示效果。

 Unity实时阴影实现图解

      那么,现在开始进入正题了,之所以会有人问半透明物体的阴影如何产生,其根源在于:虽然半透明物体可以不写深度缓冲,但必须写深度图,那么半透明物体的深度值如何写入深度图呢?

      众所周知,写深度这一操作,只有两种方式,写与不写:对于不透明的物体来说,深度一直写;对于半透明的物体来说,假设透明度是alpha,alpha=0不写,alpha=1写,那么alpha=0.5究竟写还是不写呢?

      带着这个问题,让我们来看看Unity的标准Shader是如何处理的。

 Unity实时阴影实现图解

        上边代码的红框决定深度是否写入一张3D纹理,如变量_DitherMaskLOD。设置变量vpos和alpha对3D纹理进行采样,vpos为正交或透视投影产生的结果,alpha为半透明物体对应的alpha值。由于vpos是变化的,即使alpha都为0.5,采样的结果也会不同,由此来决定此像素是否剔除。

       制作这张3D抖动纹理时一定要保证alpha为0或1时采样的结果是固定,alpha大于0.5时,采样到1的概率偏大,alpha小于0.5时,采样到0的概率偏大。通过查看内存,此3D纹理可视化后是这样的:

 Unity实时阴影实现图解

      讲到这里,这个问题的答案已呼之欲出:实时半透明物体阴影与不透明物体阴影在实现上并没有太多区别,只是在生成灯光视角的深度图上,半透明物体在Pixel Shader阶段会采样一张3D抖动纹理,来随机决定此像素是否输出,进而产生近似的半透明阴影效果。

      这个算法有一个缺点,由于使用视角数据来采样抖动纹理,所以当视角变化时,阴影必然会有明显的抖动。

 Unity实时阴影实现图解

   解决抖动的优化办法,留给大家去思考。

结束语

      关于Unity中实现实时阴影的问题,由于篇幅有限,本文其实只讲到了冰山一角,还有很多其他的阴影有关的话题,如烘培阴影会使用Shader吗?它会利用到GPU吗?我们会经常分享更多的Unity相关技术剖析在Unity官方中文社区(forum.china.unity3d.com),请保持关注!

      转载请注明来源:Unity官方中文社区(forum.china.unity3d.com)。请勿私自更改任何版权说明信息。

实时阴影

Unity实时阴影实现图解

2.png

Unity实时阴影实现图解

12.png

Unity实时阴影实现图解

13.png

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
Unity 4.x 2D游戏开发基础教程大学霸第一更
Unity4.x2D游戏开发基础教程大学霸第1章  Unity及其组成的介绍本书主要讲解的是,如何使用Unity开发2D游戏。但在开始讲解之前,最好先熟悉一下Unity这个工具。本章会首先介绍Unity的下载和安装,然后会介绍Unity界面的各组成部分,这些知
Wesley13 Wesley13
3年前
Unity之SDK接入(Unity与Android通信)
首先介绍一点关于Android与unity通信的知识:完成通信主要靠unity中的class.jar包(在unity的安装目录下)。在unity中调用android的方法:jo.call("方法名"\,参数名\)       其中\\代表可有可无。在endroid中调用unityu的方法:导入class.jar包,继承
Wesley13 Wesley13
3年前
Unity技术支持团队性能优化经验分享
https://mp.weixin.qq.com/s?\_\_bizMzU5MjQ1NTEwOA&mid2247490321&idx1&snf9f34407ee5c5d0d1edb478981299108&chksmfe1e2fbac969a6acee4eddb865d161fe09400c9147794c777ea4087e59f1e5fc3
Wesley13 Wesley13
3年前
Unity横屏
Android下发现Unity里面的Player设置,并不能完全有效,比如打开了自动旋转,启动的时候还是会横屏,修改XML添加以下代码<applicationandroid:icon"@drawable/ic\_launcher"                    android:label"@string/app\_name"
Wesley13 Wesley13
3年前
Unity5.6.4f1 配置WebGL教程
Unity5.6.4f1发布WebGL的配置教程步骤一:先查看自带的Unity是否yi配置好WebGL的项,若无,则可遵循以下教程来设置!(https://oscimg.oschina.net/oscnet/54612ae3d9b094f1db96b00b1c81a5fe432.png)步骤二:下图是我已经设置好的,未设置
Stella981 Stella981
3年前
OpenGL平面阴影
几种绘制阴影的方法在OpenGL中,比较常见的绘制阴影的方法有:shadowmapping,shadowvolumes以及一种在红宝书上提及的适合在确定平面上绘制阴影的方法。平面阴影在确定的平面下,可以直接通过模版测试,经过变换矩阵来显示阴影。具体的过程如下:1.开启模板测试,初始化模板缓冲区为02.模板测试设置
Wesley13 Wesley13
3年前
unity将 -u4E00 这种 编码 转汉字 方法
 unity中直接使用 JsonMapper.ToJson(对象),取到的字符串,里面汉字可能是\\u4E00类似这种其实也不用转,服务器会通过类似fastjson发序列化的方式,将json转对象,获取对象的值就是中文但是有时服务器要求将传参中字符串中类似\\u4E00这种转汉字,就需要下面 publ