Shadow - myl2232/WorkingOn GitHub Wiki

场景Shadow和角色Shadow

阴影方案分两类,场景阴影和角色自阴影。场景阴影靠一个主光,自阴影靠UniqueShadow。

  • 最早使用的是Dynamic Shadow的方案,来制作场景阴影,场景中的一个GameObject挂载Projector,由它来限定投射范围,把project范围内的对象重画一遍黑片到一个固定RT上,虽然减少了投影pass的数量,但是对美术 并不友好,需要经常调整projector的形态,总的渲染效率来看似乎并不占优势。故而最终废掉了。
  • 采用场景主光的投影,需要把被投射的地形层、建筑物层等选入,也需把需要投射阴影的对象层都选中,否则没有阴影。(另外,一个类似bug 需要注意的是,如果在Grapics Setting当中选择了Cascade Shadow,那就默认会走一个ScreenSpace shadow,但是需要开启buildin shadow,否则看不到任何阴影。不开启building ScreenSpaceshadow,只开启cascade的时候实际已经渲染了多级cascade阴影,但是在最终使用的时候没有开启SHADOW_SCREEN宏,所以没用上前面得到的cascade阴影,应该还有细节没注意到)
  • 自阴影的方案UniqueShadow,主要做法是在GameObject上挂一个MeshRender,当主相机Render的时候(或者是SceneCamera、DialogCamera等),主动调用这个组件挂载一个用作投影Camera的Render,画在一个固定RT上。 起初只挂在单一角色上,每个角色独立占用一个RT,至少512x512。后期改造为多人占用一个RT,可以至少是1024x1024。在Timeline下,可能更多需要单人自阴影,可以提供更好的细节效果。在游戏运行中,使用多人自阴影 。
  • 对于Cascade shadow,正常来说只需要在GraphicSetting当中开启,并且在Quality当中设定cascade层级(0,2,4)。基本原理上是对相机方向做多级渲染,由光照方向提出一个球体来同相机裁剪区间相交,原本应该是AABB的光源视椎体,用球体也是为了避免Flickering抖动,因为两帧之间如果相机移动过快,可能导致同样区域SM的有效分辨率变化极大,所以采用了球去做相交检测。mobile平台默认cascade为1.

https://blog.csdn.net/BugRunner/article/details/7366762 关于csm优化的一些细节,在unity当中已有实现。

  • 如果启用build-in中的ScreenSpaceShadows,会开启屏幕空间阴影。首先需要从当前摄像机处观察到的深度纹理,如果是deferred shading,那么已经有深度图了。但是因为是前向,所以会把场景全画一遍深度图。其次,从光源出发得到从该光源处观察到的深度纹理,也被称为这个光源的ShadowMap,会从屏幕空间做一次Collect Shadow。其中input是摄像机的深度纹理和光源的ShadowMap。这个过程概括来说就是把每一个像素根据它在摄像机深度纹理中的深度值得到世界空间坐标,再把它的坐标从世界空间转换到光源空间中,和光源的ShadowMap里面的深度值对比,如果大于ShadowMap中的深度距离,那么就说明光源无法照到,在阴影内。最后,在正常渲染物体为它计算阴影的时候,只需要按照当前处理的fragment在屏幕空间中的位置对得到的屏幕空间阴影图ScreenSpaceshadowMap采样就可以了。基本步骤:depth Pass-> shadow Casters->Collect shadow->draw geometry+ sample shadow。

深度图储存着投影空间下的z轴坐标,投影空间的坐标则是float4(screenUV * 2 - 1, depth, 1),再通过传入的VP矩阵的逆矩阵,也就是(projection * view).inverse,将深度转换到世界坐标,也就拥有了每个像素的世界坐标,shadowmap同理,最后比较两点距离灯光距离即可判断是否被遮挡,是否投影。 利用深度图重建世界坐标:(ScreenSpace Shadow)

inline float3 computeCameraSpacePosFromDepthAndVSInfo(v2f i)

{
float zdepth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv.xy);
// 0..1 linear depth, 0 at camera, 1 at far plane.
float depth = lerp(Linear01Depth(zdepth), zdepth, unity_OrthoParams.w);
#if defined(UNITY_REVERSED_Z)
zdepth = 1 - zdepth;
#endif
// view position calculation for perspective & ortho cases
float3 vposPersp = i.ray * depth;
float3 vposOrtho = lerp(i.orthoPosNear, i.orthoPosFar, zdepth);
// pick the perspective or ortho position as needed
float3 camPos = lerp(vposPersp, vposOrtho, unity_OrthoParams.w);
return camPos.xyz;
}