关于SHADOWS_SCREEN

作者: 恶毒的狗 | 来源:发表于2019-12-19 16:52 被阅读0次

关于SHADOWS_SCREEN

在读Unity内置shader源码的过程中,我们经常看到这样的代码:

#if SHADOWS_SCREEN       
    // xxx
#else
    // xxx
#endif

那么,上面代码中的 SHADOWS_SCREEN 代表的是什么呢?

通过仔细阅读代码发现,SHADOWS_SCREEN 代表的是 屏幕空间阴影

URP 管线 主灯的实时阴影 为例,我们看一下相关代码:

主灯影衰减的入口

Light GetMainLight(float4 shadowCoord)
{
    Light light = GetMainLight();
    light.shadowAttenuation = MainLightRealtimeShadow(shadowCoord);
    return light;
}

主灯实时阴影的主要函数

half MainLightRealtimeShadow(float4 shadowCoord)
{
#if !defined(_MAIN_LIGHT_SHADOWS) || defined(_RECEIVE_SHADOWS_OFF)
    return 1.0h;
#endif

#if SHADOWS_SCREEN
    return SampleScreenSpaceShadowmap(shadowCoord);
#else
    ShadowSamplingData shadowSamplingData = GetMainLightShadowSamplingData();
    half4 shadowParams = GetMainLightShadowParams();
    return SampleShadowmap(TEXTURE2D_ARGS(_MainLightShadowmapTexture, sampler_MainLightShadowmapTexture), shadowCoord, shadowSamplingData, shadowParams, false);
#endif
}

这里我们看到,如果定义了 SHADOWS_SCREEN,那么会走 屏幕空间阴影 的采样流程,否则,走 ShadowMap 的采样流程。

SHADOWS_SCREEN的定义

那么 SHADOWS_SCREEN 在什么情况下会被定义呢?我们继续看代码:

#ifndef SHADOWS_SCREEN
    #if defined(_MAIN_LIGHT_SHADOWS) && defined(_MAIN_LIGHT_SHADOWS_CASCADE) && !defined(SHADER_API_GLES)
        #define SHADOWS_SCREEN 1
    #else
        #define SHADOWS_SCREEN 0
    #endif
#endif

这里是否采用 屏幕空间阴影,主要取决于是否开启了 阴影级联

对于大部分使用 SHADER_API_GLESAndroid 设备来说,也会关闭 屏幕空间阴影


开启屏幕空间阴影的渲染流程

Unity针对 GLES 设备关闭 屏幕空间阴影,说明它的渲染开销较高。

屏幕空间阴影 多用于 延迟渲染,不过 URP 还是一个 前向渲染 的流程,那么我们就来看一下开启 屏幕空间阴影URP 的渲染流程。

以Unity自带的 SampleScene 为例,并且只考虑 一盏主灯 的情况,如下图:

screenshot1.png

这里开了 2级阴影级联ShadowMap 的分辨率是 1024Shadow Distance64

第一步,绘制ShadowMap

因为开了 2级级联,实际创建的 ShadowMap 的分辨率是 1024 x 512,左边的 512 x 512 绘制 0级阴影,右边的 512 x 512 绘制 1级阴影,如下:

screenshot2.png

要写入 ShadowMap,shader必须有 ShadowCaster 这个Pass。

第二步,绘制CameraDepthTexture

要生成 屏幕空间阴影 贴图,我们需要通过 屏幕坐标当前像素的深度 还原出屏幕上一个点的 世界坐标

而要获取 当前像素的深度,我们就必须依靠深度贴图 CameraDepthTexture,如下:

screenshot3.png

要写入 CameraDepthTexture,shader必须有 DepthOnly 这个Pass。

对于 移动设备 来说,如果场景比较复杂,drawcall 会大幅增加,这一步会比较昂贵。

第三步,生成屏幕空间阴影贴图

有了 ShadowMapCameraDepthTexture,就可以生成名为 _ScreenSpaceShadowmapTexture屏幕空间阴影贴图 了:

screenshot4.png

生成的代码见 Shaders/Utils/ScreenSpaceShadows.shader

half4 Fragment(Varyings input) : SV_Target
{
    UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);

    float deviceDepth = SAMPLE_TEXTURE2D_X(_CameraDepthTexture, sampler_CameraDepthTexture, input.uv.xy).r;

    #if UNITY_REVERSED_Z
            deviceDepth = 1 - deviceDepth;
    #endif
    deviceDepth = 2 * deviceDepth - 1; //NOTE: Currently must massage depth before computing CS position.

    float3 vpos = ComputeViewSpacePosition(input.uv.zw, deviceDepth, unity_CameraInvProjection);
    float3 wpos = mul(unity_CameraToWorld, float4(vpos, 1)).xyz;

    //Fetch shadow coordinates for cascade.
    float4 coords = TransformWorldToShadowCoord(wpos);

    // Screenspace shadowmap is only used for directional lights which use orthogonal projection.
    ShadowSamplingData shadowSamplingData = GetMainLightShadowSamplingData();
    half4 shadowParams = GetMainLightShadowParams();
    return SampleShadowmap(TEXTURE2D_ARGS(_MainLightShadowmapTexture, sampler_MainLightShadowmapTexture), coords, shadowSamplingData, shadowParams, false);
}

第四步,渲染场景

有了 屏幕空间阴影贴图,我们就可以根据 屏幕坐标 采样贴图,得到光的 影衰减,最终用于光照计算。

相关代码如下:

顶点着色器:

output.ShadowCoords = GetShadowCoord(vertexInput); 
float4 GetShadowCoord(VertexPositionInputs vertexInput)
{
#if SHADOWS_SCREEN
    return ComputeScreenPos(vertexInput.positionCS);
#else
    return TransformWorldToShadowCoord(vertexInput.positionWS);              
#endif
}
float4 ComputeScreenPos(float4 positionCS) 
{               
    float4 o = positionCS * 0.5f;
    o.xy = float2(o.x, o.y * _ProjectionParams.x) + o.w;
    o.zw = positionCS.zw;
    return o;
}

像素着色器:

half MainLightRealtimeShadow(float4 shadowCoord)
{
#if !defined(_MAIN_LIGHT_SHADOWS) || defined(_RECEIVE_SHADOWS_OFF)
    return 1.0h;
#endif

#if SHADOWS_SCREEN
    return SampleScreenSpaceShadowmap(shadowCoord);
#else
    ShadowSamplingData shadowSamplingData = GetMainLightShadowSamplingData();
    half4 shadowParams = GetMainLightShadowParams();
    return SampleShadowmap(TEXTURE2D_ARGS(_MainLightShadowmapTexture, sampler_MainLightShadowmapTexture), shadowCoord, shadowSamplingData, shadowParams, false);
#endif
}
half SampleScreenSpaceShadowmap(float4 shadowCoord)
{
    shadowCoord.xy /= shadowCoord.w;

    // The stereo transform has to happen after the manual perspective divide 
    shadowCoord.xy = UnityStereoTransformScreenSpaceTex(shadowCoord.xy);

#if defined(UNITY_STEREO_INSTANCING_ENABLED) || defined(UNITY_STEREO_MULTIVIEW_ENABLED)
    half attenuation = SAMPLE_TEXTURE2D_ARRAY(_ScreenSpaceShadowmapTexture, sampler_ScreenSpaceShadowmapTexture, shadowCoord.xy, unity_StereoEyeIndex).x;
#else
    half attenuation = SAMPLE_TEXTURE2D(_ScreenSpaceShadowmapTexture, sampler_ScreenSpaceShadowmapTexture, shadowCoord.xy).x;
#endif

    return attenuation;
}

题外话

很多游戏的实时阴影都不是Unity内置的,如今在 URP 开放源码的情况下,我正在考虑把我们游戏的实时阴影切回Unity的内置版本。

去年为了抄 塞尔达,我们在移动设备搞过 全场景的实时阴影

  • 2级级联
  • 70米实时影
  • PCF2X2采样

当时基于一个叫 Sunshine 的Unity插件做了大量优化,在我的 小米MIX2 上还是非常流畅的,怀念一下:

screenshot5.jpg

个人主页

本文的个人主页链接:https://baddogzz.github.io/2019/12/19/URP-Shadow/

好了,拜拜。

相关文章

  • 关于SHADOWS_SCREEN

    关于SHADOWS_SCREEN 在读Unity内置shader源码的过程中,我们经常看到这样的代码: 那么,上面...

  • 关于关于关于

    他们爱他们自己,不爱你 他们爱你是他们的母亲妻子女儿姐妹 他们不爱你 直到你死的时候,爱才产生,与遗忘同时 那也不...

  • 光明人生

    关于出生 关于成长 关于求学 关于青春期 关于恋爱 关于择业 关于婚姻 关于养生 关于家庭 关于人际 关于教子 关...

  • 「梦雅的简动力」打卡计时65天

    * 关于人生 * 关于梦想 * 关于方向 * 关于创业 * 关于投资 * 关于成败 * 关于个性 * 关于高度 *...

  • 关于

    关于两个人? 关于100步? 关于回头? 关于深情? 关于家庭? 关于孩子? 关于成长? 关于伤痛? 关于怀抱? ...

  • 2017新手妈妈年终总结

    关于购物 关于体重 关于减肥 关于纪念日 关于生活态度 关于上班 关于职场晋升加薪 关于睡眠 关于抱孩子 关于发型...

  • 2018-11-28

    关于流浪、关于随心、关于自由、关于世俗、关于规则、关于坦诚、关于真我、关于好奇心、关于对这整个世界的态度、关于整个...

  • 一首歌的时间

    认真的 想理出点思绪 关于今天关于明天 关于工作关于梦想 关于冬天关于夜晚 关于阳光关于浪花 关于木马关于窗花 关...

  • 最近的各种关于

    关于运动,关于中文阅读,关于英文听力,关于口算。 关于专注,关于目标,关于举家迁移。 关于对正确的过于执着,关于对...

  • 『关于』

    关于雨落,关于天晴; 关于入夜,关于天明; 关于齐眉,关于耳鬓; 关于缘定,关于今生。 (早安~诸君!)

网友评论

    本文标题:关于SHADOWS_SCREEN

    本文链接:https://www.haomeiwen.com/subject/owxvnctx.html