rainyeve
  • 首页
  • 编程
  • 涂鸦
  • 其他
  • 关于
2020年6月25日
编程

【自定义Post Processing Volume】画面上下黑边遮幅(LetterBoxing)

【自定义Post Processing Volume】画面上下黑边遮幅(LetterBoxing)
2020年6月25日
编程

上下黑边遮幅的镜头效果其标准称谓我也不确定,搜索了一轮姑且称为LetterBoxing(一种画面缩放策略),在游戏中常见用于播放过场动画时的镜头处理,带出一种信息:“当前是在播放动画/电影片段,玩家暂时无法操作”。

在Unity Post Processing Stack 3(Post Processing Volume)中,有自带的vignette效果,但它是把屏幕四周都上色,于是手动实现一个上下黑边的LetterBoxing效果并整合到Volume中,好处是可以像其他效果一样,通过Volume对象的Weight来调节参数:

播放小兵出场动画时镜头启用上下黑边

自定义Volume Component

用Volume的参数来调节效果

自定义Renderer Feature

RendererFeature只传入一个材质当做参数,而其余的参数放在Volume中调节,甚至开关也是由Volume控制。

自定义LetterBoxingRendererFeature

Scriptable Renderer Feature

public class LetterBoxingRenderFeature : ScriptableRendererFeature
{
    private LetterBoxingPass _letterBoxingPass;
    
    //把材质当做参数配置,避免打包时漏掉Shader
    //若用shader生成材质,则必须在ProjectSetting窗口中配置好常驻Shader,否则打包之后材质会创建不出来
    public Material letterBoxingMaterial;
    
    public override void Create()
    {
        _letterBoxingPass = new LetterBoxingPass(letterBoxingMaterial);
    }

    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        _letterBoxingPass.Setup(renderer.cameraColorTarget);
        renderer.EnqueuePass(_letterBoxingPass);
    }
}

Scriptable Render Pass

public class LetterBoxingPass : ScriptableRenderPass
{
    private Material _letterBoxingMat;
    private LetterBoxing _letterBoxing;
    private RenderTargetIdentifier _currentTarget;
    
    static readonly string RenderTag = "Letter Boxing Effect";
    static readonly int MainTexId = Shader.PropertyToID("_MainTex");
    static readonly int TempTargetId = Shader.PropertyToID("LetterBoxingTemp");
    static readonly int SizeId = Shader.PropertyToID("_Size");

    
    public LetterBoxingPass(Material mat)
    {
        //与PostProcessingVolume做了绑定,必须赶在PostProcessPass执行之前
        renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
        
        //把材质当做参数配置,避免打包时漏掉Shader
        //若用shader生成材质,则必须在ProjectSetting窗口中配置好常驻Shader,否则打包之后材质会创建不出来 
        _letterBoxingMat = mat;
    }
    
    public void Setup(in RenderTargetIdentifier currentTarget)
    {
        _currentTarget = currentTarget;
    }

    public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    {
        if (_letterBoxingMat == null)
        {
            Debug.LogError("Material not created.");
            return;
        }

        if (!renderingData.cameraData.postProcessEnabled) return;

        var stack = VolumeManager.instance.stack;
        if(_letterBoxing==null) _letterBoxing = stack.GetComponent<LetterBoxing>();
        if (_letterBoxing == null || !_letterBoxing.IsActive()) { return; }
        
        if (_letterBoxing.verticalSize.value <= float.Epsilon) {return;}

        var cmd = CommandBufferPool.Get(RenderTag);
        
        ref var cameraData = ref renderingData.cameraData;
        var source = _currentTarget;
        int destination = TempTargetId;
        
        var w = cameraData.camera.scaledPixelWidth;
        var h = cameraData.camera.scaledPixelHeight;
         
        _letterBoxingMat.SetFloat(SizeId, _letterBoxing.verticalSize.value);

        int shaderPass = 0;
        cmd.SetGlobalTexture(MainTexId, source);
        cmd.GetTemporaryRT(destination, w, h, 0, FilterMode.Bilinear, RenderTextureFormat.Default);
        cmd.Blit(source, destination);
        cmd.Blit(destination, source, _letterBoxingMat, shaderPass);
        
        context.ExecuteCommandBuffer(cmd);
        CommandBufferPool.Release(cmd);
    }
    
}

Volume Component

public class LetterBoxing : VolumeComponent, IPostProcessComponent
{
    public BoolParameter on = new BoolParameter(false);
    [Tooltip("上下黑边对于屏幕高度的占比")]//这里的默认值只能取min,否则会导致修改volume权重的时候插值不正确,因为是取“默认值”作为插值左边参数的
    public ClampedFloatParameter verticalSize = new ClampedFloatParameter(0f, 0f, .5f);
    
    public bool IsActive() => on.value;

    public bool IsTileCompatible() => false;
}

Shader

Shader "MyAct/LetterBoxing"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }

    SubShader
    {
        Tags { "RenderType"="Opaque" }
        
        Pass
        {
            CGPROGRAM
            #pragma vertex Vert
            #pragma fragment Frag
            #include "UnityCG.cginc"

            sampler2D _MainTex;
            float _Size;

            ...

            fixed4 Frag (v2f i) : SV_Target
            {
                float2 uv = i.uv;
                if(uv.y < _Size || uv.y > 1-_Size) return fixed4(0, 0, 0, 1);  
                fixed4 col = tex2D(_MainTex, uv);
                return col;
            }
            ENDCG
        }
    }
}
Post Views: 3,634

post processing shader SRP unity

上一篇【动作游戏设计】关于“慢动作”的小结下一篇 【Unity Shader】模拟水面包含折射与反射与波浪动画

发表评论 取消回复

邮箱地址不会被公开。 必填项已用*标注

分类目录

  • 其他 (2)
  • 旅行 (1)
  • 涂鸦 (6)
  • 编程 (28)
Email: wuch441692@163.com