Unity 屏幕特效 之 分屏之 斜角(对角线) 分屏渲染不同相机画面功能的简单实现


Unity 屏幕特效 之 分屏之 斜角(对角线) 分屏渲染不同相机画面功能的简单实现

目录

Unity 屏幕特效 之 分屏之 斜角(对角线) 分屏渲染不同相机画面功能的简单实现

一、简介

二、关键技术

三、注意事项

四、效果预览

五、实现步骤

六、关键代码


一、简介

所谓屏幕后处理,简单来说就是渲染流水线的最后阶段,对由整个场景生成的一张图片进行处理,比如HDR,运动模糊等等效果,通过屏幕空间的后处理,可以整体改变整个游戏的风格或者效果。所以,要制作屏幕后处理,我们需要两样东西,一个是用于渲染后处理效果的shader,而另一个是我们需要调用这个渲染的脚本。

实现原理:

  • 斜角分屏,根据点再对角线的上或者下,进行分屏渲染

二、关键技术

1、在 Shader 中,根据数学公式得到 UV 对角线 上下的规律,进行斜角分屏渲染;

2、OnRenderImage函数每帧渲染完全部内容后执行,我们在每一帧设置Material的各项参数,通过Material.SetXXX("name",value)可以向shader中传递各种参数;

3、ceil 函数(向上求整), 返回大于或等于输入值的最小整数。

4、关键代码

1
2
3
4
5
6
7
8
9
// 斜角渲染不同相机的画面
// 斜角线有两条:y-x=0,y+x-1=0
// uv的 x y 值 带入上面斜角线,值大于0,在线上面,值小于 0 ,在线下面
if (_SpiltScreenMode) {
    col = tex2D(_MainTex, i.uv)*ceil(i.uv.x+i.uv.y -1)+tex2D(_SecondCameraTexture,i.uv)*ceil(1-i.uv.x-i.uv.y);
}
else{
    col = tex2D(_MainTex, i.uv)*ceil(i.uv.y-i.uv.x)+tex2D(_SecondCameraTexture,i.uv)*ceil(i.uv.x-i.uv.y);
}

三、注意事项

1、各种后处理效果可以叠加,这里的dest并不一定就是屏幕。不过后处理是很耗费性能的,一方面是pixel shader全屏幕overdraw,另一方面,一个rendertexture的内存占用很大,尤其是大分辨率手机上,多个后处理效果可能造成内存耗尽,程序崩溃;(不过网上有方式可以优化这个功能,到时候找找资料看看,在给大家试试哈,大家也可以自己先看看试试)

2、注意判断屏幕特效是否支持可以用;

3、注意参数值根据实际效果,合理调节;

四、效果预览

五、实现步骤

简单原理图:

具体实现步骤:

1、打开Unity,在场景中添加几个 Cube 和 camera

2、其中,可以调整两个camer 位置和方向,进行不一样的渲染,MainCamera 和 Camera 的渲染如下

3、在工程中,新建两个脚本和Shader,Shader 实现分屏,脚本把屏幕数据传给Shader

4、把脚本挂载到 MainCamera 上,并赋值

5、运行场景,效果如上

六、关键代码

1、ShaderSpiltScreen.shader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
Shader "Unlit/ShaderSpiltScreen"
{
    Properties
    {
        _MainTex ("主camera的渲染图", 2D) = "white" {}
        _SecondCameraTexture("第二个camera的渲染图",2D)="white"{}
        [MaterialToggle]_SpiltScreenMode("屏幕分屏模式切换",float) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        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;
            float4 _MainTex_ST;
            sampler2D _SecondCameraTexture;
            float4 _SecondCameraTexture_ST;
            bool _SpiltScreenMode;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
       
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col;
               
                // 斜角渲染不同相机的画面
                // 斜角线有两条:y-x=0,y+x-1=0
                // uv的 x y 值 带入上面斜角线,值大于0,在线上面,值小于 0 ,在线下面
                if (_SpiltScreenMode) {
                    col = tex2D(_MainTex, i.uv)*ceil(i.uv.x+i.uv.y -1)+tex2D(_SecondCameraTexture,i.uv)*ceil(1-i.uv.x-i.uv.y);
                }
                else{
                    col = tex2D(_MainTex, i.uv)*ceil(i.uv.y-i.uv.x)+tex2D(_SecondCameraTexture,i.uv)*ceil(i.uv.x-i.uv.y);
                }

           

                return col;
            }
            ENDCG
        }
    }
}

2、SpiltScreen.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SpiltScreen : ScreenPostEffectBase
{
    public bool _SpiltScreenMode = true;
    public Camera _SecondCamera;

    void OnRenderImage(RenderTexture sourceTexture, RenderTexture destTexture)
    {
        if (_Material != null)
        {
            if (_SecondCamera.targetTexture == null) {
                ////根据主相机的宽高创建一个同样宽高的纹理,记录辅助相机的图像
                _SecondCamera.targetTexture = new RenderTexture(_SecondCamera.pixelWidth,_SecondCamera.pixelHeight,0);
            }
            _Material.SetTexture("_SecondCameraTexture", _SecondCamera.targetTexture);
            _Material.SetInt("_SpiltScreenMode", _SpiltScreenMode?1:0);

            //Debug.Log(_SpiltScreenMode ? 1 : 0);

            Graphics.Blit(sourceTexture, destTexture, _Material);
        }
        else
        {
            Graphics.Blit(sourceTexture, destTexture);
        }
    }
}

3、ScreenPostEffectBase.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
using UnityEngine;

//非运行时也触发效果
[ExecuteInEditMode]
//屏幕后处理特效一般都需要绑定在摄像机上
[RequireComponent(typeof(Camera))]
//提供一个后处理的基类,主要功能在于直接通过Inspector面板拖入shader,生成shader对应的材质
public class ScreenPostEffectBase : MonoBehaviour
{

    //Inspector面板上直接拖入
    public Shader shader = null;
    private Material _material = null;
    public Material _Material
    {
        get
        {
            if (_material == null)
                _material = GenerateMaterial(shader);
            return _material;
        }
    }

    //根据shader创建用于屏幕特效的材质
    protected Material GenerateMaterial(Shader shader)
    {
        // 系统是否支持
        if (!SystemInfo.supportsImageEffects)
        {
            return null;
        }

        if (shader == null)
            return null;
        //需要判断shader是否支持
        if (shader.isSupported == false)
            return null;
        Material material = new Material(shader);
        material.hideFlags = HideFlags.DontSave;
        if (material)
            return material;
        return null;
    }

    void OnDisable()
    {
        if (_material != null)
        {
            DestroyImmediate(_material);
        }
    }

}