上一篇记录了在Unity中阴影映射的标准流程实现,本篇博客一起来看看阴影映射存在的自遮挡/自阴影问题及其优化方案。
因为在之前学习202的过程中也有记录这方面的内容,所以这里结合我的GAMES202作业1-实现过程详细步骤这篇博客以及百人计划实时阴影那节课再来巩固一下。
由于阴影映射的分辨率有限,或者说采样的时候做判断时“比大小”的过程数值精度比较会有偏差,难免会造成不正确的自遮挡阴影,关键在于“自”!是指物体自己表面上出现了本不该有的阴影锯齿。而且这种不正确的自遮挡阴影往往会出现在物体和光线靠近的那种边缘处,出现这种神奇的自遮挡锯齿:(下图是202那篇文章中我的作业展示图)

这种问题一般称作阴影瑕疵(Shadow Acne),甚至更加形象的称为“阴影粉刺 Surface Acne”,也有叫做Z-Fighting,总之都是表达的上述自遮挡的错误现象。
让我们拿Games202和百人计划的图一起做例子:

左右两个图里红色的线可以理解成shadowmap中每个texel记录的深度值的大小,那么texel涵盖物体表面的区域内的深度值都将会是一样的(都取的是左图所示中间红色着色点的深度值)。
那么我们取一个后面部分“被迫涵盖的”蓝色着色点来看:从摄像机视角记录的场景中的深度值记为zp,但它采样shadowmap得到的值zs由于被迫一视同仁而直接取了红色着色点的深度值,于是zp>zs,蓝色点就被列入了阴影中。
根据上述原理不难法线,当光线和物体表面法线夹角越大,一个texel的遮盖区域将越多,shadow acne问题将越严重。
即从shadowmap本身的采样分辨率出发的——提高shadowmap的分辨率(直接体现为texel尺寸变小,即上面图中的红色线变短),我们随便从Unity中搭一个Cube,看看选择不同shadowmap分辨率的效果,下图上为Unity提供的最低分辨率;下为最高分辨率:


但!这个方法太不实际了!且效果还是不如人意(图里还是有一点点锯齿的感觉)。而且在面对一个大世界下的场景,shadowmap再怎么大也还是会出现自遮挡,考虑到性能很多游戏对于纹理大小是有限制的,这个方法直接被PASS掉了。
参考自适应Shadow Bias算法 - 知乎 (zhihu.com)
Shadow Mapping Summary – Part 1 – The Witness (the-witness.net)
接下来就是最常用的方法。回想起101的作业,代码中凡是涉及到“数值比大小”的内容,都会考虑数值精度问题给一个容许偏差(或者叫容错阈值),即常说的bias。那么既然上述阴影瑕疵的产生原因也是数值偏差问题,解决办法就也会是设置一个bias,一般会设置:
但值得注意的是,当这个Bias设置过大时会出现漏光现象(即后面会讲到的Peter Panning),所以这个值是很难一下子适配到场景中的所有物体的,只能尽量取一个合适的大小。
那么这个值怎么给呢?还是拿上面的图:

通用做法是采样shadowmap进行阴影深度测试时,让红线向远离光源方向偏移一个bias,即zs+bias,那摄像机获得的深度值zp和zs的判断就从zp>zs变成zp<zs+bias,这样一来蓝色点就在光源能照射到的范围里啦!
一些必要的参数解释
这部分是参考百人计划里老师给出的一些解释,我认为很用必要:


第一种是直接朝着光源的方向平移一段距离就行!由于这个bias的大小牵扯到Light的透视矩阵和shadowmap的分辨率大小,所以bias还需要经过一些计算,我这里直接截图第一篇参考文章里的了,可以自行查看:

因为深度判断是在着色器中对整个场景中每个片元都适用的,而且实际中大多数引擎实现Slope Bias时计算bias不会像上面那么复杂,都会给一个固定的bias, 每个片元都会执行这个偏移。用Slope Depth的方法由于不仅如果这个偏移量过大,很有可能让原本就应该处于阴影中的片元被错误判断进了光源照得到的区域,这就会造成阴影悬浮(Peter Panning)问题,例如下图,右图相对于左图给的bias大了很多,右图就会出现阴影悬浮的问题。


为了修正深度偏移bias过大带来的悬浮问题,Normal Bias被提出。

计算公式同样参考:

Light的ShadowMap参数
我们先来看看Light组件包含的ShadowMap相关的参数:

Unity把偏移bias这些操作都包装起来了,我们可以在UnityCG.cginc中找到下面的UnityClipSpaceShadowCasterPos,这个就是应用NormalBias的:
float4 UnityClipSpaceShadowCasterPos(float4 vertex, float3 normal)
{
//获得世界空间下的顶点坐标
float4 wPos = mul(unity_ObjectToWorld, vertex);
if (unity_LightShadowBias.z != 0.0)
{
float3 wNormal = UnityObjectToWorldNormal(normal);
float3 wLight = normalize(UnityWorldSpaceLightDir(wPos.xyz));
float shadowCos = dot(wNormal, wLight);
float shadowSine = sqrt(1-shadowCos*shadowCos);
float normalBias = unity_LightShadowBias.z * shadowSine;
wPos.xyz -= wNormal * normalBias;
}
//世界空间 -> 裁剪空间
return mul(UNITY_MATRIX_VP, wPos);
}
可以发现Unity是在Shadow Caster阶段生成屏幕空间阴影纹理时,把顶点位置朝着法线方向偏移了一个bias,这个顶点再参与到后续的采样过程。
重点来看看这个unity_LightShadowBias,当我把Normal Bias设置成0.2时,打开Frame Debug可以看到:

可以推断,Unity内部会根据我们输入的bias值去计算出考虑分辨率和透视矩阵的正确的bias大小。至于Normal Bias相关的计算,就是一个简单的计算cos再算出sin最后应用在法线方向,这里不再赘述。
Unity同样提供了深度偏移,偏移裁剪空间的深度值:
float4 UnityApplyLinearShadowBias(float4 clipPos)
{
#if !(defined(SHADOWS_CUBE) && defined(SHADOWS_CUBE_IN_DEPTH_TEX))
#if defined(UNITY_REVERSED_Z)
clipPos.z += max(-1, min(unity_LightShadowBias.x / clipPos.w, 0));
#else
clipPos.z += saturate(unity_LightShadowBias.x/clipPos.w);
#endif
#endif
...
如果我们在Light组件中给Bias一个值:

此时参与真正计算的unity_LightShadowBias.x是个负数:

相当于也是把顶点的深度值减小了,这样跟shadowmap深度值对比的时候,顶点就从阴影里变到了可以接受光照的区域了。
在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',
?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------
本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01 客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02 数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit
目录1.AdmobSDK下载地址2.将下载好的unityPackagesdk导入到unity里编辑 3.解析依赖到项目中
Unity自动旋转动画1.开门需要门把手先动,门再动2.关门需要门先动,门把手再动3.中途播放过程中不可以再次进行操作觉得太复杂?查看我的文章开关门简易进阶版效果:如果这个门可以直接打开的话,就不需要放置"门把手"如果门把手还有钥匙需要旋转,那就可以把钥匙放在门把手的"门把手",理论上是可以无限套娃的可调整参数有:角度,反向,轴向,速度运行时点击Test进行测试自己写的代码比较垃圾,命名与结构比较拉,高手轻点喷,新手有类似的需求可以拿去做参考上代码usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;u
我有一个应用程序正在从Ruby迁移到JRuby(由于需要通过Java提供更好的Web服务安全支持)。我使用的gem之一是daemons创建后台作业。问题在于它使用fork+exec来创建后台进程,但这对JRuby来说是禁忌。那么-是否有用于创建后台作业的替代gem/wrapper?我目前的想法是只从shell脚本调用rake并让rake任务永远运行......提前致谢,克里斯。更新我们目前正在使用几个与Java线程相关的包装器,即https://github.com/jmettraux/rufus-scheduler和https://github.com/philostler/acts
写在之前Shader变体、Shader属性定义技巧、自定义材质面板,这三个知识点任何一个单拿出来都是一套知识体系,不能一概而论,本文章目的在于将学习和实际工作中遇见的问题进行总结,类似于网络笔记之用,方便后续回顾查看,如有以偏概全、不祥不尽之处,还望海涵。1、Shader变体先看一段代码......Properties{ [KeywordEnum(on,off)]USL_USE_COL("IsUseColorMixTex?",int)=0 [Toggle(IS_RED_ON)]_IsRed("IsRed?",int)=0}......//中间省略,后续会有完整代码 #pragmamulti_c
三分钟集成Tap防沉迷SDK(Unity版)一、SDK介绍基于国家对上线所有游戏必须增加防沉迷功能的政策下,TapTap推出防沉迷SDK,供游戏开发者进行接入;允许未成年用户在周五、六、日以及法定节假日晚上8:00-9:00进行游戏,防沉谜时间段进入游戏会弹窗进行提示!开发环境要求:Unity2019.4或更高版本iOS10或更高版本Android5.0(APIlevel21)或更高版本🔗Unity集成Demo参考链接🔗UnityTapSDK功能体验APK下载链接二、集成前准备1.创建应用进入开发者后台,按照提示开始创建应用;2.开通服务在使用TDS实名认证和防沉迷服务之前,需要在上面创建的应
写在前面前两天学习并整理的大气散射基础知识:【Unity大气渲染】关于单次大气散射的理论知识,收获了很多,但不得不承认的是,这其实已经是最早的、90年代的非常古老的方法了,后来也出现了一些优化性的计算思路和方法。因此,我打算先不急着跟各种教程在Unity中实现大气散射,而是再花时间来看看最近的游戏是如何去实现大气渲染的:06.游戏中地形大气和云的渲染(下)|GAMES104-现代游戏引擎:从入门到实践接下来就跟着GAMES104讲地形大气和云渲染的部分学习并做简单的记录,涉及到之前没提到的Mie散射也只选择直接截图PPT的方式记录啦!毕竟对于做作品来说,之后实现出来才是重要的~当然,May佬的
关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于StackOverflow来说是偏离主题的,因为它们往往会吸引自以为是的答案和垃圾邮件。相反,describetheproblem以及迄今为止为解决该问题所做的工作。关闭9年前。Improvethisquestion我想知道是否有人知道Ruby的rubyzip替代品,它可以处理各种格式,特别是zip/rar/7z?我知道libarchive,但它对我的目的来说并不完整(它是一个很好的gem)。(澄清一下,libarchive-对我不起作用-因为