关于计算机图形学:游戏中的动态阴影下
本篇分为高低两篇,上篇内容请关注:游戏中的动静暗影(上) 六、基于Shadowmap实现软暗影1. Percentage-Closer Filtering(PCF)采样Shadowmap时,咱们往往这样来实现一些软暗影的成果:在指标采样点四周,进行四次采样,而后取平均值,作为最终后果。留神这里的取平均值,并不是取平均值后进行比拟,而是对四个采样点,别离进行深度测试,而后每个采样点的0或1的后果值进行均匀,这样在半影区域就能失去软暗影成果。 这种将采样后果进行均匀的形式叫做Percentage-Closer Filtering(PCF)[6],PCF通过将指标点左近的采样后果均匀,来模拟出半影的成果。 当初的硬件都间接提供四周四点采样的加权PCF深度测试,比方OpenGL中的sampler2DShadow,DirectX中的SampleCmp。这种采样的加权形式相似于一般像素采样时的双线性采样,在指标地位左近2X2像素中,逐像素进行深度比拟,失去后果值0或1,而后将后果依照绝对四周像素地位进行加权均匀。 间接应用硬件PCF,只能采样到2X2的像素点,失去的半影过渡,往往不够柔和。如果想要更加柔和的暗影过渡,或者把半影区域扩充,就须要将采样点散布范畴扩充,也须要减少采样点的个数。 简略的形式,是间接在指标点四周依照Grid模式进行采样,然而这样往往会在半影中看到分层的瑕疵。 因而咱们更加罕用的形式,是应用预计算好的Possion散布的采样点,来进行采样。为了使后果进一步平滑,咱们还能够应用逐像素的噪声值,对采样点地位进行旋转,这样每两个相邻的像素点,采样的模式都是不同的,能够无效地平滑半影区域。从左到右顺次是:4X4的Grid采样12点Possion采样12点Possion采样+旋转Possion分布图 2. PCF软暗影的Bias问题在后面咱们曾经讲过Bias的问题,在PCF采样中,因为PCF采样Shadowmap的范畴会比拟大,因而会进一步暴露出Shadow Acane的问题。当然咱们也有响应的伎俩来解决这些问题。 一种简略的形式,是依据PCF filter kernel的大小,来动静扭转Shadow Bias的大小,当然这样做的毛病也很显著,就是PCF kernel越大,就会损失越多的暗影精度信息。 另外一种形式是Bias Cone,依据以后采样点到采样核心的地位,来缩放Bias的大小,如下图右边所示。是一种绝对简略无效的缓解Bias问题的计划。左:Bias Cone;右:Receiver Plane Depth Bias 上图左边显示的一种逐采样点来做准确Bias的算法:Receiver Plane Depth Bias。这种形式须要假设承受暗影的是一个立体,而后会依据每个暗影采样点到核心的地位,来计算偏移。个别能产生十分好的后果[7]。 3. Percentage-Closer Soft Shadows(PCSS)PCF暗影的一个毛病,就是半影的宽度十分固定,无论产生暗影的地位间隔光照有多远,半影的宽度都是一样的。 PCSS[8]通过判断半影到遮挡物和半影到光源的间隔,来动静确定半影的宽度。半影宽度越大,采样暗影的模式散布也越大,就能失去越柔和的暗影。这样就能失去如下图左边所示的,随间隔变动的暗影成果。左:PCF硬暗影,中:PCF软暗影,右:PCSS暗影 PCSS算法分成这样几个步骤: 计算出区域内均匀Blocker深度;依据Blocker深度,计算出须要的半影宽度;用半影宽度,作为PCF kernal的大小,计算出暗影。PCSS的计算其实很简略,就是依据三角形类似,来计算出采样所需的散布间隔,而后将间隔内的采样值进行均匀。 不过当半影宽度十分大时,就须要十分多的采样点,这样采样Shadowmap的开销也会变大。因而PCSS是一种不太稳固的软暗影计划,在游戏中的理论利用并不是特地多。 七、基于Shadowmap的逐物体暗影/Per Obejct Shadow1. Modulated shadow的实现后面咱们讲到的立体暗影,只能投射暗影到立体上,在应用Shadowmap保留深度后,就能够将暗影投射到任意的曲面上,具体放办法如下: 首先咱们失去须要渲染暗影物体的AABB,而后将AABB转换到Light Sapce,失去新的 Orthogonal Light Space ABB。而后咱们将物体的深度渲染到一张Shadowmap中。 咱们将Light Sapce的AABB沿着光照方向进行缩短,就失去了一个Shadow Volume。 接下来咱们就能够应用这个Shadow Volume来失去投射暗影了。将Shadow Volume作为几何体进行渲染,在Shader中读取以后地位的Depth值,反算出世界坐标,再通过投影矩阵算出光照空间下的深度值,在Shadowmap中进行采样,失去暗影。将最终输入后果的混合形式为DstColor Zero,这样,被遮挡区域有暗影的地位,色彩都这样乘以一个暗影系数,失去一个染色的成果,也就实现了Modulated shadow。 留神,为了避免在不须要暗影的区域渲染出暗影,咱们须要在代码中进行clip,如果计算出Shadowmap中对应的uv坐标超出0~1的范畴,就不再渲染暗影。在Unity中实现的Shader代码大抵如下: float4 frag (v2f i) : SV_Target{float2 uv = i.vertex.xy * (_ScreenParams.zw - 1);float depth = tex2D(_CameraDepthTexture, uv).r;#if !UNITY_REVERSED_Zdepth = depth * 2 - 1;#endif#if UNITY_REVERSED_Zuv.y = 1 - uv.y;#endiffloat4 clipPos = float4(2.0f * uv - 1.0f, depth, 1.0);////反算出世界空间坐标float4 worldSpacePos = mul(UNITY_MATRIX_I_VP, clipPos);worldSpacePos /= worldSpacePos.w;////失去shadowmap中uv坐标float4 projectorPos = mul(_WorldToProjector, worldSpacePos);#if UNITY_REVERSED_ZprojectorPos.z = clamp(projectorPos.z, 0.0001, 1);#elseprojectorPos.z = clamp(projectorPos.z, 0, 0.9999);#endif// uv不在0~1范畴内,不须要暗影clip(projectorPos.xy);clip(1 - projectorPos.xy);projectorPos.xy = projectorPos.xy * _ShadowmapTex_ST.xy + _ShadowmapTex_ST.zw;float shadow = SAMPLE_TEXTURE2D_SHADOW(_H3D_GroundShadowmapTex, sampler_H3D_GroundShadowmapTex, projectorPos.xyz).r;return shadow;}Modulated shadow有这样两个显著的毛病: ...