主要参考Highlighting System 插件。
效果:
被Plane挡住:
没有被plane挡住:
HighlightableObjectSimple 组件,挂上这个组件的物体更换所有的材质,更换物体的layer。
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class HighlightableObjectSimple : MonoBehaviour {
private List highlightableRenderers;
private bool zWrite = false;
private static float transparentCutoff = 0.5f;
private bool materialsIsDirty = true;
private int[] layersCache;
public static int highlightingLayer = 7;
private Color currentColor;
private Material highlightingMaterial
{
get
{
return zWrite ? opaqueZMaterial : opaqueMaterial;
}
}
// Common (for this component) replacement material for opaque geometry highlighting
private Material _opaqueMaterial;
private Material opaqueMaterial
{
get
{
if (_opaqueMaterial == null)
{
_opaqueMaterial = new Material(opaqueShader);
_opaqueMaterial.hideFlags = HideFlags.HideAndDontSave;
}
return _opaqueMaterial;
}
}
// Common (for this component) replacement material for opaque geometry highlighting with Z-Buffer writing enabled
private Material _opaqueZMaterial;
private Material opaqueZMaterial
{
get
{
if (_opaqueZMaterial == null)
{
_opaqueZMaterial = new Material(opaqueZShader);
_opaqueZMaterial.hideFlags = HideFlags.HideAndDontSave;
}
return _opaqueZMaterial;
}
}
//
private static Shader _opaqueShader;
private static Shader opaqueShader
{
get
{
if (_opaqueShader == null)
{
_opaqueShader = Shader.Find("Hidden/Highlighted/StencilOpaque");
}
return _opaqueShader;
}
}
//
private static Shader _transparentShader;
public static Shader transparentShader
{
get
{
if (_transparentShader == null)
{
_transparentShader = Shader.Find("Hidden/Highlighted/StencilTransparent");
}
return _transparentShader;
}
}
//
private static Shader _opaqueZShader;
private static Shader opaqueZShader
{
get
{
if (_opaqueZShader == null)
{
_opaqueZShader = Shader.Find("Hidden/Highlighted/StencilOpaqueZ");
}
return _opaqueZShader;
}
}
//
private static Shader _transparentZShader;
private static Shader transparentZShader
{
get
{
if (_transparentZShader == null)
{
_transparentZShader = Shader.Find("Hidden/Highlighted/StencilTransparentZ");
}
return _transparentZShader;
}
}
void Start()
{
InitMaterials(false);
}
private void InitMaterials(bool writeDepth)
{
highlightableRenderers = new List();
MeshRenderer[] mr = GetComponentsInChildren();
CacheRenderers(mr);
SkinnedMeshRenderer[] smr = GetComponentsInChildren();
CacheRenderers(smr);
materialsIsDirty = false;
}
private void CacheRenderers(Renderer[] renderers)
{
for (int i = 0; i < renderers.Length; i++)
{
Material[] materials = renderers[i].sharedMaterials;
if (materials != null)
{
highlightableRenderers.Add(new HighlightingRendererCache(renderers[i], materials, highlightingMaterial, zWrite));
}
}
}
private void OnEnable()
{
HighlightingEffect.highlightingEvent += UpdateEventHandler;
}
private void OnDisable()
{
HighlightingEffect.highlightingEvent -= UpdateEventHandler;
if (highlightableRenderers != null)
{
highlightableRenderers.Clear();
}
// Reset highlighting parameters to default values
layersCache = null;
materialsIsDirty = true;
zWrite = false;
if (_opaqueMaterial)
{
DestroyImmediate(_opaqueMaterial);
}
if (_opaqueZMaterial)
{
DestroyImmediate(_opaqueZMaterial);
}
}
private void SetColor(Color c)
{
if (currentColor == c)
{
return;
}
if (zWrite)
{
opaqueZMaterial.SetColor("_Outline", c);
}
else
{
opaqueMaterial.SetColor("_Outline", c);
}
for (int i = 0; i < highlightableRenderers.Count; i++)
{
highlightableRenderers[i].SetColorForTransparent(c);
}
currentColor = c;
}
private void UpdateEventHandler(bool trigger, bool writeDepth)
{
// Update and enable highlighting
if (trigger)
{
// ZWriting state changed?
if (zWrite != writeDepth)
{
materialsIsDirty = true;
}
// Initialize new materials if needed
if (materialsIsDirty)
{
InitMaterials(writeDepth);
}
SetColor(Color.red);
if (highlightableRenderers != null)
{
layersCache = new int[highlightableRenderers.Count];
for (int i = 0; i < highlightableRenderers.Count; i++)
{
GameObject go = highlightableRenderers[i].goCached;
// cache layer
layersCache[i] = go.layer;
// temporary set layer to renderable by the highlighting effect camera
go.layer = highlightingLayer;
// 换材质
highlightableRenderers[i].SetState(true);
}
}
}
// Disable highlighting
else
{
if (highlightableRenderers != null)
{
for (int i = 0; i < highlightableRenderers.Count; i++)
{
highlightableRenderers[i].goCached.layer = layersCache[i];
highlightableRenderers[i].SetState(false);
}
}
}
}
private class HighlightingRendererCache
{
public Renderer rendererCached;
public GameObject goCached;
private Material[] sourceMaterials;
private Material[] replacementMaterials;
private List transparentMaterialIndexes;
// Constructor
public HighlightingRendererCache(Renderer rend, Material[] mats, Material sharedOpaqueMaterial, bool writeDepth)
{
rendererCached = rend;
goCached = rend.gameObject;
sourceMaterials = mats;
replacementMaterials = new Material[mats.Length];
transparentMaterialIndexes = new List();
for (int i = 0; i < mats.Length; i++)
{
Material sourceMat = mats[i];
if (sourceMat == null)
{
continue;
}
string tag = sourceMat.GetTag("RenderType", true);
if (tag == "Transparent" || tag == "TransparentCutout")
{
Material replacementMat = new Material(writeDepth ? transparentZShader : transparentShader);
if (sourceMat.HasProperty("_MainTex"))
{
replacementMat.SetTexture("_MainTex", sourceMat.mainTexture);
replacementMat.SetTextureOffset("_MainTex", sourceMat.mainTextureOffset);
replacementMat.SetTextureScale("_MainTex", sourceMat.mainTextureScale);
}
replacementMat.SetFloat("_Cutoff", sourceMat.HasProperty("_Cutoff") ? sourceMat.GetFloat("_Cutoff") : transparentCutoff);
replacementMaterials[i] = replacementMat;
transparentMaterialIndexes.Add(i);
}
else
{
replacementMaterials[i] = sharedOpaqueMaterial;
}
}
}
// Based on given state variable, replaces materials of this cached renderer to the highlighted ones and back
public void SetState(bool state)
{
rendererCached.sharedMaterials = state ? replacementMaterials : sourceMaterials;
}
// Sets given color as the highlighting color on all transparent materials for this cached renderer
public void SetColorForTransparent(Color clr)
{
for (int i = 0; i < transparentMaterialIndexes.Count; i++)
{
replacementMaterials[transparentMaterialIndexes[i]].SetColor("_Outline", clr);
}
}
}
}
HighlightingEffect 挂在Camera上。
以上的,主要就是为了渲染stencilBuffer rendererTexture。
using UnityEngine;
// Delegate for the highlighting event
public delegate void HighlightingEventHandler(bool state, bool zWrite);
[RequireComponent(typeof(Camera))]
public class HighlightingEffect : MonoBehaviour
{
// Event used to notify highlightable objects to change their materials before rendering to the highlighting buffer begins
public static event HighlightingEventHandler highlightingEvent;
#region Inspector Fields
// Stencil (highlighting) buffer depth
public int stencilZBufferDepth = 0;
// Stencil (highlighting) buffer size downsample factor
public int _downsampleFactor = 4;
// Blur iterations
public int iterations = 2;
// Blur minimal spread
public float blurMinSpread = 0.65f;
// Blur spread per iteration
public float blurSpread = 0.25f;
// Blurring intensity for the blur material
public float _blurIntensity = 0.3f;
// These properties available only in Editor - we don't need them in standalone build
#if UNITY_EDITOR
// Z-buffer writing state getter/setter
public bool stencilZBufferEnabled
{
get
{
return (stencilZBufferDepth > 0);
}
set
{
if (stencilZBufferEnabled != value)
{
stencilZBufferDepth = value ? 16 : 0;
}
}
}
// Downsampling factor getter/setter
public int downsampleFactor
{
get
{
if (_downsampleFactor == 1)
{
return 0;
}
if (_downsampleFactor == 2)
{
return 1;
}
return 2;
}
set
{
if (value == 0)
{
_downsampleFactor = 1;
}
if (value == 1)
{
_downsampleFactor = 2;
}
if (value == 2)
{
_downsampleFactor = 4;
}
}
}
// Blur alpha intensity getter/setter
public float blurIntensity
{
get
{
return _blurIntensity;
}
set
{
if (_blurIntensity != value)
{
_blurIntensity = value;
if (Application.isPlaying)
{
blurMaterial.SetFloat("_Intensity", _blurIntensity);
}
}
}
}
#endif
#endregion
#region Private Fields
// Highlighting camera layers culling mask
//private int layerMask = (1 << HighlightableObject.highlightingLayer);
private int layerMask = (1 << HighlightableObjectSimple.highlightingLayer);
// This GameObject reference
private GameObject go = null;
// Camera for rendering stencil buffer GameObject
private GameObject shaderCameraGO = null;
// Camera for rendering stencil buffer
private Camera shaderCamera = null;
// RenderTexture with stencil buffer
private RenderTexture stencilBuffer = null;
// Camera reference
private Camera refCam = null;
// Blur Shader
private static Shader _blurShader;
private static Shader blurShader
{
get
{
if (_blurShader == null)
{
_blurShader = Shader.Find("Hidden/Highlighted/Blur");
}
return _blurShader;
}
}
// Compositing Shader
private static Shader _compShader;
private static Shader compShader
{
get
{
if (_compShader == null)
{
_compShader = Shader.Find("Hidden/Highlighted/Composite");
}
return _compShader;
}
}
// Blur Material
private static Material _blurMaterial = null;
private static Material blurMaterial
{
get
{
if (_blurMaterial == null)
{
_blurMaterial = new Material(blurShader);
_blurMaterial.hideFlags = HideFlags.HideAndDontSave;
}
return _blurMaterial;
}
}
// Compositing Material
private static Material _compMaterial = null;
private static Material compMaterial
{
get
{
if (_compMaterial == null)
{
_compMaterial = new Material(compShader);
_compMaterial.hideFlags = HideFlags.HideAndDontSave;
}
return _compMaterial;
}
}
#endregion
void Awake()
{
go = gameObject;
refCam = GetComponent();
}
void OnDisable()
{
if (shaderCameraGO != null)
{
DestroyImmediate(shaderCameraGO);
}
if (_blurShader)
{
_blurShader = null;
}
if (_compShader)
{
_compShader = null;
}
if (_blurMaterial)
{
DestroyImmediate(_blurMaterial);
}
if (_compMaterial)
{
DestroyImmediate(_compMaterial);
}
if (stencilBuffer != null)
{
RenderTexture.ReleaseTemporary(stencilBuffer);
stencilBuffer = null;
}
}
void Start()
{
// Disable if Image Effects is not supported
if (!SystemInfo.supportsImageEffects)
{
Debug.LogWarning("HighlightingSystem : Image effects is not supported on this platform! Disabling.");
this.enabled = false;
return;
}
// Disable if Render Textures is not supported
if (!SystemInfo.supportsRenderTextures)
{
Debug.LogWarning("HighlightingSystem : RenderTextures is not supported on this platform! Disabling.");
this.enabled = false;
return;
}
// Disable if required Render Texture Format is not supported
if (!SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGB32))
{
Debug.LogWarning("HighlightingSystem : RenderTextureFormat.ARGB32 is not supported on this platform! Disabling.");
this.enabled = false;
return;
}
// Disable if HighlightingStencilOpaque shader is not supported
if (!Shader.Find("Hidden/Highlighted/StencilOpaque").isSupported)
{
Debug.LogWarning("HighlightingSystem : HighlightingStencilOpaque shader is not supported on this platform! Disabling.");
this.enabled = false;
return;
}
// Disable if HighlightingStencilTransparent shader is not supported
if (!Shader.Find("Hidden/Highlighted/StencilTransparent").isSupported)
{
Debug.LogWarning("HighlightingSystem : HighlightingStencilTransparent shader is not supported on this platform! Disabling.");
this.enabled = false;
return;
}
// Disable if HighlightingStencilOpaqueZ shader is not supported
if (!Shader.Find("Hidden/Highlighted/StencilOpaqueZ").isSupported)
{
Debug.LogWarning("HighlightingSystem : HighlightingStencilOpaqueZ shader is not supported on this platform! Disabling.");
this.enabled = false;
return;
}
// Disable if HighlightingStencilTransparentZ shader is not supported
if (!Shader.Find("Hidden/Highlighted/StencilTransparentZ").isSupported)
{
Debug.LogWarning("HighlightingSystem : HighlightingStencilTransparentZ shader is not supported on this platform! Disabling.");
this.enabled = false;
return;
}
// Disable if HighlightingBlur shader is not supported
if (!blurShader.isSupported)
{
Debug.LogWarning("HighlightingSystem : HighlightingBlur shader is not supported on this platform! Disabling.");
this.enabled = false;
return;
}
// Disable if HighlightingComposite shader is not supported
if (!compShader.isSupported)
{
Debug.LogWarning("HighlightingSystem : HighlightingComposite shader is not supported on this platform! Disabling.");
this.enabled = false;
return;
}
// Set the initial intensity in blur shader
blurMaterial.SetFloat("_Intensity", _blurIntensity);
}
// Performs one blur iteration
public void FourTapCone(RenderTexture source, RenderTexture dest, int iteration)
{
float off = blurMinSpread + iteration * blurSpread;
blurMaterial.SetFloat("_OffsetScale", off);
Graphics.Blit(source, dest, blurMaterial);
}
// Downsamples source texture
private void DownSample4x(RenderTexture source, RenderTexture dest)
{
float off = 1.0f;
blurMaterial.SetFloat("_OffsetScale", off);
Graphics.Blit(source, dest, blurMaterial);
}
// Render all highlighted objects to the stencil buffer
void OnPreRender()
{
#if UNITY_4_0
if (this.enabled == false || go.activeInHierarchy == false)
#else
if (this.enabled == false || go.active == false)
#endif
return;
if (stencilBuffer != null)
{
RenderTexture.ReleaseTemporary(stencilBuffer);
stencilBuffer = null;
}
// Turn on highlighted shaders
if (highlightingEvent != null)
{
highlightingEvent(true, (stencilZBufferDepth > 0));
}
// We don't need to render the scene if there's no HighlightableObjects
else
{
return;
}
stencilBuffer = RenderTexture.GetTemporary((int)GetComponent().pixelWidth, (int)GetComponent().pixelHeight, stencilZBufferDepth, RenderTextureFormat.ARGB32);
if (!shaderCameraGO)
{
shaderCameraGO = new GameObject("HighlightingCamera", typeof(Camera));
shaderCameraGO.GetComponent().enabled = false;
shaderCameraGO.hideFlags = HideFlags.HideAndDontSave;
}
if (!shaderCamera)
{
shaderCamera = shaderCameraGO.GetComponent();
}
shaderCamera.CopyFrom(refCam);
//shaderCamera.projectionMatrix = refCam.projectionMatrix; // Uncomment this line if you have problems using Highlighting System with custom projection matrix on your camera
shaderCamera.cullingMask = layerMask;
shaderCamera.rect = new Rect(0f, 0f, 1f, 1f);
shaderCamera.renderingPath = RenderingPath.VertexLit;
shaderCamera.hdr = false;
shaderCamera.useOcclusionCulling = false;
shaderCamera.backgroundColor = new Color(0f, 0f, 0f, 0f);
shaderCamera.clearFlags = CameraClearFlags.SolidColor;
shaderCamera.targetTexture = stencilBuffer;
shaderCamera.Render();
// Turn off highlighted shaders
if (highlightingEvent != null)
{
highlightingEvent(false, false);
}
}
// Compose final frame with highlighting
void OnRenderImage(RenderTexture source, RenderTexture destination)
{
// If stencilBuffer is not created by some reason
if (stencilBuffer == null)
{
// Simply transfer framebuffer to destination
Graphics.Blit(source, destination);
return;
}
// Create two buffers for blurring the image
int width = source.width / _downsampleFactor;
int height = source.height / _downsampleFactor;
RenderTexture buffer = RenderTexture.GetTemporary(width, height, stencilZBufferDepth, RenderTextureFormat.ARGB32);
RenderTexture buffer2 = RenderTexture.GetTemporary(width, height, stencilZBufferDepth, RenderTextureFormat.ARGB32);
// Copy stencil buffer to the 4x4 smaller texture
DownSample4x(stencilBuffer, buffer);
// Blur the small texture
bool oddEven = true;
for (int i = 0; i < iterations; i++)
{
if (oddEven)
{
FourTapCone(buffer, buffer2, i);
}
else
{
FourTapCone(buffer2, buffer, i);
}
oddEven = !oddEven;
}
// Compose
compMaterial.SetTexture("_StencilTex", stencilBuffer);
compMaterial.SetTexture("_BlurTex", oddEven ? buffer : buffer2);
Graphics.Blit(source, destination, compMaterial);
// Cleanup
RenderTexture.ReleaseTemporary(buffer);
RenderTexture.ReleaseTemporary(buffer2);
if (stencilBuffer != null)
{
RenderTexture.ReleaseTemporary(stencilBuffer);
stencilBuffer = null;
}
}
}
HighlightingInclude.cginc
#ifndef HIGHLIGHTING_CG_INCLUDED
#define HIGHLIGHTING_CG_INCLUDED
#include "UnityCG.cginc"
// Opaque
uniform fixed4 _Outline;
struct appdata_vert
{
float4 vertex : POSITION;
};
float4 vert(appdata_vert v) : POSITION
{
return mul(UNITY_MATRIX_MVP, v.vertex);
}
fixed4 frag() : COLOR
{
return _Outline;
}
// Transparent
uniform sampler2D _MainTex;
uniform float4 _MainTex_ST;
uniform fixed _Cutoff;
struct appdata_vert_tex
{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float4 pos : POSITION;
float2 texcoord : TEXCOORD0;
};
v2f vert_alpha(appdata_vert_tex v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag_alpha(v2f i) : COLOR
{
clip(tex2D(_MainTex, i.texcoord).a - _Cutoff);
return _Outline;
}
#endif
HighlightingStencilOpaque.shader
Shader "Hidden/Highlighted/StencilOpaque"
{
CGINCLUDE
#include "HighlightingInclude.cginc"
ENDCG
SubShader
{
Pass
{
ZWrite Off
ZTest Always
Lighting Off
Fog { Mode Off }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
ENDCG
}
}
Fallback Off
}
HighlightingStencilOpaqueZ.shader
Shader "Hidden/Highlighted/StencilOpaqueZ"
{
CGINCLUDE
#include "HighlightingInclude.cginc"
ENDCG
SubShader
{
Pass
{
ZWrite On
ZTest LEqual
Lighting Off
Fog { Mode Off }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
ENDCG
}
}
Fallback Off
}
Shader "Hidden/Highlighted/StencilTransparent"
{
Properties
{
_MainTex ("", 2D) = "" {}
_Cutoff ("", Float) = 0.5
}
CGINCLUDE
#include "HighlightingInclude.cginc"
ENDCG
SubShader
{
Pass
{
ZWrite Off
ZTest Always
Lighting Off
Fog { Mode Off }
CGPROGRAM
#pragma vertex vert_alpha
#pragma fragment frag_alpha
#pragma fragmentoption ARB_precision_hint_fastest
ENDCG
}
}
Fallback Off
}
HighlightingStencilTransparentZ.shader
Shader "Hidden/Highlighted/StencilTransparentZ"
{
Properties
{
_MainTex ("", 2D) = "" {}
_Cutoff ("", Float) = 0.5
}
CGINCLUDE
#include "HighlightingInclude.cginc"
ENDCG
SubShader
{
Pass
{
ZWrite On
ZTest LEqual
Lighting Off
Fog { Mode Off }
CGPROGRAM
#pragma vertex vert_alpha
#pragma fragment frag_alpha
#pragma fragmentoption ARB_precision_hint_fastest
ENDCG
}
}
Fallback Off
}
Shader "Hidden/Highlighted/Blur"
{
Properties
{
_MainTex ("", 2D) = "" {}
_Intensity ("", Range (0.25,0.5)) = 0.3
}
SubShader
{
Pass
{
ZTest Always
Cull Off
ZWrite Off
Lighting Off
Fog { Mode Off }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
uniform sampler2D _MainTex;
uniform half4 _MainTex_TexelSize;
uniform half _OffsetScale;
uniform fixed _Intensity;
struct v2f
{
float4 pos : POSITION;
half2 uv[4] : TEXCOORD0;
};
v2f vert (appdata_img v)
{
// Shader code optimized for the Unity shader compiler
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
half2 offs = _MainTex_TexelSize.xy * _OffsetScale;
o.uv[0].x = v.texcoord.x - offs.x;
o.uv[0].y = v.texcoord.y - offs.y;
o.uv[1].x = v.texcoord.x + offs.x;
o.uv[1].y = v.texcoord.y - offs.y;
o.uv[2].x = v.texcoord.x + offs.x;
o.uv[2].y = v.texcoord.y + offs.y;
o.uv[3].x = v.texcoord.x - offs.x;
o.uv[3].y = v.texcoord.y + offs.y;
return o;
}
fixed4 frag(v2f i) : COLOR
{
fixed4 color1 = tex2D(_MainTex, i.uv[0]);
fixed4 color2 = tex2D(_MainTex, i.uv[1]);
fixed4 color3 = tex2D(_MainTex, i.uv[2]);
fixed4 color4 = tex2D(_MainTex, i.uv[3]);
fixed4 color;
color.rgb = max(color1.rgb, color2.rgb);
color.rgb = max(color.rgb, color3.rgb);
color.rgb = max(color.rgb, color4.rgb);
color.a = (color1.a + color2.a + color3.a + color4.a) * _Intensity;
return color;
}
ENDCG
}
}
Fallback off
}
Shader "Hidden/Highlighted/Composite"
{
Properties
{
_MainTex ("", 2D) = "" {}
_BlurTex ("", 2D) = "" {}
_StencilTex ("", 2D) = "" {}
}
SubShader
{
Pass
{
ZTest Always
Cull Off
ZWrite Off
Lighting Off
Fog { Mode off }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
struct v2f
{
float4 pos : POSITION;
half2 uv[2] : TEXCOORD0;
};
uniform sampler2D _MainTex;
uniform sampler2D _BlurTex;
uniform sampler2D _StencilTex;
float4 _MainTex_TexelSize;
v2f vert (appdata_img v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv[0] = v.texcoord.xy;
o.uv[1] = v.texcoord.xy;
if (_MainTex_TexelSize.y < 0)
o.uv[1].y = 1 - o.uv[1].y;
return o;
}
fixed4 frag(v2f i) : COLOR
{
fixed4 framebuffer = tex2D(_MainTex, i.uv[0]);
fixed4 stencil = tex2D(_StencilTex, i.uv[1]);
if (any(stencil.rgb))
{
return framebuffer;
}
else
{
fixed4 blurred = tex2D(_BlurTex, i.uv[1]);
fixed4 color;
color.rgb = lerp(framebuffer.rgb, blurred.rgb, saturate(blurred.a - stencil.a));
color.a = framebuffer.a;
return color;
}
}
ENDCG
}
}
SubShader
{
Pass
{
SetTexture [_MainTex] {}
}
}
Fallback Off
}