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); } } } |