做了一个单相机实现分屏混合的功能, 需求大概就是在同一视角下, 相机通过不同的CullingMask获取不同的渲染图片RenderTexture之后, 通过某种方式一起显示在界面的功能. 其实核心逻辑就是怎样用一个相机渲染不同的图片罢了, 直接上代码:
using System.Collections; using System.Collections.Generic; using UnityEngine; [ExecuteInEditMode] public class BlendRenderTexture : MonoBehaviour { public enum BlendDirection { Horizontal = 0, Vertical = 1 } [SerializeField] [Range(0, 1)] public float blend = 0.5f; [SerializeField] public BlendDirection blendDirection = BlendDirection.Horizontal; [SerializeField] public LayerMask tagLayerMask; [SerializeField] public RenderingTools.BlendRenderTextureTarget blendRenderTextureTarget; private bool m_grabbing = false; private void OnEnable() { if(blendRenderTextureTarget == null) { blendRenderTextureTarget = new RenderingTools.BlendRenderTextureTarget("Unlit/BlendRenderTexture"); } } private void OnDisable() { if(blendRenderTextureTarget != null) { blendRenderTextureTarget.Release(); } } void OnRenderImage(RenderTexture src, RenderTexture dest) { if(m_grabbing) { m_grabbing = false; Graphics.Blit(src, dest); return; } if(blendRenderTextureTarget.renderTexture && blendRenderTextureTarget.material) { blendRenderTextureTarget.material.SetTexture("_Left", src); blendRenderTextureTarget.material.SetTexture("_Right", blendRenderTextureTarget.renderTexture); blendRenderTextureTarget.material.SetFloat("_Blend", Mathf.Clamp01(blend)); blendRenderTextureTarget.material.SetInt("_Direction", (int)blendDirection); Graphics.Blit(src, dest, blendRenderTextureTarget.material); } else { Graphics.Blit(src, dest); } } private void LateUpdate() { RenderTargetTexture(); } public void RenderTargetTexture() { var material = blendRenderTextureTarget.GetMaterial(); if (m_grabbing = material) { var lastMask = Camera.main.cullingMask; var lastTex = Camera.main.targetTexture; Camera.main.cullingMask = tagLayerMask; Camera.main.targetTexture = blendRenderTextureTarget.GetRenderTexture(); Camera.main.Render(); Camera.main.cullingMask = lastMask; Camera.main.targetTexture = lastTex; } } }
在LateUpdate中请求目标图片渲染, 标记了m_grabbing之后, 调用到OnRenderImage, 直接就把目标渲染图片输出到我们的临时RenderTexture上了, 然后再通过正常渲染时调用OnRenderImage之后, 就会通过Material进行混合了.
BlendRenderTextureTarget 只是一个资源的封装:
using System.Collections; using System.Collections.Generic; using UnityEngine; public static class RenderingTools { [System.Serializable] public class BlendRenderTextureTarget { [SerializeField] public Material material = null; [SerializeField] public string shaderName = string.Empty; public RenderTexture renderTexture { get; set; } public BlendRenderTextureTarget(string shaderName) { this.shaderName = shaderName; } public Material GetMaterial() { if(material == false) { var shader = Shader.Find(shaderName); if(shader) { material = new Material(shader); material.hideFlags = HideFlags.DontSave; } } return material; } public RenderTexture GetRenderTexture() { if(renderTexture) { if(renderTexture.width != Screen.width || renderTexture.height != Screen.height) { RenderTexture.ReleaseTemporary(renderTexture); renderTexture = null; } } if(renderTexture == false) { renderTexture = RenderTexture.GetTemporary(Screen.width, Screen.height, 24, RenderTextureFormat.ARGB32); renderTexture.hideFlags = HideFlags.DontSave; } return renderTexture; } public void Release() { if(renderTexture) { RenderTexture.ReleaseTemporary(renderTexture); renderTexture = null; } if(material) { material = null; } } } }
混用用到的Shader也很简单:
Shader "Unlit/BlendRenderTexture" { Properties { _MainTex("Texture", 2D) = "white" {} _Left("Texture", 2D) = "white" {} _Right("Texture", 2D) = "white" {} _Blend("Blend", Range(0,1)) = 0.5 [Enum(Horizontal,0,Vertical,1)] _Direction("Blend Direction", Float) = 0 } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag // make fog work #pragma multi_compile_fog #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; UNITY_FOG_COORDS(1) float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; sampler2D _Left; sampler2D _Right; float _Blend; float _Direction; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); UNITY_TRANSFER_FOG(o,o.vertex); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = 1; // sample the texture bool blend_left = (_Direction == 0) ? (i.uv.x <= _Blend) : (i.uv.y <= _Blend); if (blend_left) { col = tex2D(_Left, i.uv); } else { col = tex2D(_Right, i.uv); } // apply fog UNITY_APPLY_FOG(i.fogCoord, col); return col; } ENDCG } } }