做了一个单相机实现分屏混合的功能, 需求大概就是在同一视角下, 相机通过不同的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
}
}
}