Unity3D 阴影和深度纹理总结

原创 2017年08月28日 14:43:16

首先介绍一下,阴影的生成,阴影一般是在屏幕空间生成的,利用Unity3D引擎实现阴影的绘制,首先要明白其实现原理,Unity5的Demo已经为我们提供了代码实现,先介绍一下阴影的视线原理:

屏幕空间的阴影实现主要有这么几个步骤:

1、首先得到从当前摄像机处观察到的深度纹理。在延迟渲染里这张深度图本来就有,如果是前向渲染的话就需要把场景整个渲染一遍,把深度渲染

到深度图中。

2、然后再从光源出发得到从该光源处观察到的深度纹理,也被称为这个光源的ShadowMap

3、然后在屏幕空间做一次阴影收集计算(Shadows Collector),这次计算会得到一张屏幕空间阴影纹理,也就是说这张图里面需要有阴影的部分

已经显示在图上了。这个过程概括来说就是把每一个像素根据它在摄像机深度纹理中的深度值得到世界空间坐标,再把它的坐标从世界空间转换到

光源空间中,和光源的ShadowMap里面的深度值对比,如果大于ShadowMap中的深度距离,那么就说明光源无法照到,在阴影内。

4、最后,在正常渲染物体为它计算阴影的时候,只需要按照当前处理的fragment在屏幕空间中的位置对步骤3得到的屏幕空间阴影图采样就可以了。


它的具体实现Shader名字是:

Hidden/Internal-ScreenSpaceShadows(在DefaultResourcesExtra/Internal-ScreenSpaceShadows.shader)。这个Pass在不同的Unity版本

里是不一样的,比如在Unity 5.3里面就是Internal-PrePassCollectShadows(在DefaultResources/Internal-PrePassCollectShadows.shader),看的时候还是要全局搜索确定下。

主要思路就是从摄像机的深度纹理里采样得到该fragment的深度值,然后利用矩阵变换计算得到该点

对应的世界空间的世界坐标(利用CameraToWorld矩阵),然后再变换到光源空间下的坐标(利用WorldToShadow矩阵),最后拿

这个坐标对光源的ShadowMap采样计算阴影。


通常的做法其实都可以满足需求,最重要的是遇到一些特殊情况比如材质是透明或者是半透明的,这个就需要特殊处理了。
在Unity自带的Shader:Standard Shader中计算阴影的Pass:
//  Shadow rendering pass
Pass {
    Name "ShadowCaster"
    Tags { "LightMode" = "ShadowCaster" }

    ZWrite On ZTest LEqual

    CGPROGRAM
    #pragma target 3.0
    // TEMPORARY: GLES2.0 temporarily disabled to prevent errors spam on devices without textureCubeLodEXT
    #pragma exclude_renderers gles

    #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
    #pragma multi_compile_shadowcaster

    #pragma vertex vertShadowCaster
    #pragma fragment fragShadowCaster

    #include "UnityStandardShadow.cginc"

    ENDCG
}

可以发现也是和我们一样定义了LightMode为ShadowCaster的Pass。_ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON这些keyword就是为半透明阴影效果定义的。再看fragShadowCaster的实现(在UnityStandardShadow.cginc中定义):

half4 fragShadowCaster (
    #ifdef UNITY_STANDARD_USE_SHADOW_OUTPUT_STRUCT
    VertexOutputShadowCaster i
    #endif
    #ifdef UNITY_STANDARD_USE_DITHER_MASK
    , UNITY_VPOS_TYPE vpos : VPOS
    #endif
    ) : SV_Target
{
    #if defined(UNITY_STANDARD_USE_SHADOW_UVS)
        half alpha = tex2D(_MainTex, i.tex).a * _Color.a;
        #if defined(_ALPHATEST_ON)
            clip (alpha - _Cutoff);
        #endif
        #if defined(_ALPHABLEND_ON) || defined(_ALPHAPREMULTIPLY_ON)
            #if defined(UNITY_STANDARD_USE_DITHER_MASK)
                // Use dither mask for alpha blended shadows, based on pixel position xy
                // and alpha level. Our dither texture is 4x4x16.
                half alphaRef = tex3D(_DitherMaskLOD, float3(vpos.xy*0.25,alpha*0.9375)).a;
                clip (alphaRef - 0.01);
            #else
                clip (alpha - _Cutoff);
            #endif
        #endif
    #endif // #if defined(UNITY_STANDARD_USE_SHADOW_UVS)

    SHADOW_CASTER_FRAGMENT(i)
}   

代码也很简单,我们需要关注的就是tex3D(_DitherMaskLOD, float3(vpos.xy_0.25,alpha_0.9375)).a这一句。可以看出来,它是对一张

3D的dither纹理采样,这张纹理的z方向对应了透明度值。再使用clip对dither后的结果进行cutout,造成一种半透明阴影的假象。

说得简单点,这种Dither技术其实就是利用人眼的特性,我们看到的其实是有很多细小孔洞的阴影,由于这些空洞的大小和密度变化,

在人眼看来就像是半透明了一样,但实际上真正的阴影颜色是没有变的。总结其实就是,dither texture + cutout。

如果我们要自己在shader中实现这种效果,也可以仿照Standard Shader的做法,对_DitherMaskLOD这张纹理进行采样。例如,

在Shader中添加自定义的ShadowCaster Pass:

Pass{
    Name "ShadowCaster"
    Tags{ "LightMode" = "ShadowCaster" }

    ZWrite On ZTest LEqual

    CGPROGRAM
    #pragma target 3.0

    #pragma vertex vert
    #pragma fragment frag

    #pragma multi_compile_shadowcaster

    #define UNITY_STANDARD_USE_SHADOW_OUTPUT_STRUCT
    #define UNITY_STANDARD_USE_DITHER_MASK
    #define UNITY_STANDARD_USE_SHADOW_UVS

    #include "UnityStandardShadow.cginc"

    fixed _AlphaScale;

    struct VertexOutput
    {
        V2F_SHADOW_CASTER_NOPOS
        float2 tex : TEXCOORD1;
    };

    void vert(VertexInput v, out VertexOutput o, out float4 opos : SV_POSITION)
    {
        TRANSFER_SHADOW_CASTER_NOPOS(o,opos)
        o.tex = v.uv0;
    }

    half4 frag(VertexOutput i, UNITY_VPOS_TYPE vpos : VPOS) : SV_Target
    {
        half alpha = tex2D(_MainTex, i.tex).a * _AlphaScale;

        half alphaRef = tex3D(_DitherMaskLOD, float3(vpos.xy*0.25,alpha*0.9375)).a;
        clip(alphaRef - 0.01);

        SHADOW_CASTER_FRAGMENT(i)
    }

    ENDCG
}

同时禁用其他Fallback,就可以得到“半透明”阴影的效果了。

版权声明:本文为博主原创文章,未经博主允许不得转载。

unity3D阴影手册

  • 2015年08月13日 22:50
  • 52KB
  • 下载

关于unity3d纹理贴图的学习总结

今天unity3d纹理的贴图操作不清楚,折腾了大半天了弄好,心情很舒畅,于是写写总结。          Unity支持PSD、TIFF、JPEG、TAG、PNG、GIF、BMP、IFF、PICT格...

Unity3D~纹理格式

  • 2017年04月07日 11:51
  • 14KB
  • 下载

Unity3D【新手问题】阴影效果不显示的原因

Unity 不显示阴影的原因: 模型尺寸太大了,镜头比较远 把模型缩小,镜头一定要拉到最近才能看到 这是我遇到的问题和解决方法,另外一定要设置 Directional light 的 sha...

Unity3D研究院之使用RenderTexture制作动态阴影(四十八)

本文固定链接: http://www.xuanyusong.com/archives/2132 雨松MOMO 2013年03月05日 于 雨松MOMO程序研究院 发表引子:MOMO这段时间有时会玩T...

Unity3D游戏制作 移动平台上的角色阴影制作

本系列文章由 Amazonzx 编写,欢迎转载,转载请注明出处。 http://blog.csdn.net/amazonzx/article/details/7973740   本文将重点介绍两...

Sprite添加阴影摇摆动画(Unity3D开发之九)

猴子原创,欢迎转载。转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢!原文地址: http://www.cocos2dev.com/?p=575今天看到一个很简单的摇摆动画,...

Unity3D游戏制作(三)——移动平台上的角色阴影制作

本系列文章由 Amazonzx 编写,欢迎转载,转载请注明出处。 http://blog.csdn.net/amazonzx/article/details/7973740   本文将重点介绍两...
  • Amazonzx
  • Amazonzx
  • 2012年09月13日 10:21
  • 29542

Unity3D手游开发日记(1) - 移动平台实时阴影方案

自从去了某大公司,工作环境比较封闭,就没写过博客了,离开以后,开发移动游戏,相对自由,又可以重操旧业,分享一下自己的心得. 阴影这个东西,说来就话长了,很多年前人们就开始研究出各种阴影技术,但都存在各...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Unity3D 阴影和深度纹理总结
举报原因:
原因补充:

(最多只允许输入30个字)