ScreenSpaceShadowMask Blur推荐

来源:互联网 发布:js地球仪特效 编辑:程序博客网 时间:2024/06/07 22:01

在移动游戏中渲染阴影时,我们通常使用低分辨率的ShadowMap来降低内存,其不足之处就是锯齿明显。对此,我们推荐大家使用Unity ScreenSpaceShadowMask Blur技术来增加阴影质量,消除锯齿感。

目的

Unity自带的阴影功能ShadowMap占用内存过大,1024 * 1024占用8MB。而我们项目内存吃紧,所以只能选用512 * 512,如下图,带有明显的锯齿感,效果差了很多。

请输入图片描述
如果您也有类似的需求,这时就可以采用 Unity ScreenSpaceShadowMask Blur。原理就是在屏幕空间里对已经投射完阴影的物体进行高斯模糊,以此来增加阴影质量,减少锯齿感。(PS:Unity提供的软阴影应该是PCF,但是效果并不明显)。

经过 ScreenSpaceShadowMask Blur后,效果如下:
请输入图片描述


具体实现步骤

一、 渲染流程

ShadowCamera的ShadowPass
->ShadowCamera 正常Pass
->BlurPass
->Main Camera Pass
->全屏片

二、详细分析

1.ShadowPass要产生ShadowMap,只需让ShadowCamera能看到投射阴影和接收阴影的物体,同时把那些阴影开关打开,Unity就会自动加入该Pass。(PS:如果物体是自己写的Vertext Fragement Shader,则需要自己加入Shadow Caster Pass,调用Unity内置即可。文件在UnityStandardShadow.cginc里)。

Shadow Camera在渲染前会统一替换成一个简单的Shader,只需包含投射跟接收阴影2个Pass即可。这样既可以减少Shader复杂度,又可以合并Draw Call。

2.正常Pass就是正常渲染阴影,比较ShadowMap与自己的深度值。我们将ShadowCamera清屏色设置成黑色,最终效果为有阴影的地方有颜色,其余地方为黑色。(颜色值会当成最后的Alpha值来处理)。

请输入图片描述
3.Blur Pass可以用CommandBuffer,也可以用OnPostRender。输入参数为ShaderCamera的Rendertarget,通过Blit来进行高斯模糊。(建议尽量少用CommandBuffer。我采用CommandBuffer,在小米1s上 RT->blurRT->RT会出现抖动现象,OnPostRender并没有。但是这两者原理应该是一样的,通过GPA查看,代码都是一样的。暂时不清楚其原因,欢迎有兴趣的朋友和我交流)。

4.Main Camera Pass 正常渲染场景。原则上渲染的物体是不会接收阴影的。

5.全屏可以通过CommandBuffer,也可以通过OnPostRender来处理。输入参数为上次经过模糊的RT值。Alpha、阴影颜色、透明度这些参数值自己写两个进行调节即可。最终渲染流程:
请输入图片描述


消耗统计

1. 内存消耗

(1) ShadowMap:512*512 2MB
(2) ShadowCamera RT 1/4屏 带16位深度
(3) Blur RT 1/4屏 不带深度
请输入图片描述
这是小米4c上的数据。

2. 渲染消耗统计

(1)多一个相机投射阴影体,接收阴影体多渲染一次;
(2)2次Blur操作;
(3)1次全屏blit操作;
主要对于带宽影响较大的物体影响较重。


总结

1.CommandBuffer 更自由,比如很多点都可以插入自定义行为,比如对ShadowMap进行操作等。虽然稳定性不佳,性能还是可以的。

2.如果一个Camera被设成RT,它会在其他相机渲染之前渲染,不受Depth影响。


相应资源

1.ShadowPass Shader

Shader "Shadow/ShadowMask" {    Properties     {    }    SubShader     {        Tags { "RenderType"="Geometry" }        Pass         {            Tags {"LightMode"="ForwardBase"}             CGPROGRAM            #pragma multi_compile_fwdbase            #pragma vertex   vertForwardBase            #pragma fragment fragForwardBase            struct appdata_input             {                float4 vertex : POSITION;            };            struct v2f            {                float4 pos          : SV_POSITION;                LIGHTING_COORDS(1,2)            };            v2f vertForwardBase (appdata_input v)            {                v2f o;                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);                TRANSFER_VERTEX_TO_FRAGMENT(o);                return o;            }            fixed4 fragForwardBase (v2f i) : SV_Target            {                fixed4 col = fixed4(1,0,0,1);                fixed attenuation = LIGHT_ATTENUATION(i);                col = col * (1-attenuation);                return col;            }            ENDCG        }        Pass         {            Name "ShadowCaster"            Tags { "LightMode" = "ShadowCaster" }                        Fog {Mode Off}            ZWrite On ZTest LEqual Cull Off            Offset 1, 1                CGPROGRAM            #pragma vertex vert            #pragma fragment frag            #pragma multi_compile_shadowcaster            #pragma fragmentoption ARB_precision_hint_fastest            #include "UnityCG.cginc"                struct v2f {                 V2F_SHADOW_CASTER;            };                v2f vert( appdata_base v )            {                v2f o;                TRANSFER_SHADOW_CASTER(o)                return o;            }                float4 frag( v2f i ) : COLOR            {                SHADOW_CASTER_FRAGMENT(i)            }            ENDCG        }     }}

2. 高斯模糊Shader

3. 全屏blit shader

Shader "BlitQuad1"{    Properties    {        _MainTex("MainTexture",2D) = "white"{}        _Color("ShadowColor",Color) = (0,0,0,1)        _Intension("Intension",Float) = 0.6    }    SubShader    {        Tags { "RenderType"="Transparent" }        Blend SrcAlpha OneMinusSrcAlpha         zwrite off        Pass        {            CGPROGRAM            #pragma vertex vert            #pragma fragment frag            #include "UnityCG.cginc"            struct appdata            {                float4 vertex : POSITION;                float2 uv : TEXCOORD0;            };            struct v2f            {                float2 uv : TEXCOORD0;                float4 vertex : SV_POSITION;            };            sampler2D _MainTex;            half4 _MainTex_TexelSize;            float _Intension;            fixed _Color;                        v2f vert (appdata v)            {                v2f o;                o.vertex = v.vertex;                o.uv = v.uv;                return o;            }                        fixed4 frag (v2f i) : SV_Target            {                half4 color = half4(1,1,1,1) * _Color;                half4 alpha = tex2D(_MainTex,i.uv);                color.a = alpha.r * _Intension;                return color;            }            ENDCG        }    }}

C#脚本

1. ShadowCamera挂载实现 注释代码为CommandBuffer

using UnityEngine;using UnityEngine.Rendering;using System.Collections;public class ShadowMask : MonoBehaviour{    //public int maxBlurCount = 2;    public int sampleOffset = 2;    private const float UIWIDTH = 1136f;    private const int MAX_VALUE = 2048;    private int screenWidth;    private int screenHeight;    public RenderTexture  renderTexture;    public RenderTexture blurRT;    //private RenderTargetIdentifier rtl;    //private RenderTargetIdentifier blurRtl;    //private CommandBuffer blurCommand;    //private CommandBuffer screenQuadCommand;    private Material material1;    private Material material2;    public Camera shadowCamera;    //public Camera mainCamera;    private int SCALE = 2;    //private int pass = 0;    //private bool hasAdd = false;    void Awake()    {        RenderTextureFormateSupportTool.GetInstance().Init();        screenWidth = Mathf.Min(Screen.width, MAX_VALUE);        screenHeight = Mathf.Min(Screen.height, MAX_VALUE);        renderTexture = RenderTextureFormateSupportTool.GetInstance().GetRenderTexture(screenWidth / SCALE, screenHeight / SCALE, 16, RenderTextureFormateSupportTool.H3DRenderTextureFormate.R8);        renderTexture.name = "RenderTexture";        blurRT = RenderTextureFormateSupportTool.GetInstance().GetRenderTexture(screenWidth / SCALE, screenHeight / SCALE,0, RenderTextureFormateSupportTool.H3DRenderTextureFormate.R8);        blurRT.name = "BlurRT";        float aspectValue = shadowCamera.aspect;        shadowCamera.targetTexture = renderTexture;        shadowCamera.aspect = aspectValue;        Shader shadowShader = Shader.Find("Shadow/ShadowMask");        shadowCamera.SetReplacementShader(shadowShader, "");        //blurCommand = new CommandBuffer();        //blurCommand.name = "BlurShadowMask";        //screenQuadCommand = new CommandBuffer();        //screenQuadCommand.name = "ScreenQuad";        //rtl = new RenderTargetIdentifier(renderTexture);        //blurRtl = new RenderTargetIdentifier(blurRT);         Shader blurShader = Shader.Find("Shadow/ShadowMaskBlur");        material1 = new Material(blurShader);        material2 = new Material(blurShader);    }    void OnPostRender()    {        material1.SetTexture("_MainTexture", renderTexture);        material1.SetInt("_Horizontal", 0);        material1.SetInt("_SampleOffset", sampleOffset);        Graphics.Blit(renderTexture, blurRT, material1);        material2.SetTexture("_MainTexture", blurRT);        material2.SetInt("_Horizontal", 1);        material2.SetInt("_SampleOffset", sampleOffset);        Graphics.Blit(blurRT, renderTexture, material2);    }    //void Update()    //{    //    if (hasAdd)    //    {    //        blurCommand.Clear();    //    }    //    else    //    {    //        shadowCamera.AddCommandBuffer(CameraEvent.AfterEverything, blurCommand);    //        hasAdd = true;    //    }    //    for (int i = 0; i < maxBlurCount; i++)    //    {    //        if (i % 2 == 0)    //        {    //            material1.SetTexture("_MainTexture", renderTexture);    //            material1.SetInt("_Horizontal", pass % 2);    //            material1.SetInt("_SampleOffset", sampleOffset);    //            blurCommand.Blit(rtl, blurRtl, material1);    //        }    //        else    //        {    //            material2.SetTexture("_MainTexture", blurRT);    //            material2.SetInt("_Horizontal", pass % 2);    //            material2.SetInt("_SampleOffset", sampleOffset);    //            blurCommand.Blit(blurRtl, blurRt2, material2);    //        }    //        pass++;    //    }    //}}

2.mainCamera挂载实现 全屏blit操作

using UnityEngine;using System.Collections;public class MainCameraTest : MonoBehaviour {    public ShadowMask shadowMask;    public float shadowIntensity = 1f;    public Color shadowColor = Color.black;    private Material screenQuadMaterial;    private Mesh screenQuadMesh;    private Matrix4x4 matrix;    public void Start()    {        Shader screenQuadShader = Shader.Find("BlitQuad");        screenQuadMaterial = new Material(screenQuadShader);        screenQuadMaterial.SetTexture("_MainTex", shadowMask.renderTexture);        screenQuadMesh = new Mesh();        Vector3[] verts = new Vector3[4];        verts[0] = new Vector3(-1, -1, 0);        verts[1] = new Vector3(-1, 1, 0);        verts[2] = new Vector3(1, 1, 0);        verts[3] = new Vector3(1, -1, 0);        screenQuadMesh.vertices = verts;        Vector2[] uvs = new Vector2[4];        uvs[0] = new Vector2(0, 0);        uvs[1] = new Vector2(0, 1);        uvs[2] = new Vector2(1, 1);        uvs[3] = new Vector2(1, 0);        screenQuadMesh.uv = uvs;        int[] indices = new int[6];        indices[0] = 0;        indices[1] = 1;        indices[2] = 2;        indices[3] = 0;        indices[4] = 2;        indices[5] = 3;        screenQuadMesh.triangles = indices;        screenQuadMesh.Optimize();        matrix = Matrix4x4.identity;    }    void OnPostRender()    {        screenQuadMaterial.SetFloat("_Intension", shadowIntensity);        screenQuadMaterial.SetColor("_Color", shadowColor);        screenQuadMaterial.SetPass(0);        Graphics.DrawMeshNow(screenQuadMesh, matrix);    }        }


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 ipadair关不了机怎么办 联想电脑开不了机怎么办 ipad来不了机怎么办 深圳车牌租用怎么办的 电脑连wiwf老掉线怎么办 电脑要设置密码怎么办 苹果笔记本电脑忘记密码怎么办 换了外屏有缝隙怎么办 苹果7屏幕松动怎么办 平板电脑屏幕一直闪怎么办 平板电脑屏幕模糊了怎么办 平板电脑模糊怎么办啊 苹果没有声音了怎么办 微信视频回声怎么办 苹果手机总卡机不动怎么办 苹果手机不掉电怎么办 iphonex屏幕漏液怎么办 苹果6sp充不进电怎么办 iphone6充电越少怎么办 苹果6不能充电怎么办 脸部苹果肌很大怎么办 液晶屏上有划痕怎么办 苹果手机屏幕翘起来怎么办? iphone5s翘屏了怎么办 lpad触屏失灵怎么办 平板一直重启怎么办 苹果平板老死机怎么办 华为屏幕反应慢怎么办 平板屏幕没反应怎么办 iPadmini3触屏了怎么办 联想电脑触摸屏没反应怎么办 电脑触摸屏没反应怎么办 换屏后屏幕乱跳怎么办 联想平板进不去系统怎么办 平板触摸屏坏了怎么办 苹果6触摸不灵敏怎么办 5s home失灵怎么办? 平板触控失灵怎么办 ipad触摸屏坏了怎么办 ipad屏幕颠倒了怎么办 ipad电池坏了怎么办