问题背景:

最近要实现选中实体的高亮效果,要那种类似于unity中Outline的效果,网格轮廓高亮效果。

效果图:

具体代码:

OutlineEffect.cs

实体高亮效果类:

轮廓边总控制类,该脚本需要挂载到场景相机上

 using UnityEngine;
using System.Collections.Generic;
using UnityEngine.Rendering; namespace Tx3d.Framework
{
[DisallowMultipleComponent]
[RequireComponent(typeof(Camera))]
[ExecuteInEditMode]
public class OutlineEffect : MonoBehaviour
{
public static OutlineEffect Instance { get; private set; } private readonly LinkedSet<Outline> outlines = new LinkedSet<Outline>(); [Range(1.0f, 6.0f)]
public float lineThickness = 1.0f;
[Range(, )]
public float lineIntensity = 1.2f;
[Range(, )]
public float fillAmount = 0.108f; public Color lineColor0 = Color.yellow;
public Color lineColor1 = Color.green;
public Color lineColor2 = Color.blue;
public Color lineColor3 = Color.cyan; public bool additiveRendering = false; public bool backfaceCulling = true; [Header("These settings can affect performance!")]
public bool cornerOutlines = false;
public bool addLinesBetweenColors = false; [Header("Advanced settings")]
public bool scaleWithScreenSize = true;
[Range(0.1f, .9f)]
public float alphaCutoff = .5f;
public bool flipY = false;
public Camera sourceCamera;
public bool autoEnableOutlines = true; [HideInInspector]
public Camera outlineCamera;
Material outline1Material;
Material outline2Material;
Material outline3Material;
Material outline4Material;
Material outlineEraseMaterial;
Shader outlineShader;
Shader outlineBufferShader;
[HideInInspector]
public Material outlineShaderMaterial;
[HideInInspector]
public RenderTexture renderTexture;
[HideInInspector]
public RenderTexture extraRenderTexture; CommandBuffer commandBuffer; Material GetMaterialFromID(int ID)
{
if (ID == )
return outline1Material;
else if (ID == )
return outline2Material;
else if (ID == )
return outline3Material;
else if (ID == )
return outline4Material;
else
return outline1Material;
}
List<Material> materialBuffer = new List<Material>();
Material CreateMaterial(Color emissionColor)
{
Material m = new Material(outlineBufferShader);
m.SetColor("_Color", emissionColor);
m.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
m.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
m.SetInt("_ZWrite", );
m.DisableKeyword("_ALPHATEST_ON");
m.EnableKeyword("_ALPHABLEND_ON");
m.DisableKeyword("_ALPHAPREMULTIPLY_ON");
m.renderQueue = ;
return m;
} private void Awake()
{
if (Instance != null)
{
Destroy(this);
throw new System.Exception("you can only have one outline camera in the scene");
} Instance = this;
} void Start()
{
CreateMaterialsIfNeeded();
UpdateMaterialsPublicProperties(); if (sourceCamera == null)
{
sourceCamera = GetComponent<Camera>(); if (sourceCamera == null)
sourceCamera = Camera.main;
} if (outlineCamera == null)
{
foreach (Camera c in GetComponentsInChildren<Camera>())
{
if (c.name == "Outline Camera")
{
outlineCamera = c;
c.enabled = false; break;
}
} if (outlineCamera == null)
{
GameObject cameraGameObject = new GameObject("Outline Camera");
cameraGameObject.transform.parent = sourceCamera.transform;
outlineCamera = cameraGameObject.AddComponent<Camera>();
outlineCamera.enabled = false;
}
} renderTexture = new RenderTexture(sourceCamera.pixelWidth, sourceCamera.pixelHeight, , RenderTextureFormat.Default);
extraRenderTexture = new RenderTexture(sourceCamera.pixelWidth, sourceCamera.pixelHeight, , RenderTextureFormat.Default);
UpdateOutlineCameraFromSource(); commandBuffer = new CommandBuffer();
outlineCamera.AddCommandBuffer(CameraEvent.BeforeImageEffects, commandBuffer);
} bool RenderTheNextFrame;
public void OnPreRender()
{
if (commandBuffer == null)
return; // the first frame during which there are no outlines, we still need to render
// to clear out any outlines that were being rendered on the previous frame
if (outlines.Count == )
{
if (!RenderTheNextFrame)
return; RenderTheNextFrame = false;
}
else
{
RenderTheNextFrame = true;
} CreateMaterialsIfNeeded(); if (renderTexture == null || renderTexture.width != sourceCamera.pixelWidth || renderTexture.height != sourceCamera.pixelHeight)
{
renderTexture = new RenderTexture(sourceCamera.pixelWidth, sourceCamera.pixelHeight, , RenderTextureFormat.Default);
extraRenderTexture = new RenderTexture(sourceCamera.pixelWidth, sourceCamera.pixelHeight, , RenderTextureFormat.Default);
outlineCamera.targetTexture = renderTexture;
}
UpdateMaterialsPublicProperties();
UpdateOutlineCameraFromSource();
outlineCamera.targetTexture = renderTexture;
commandBuffer.SetRenderTarget(renderTexture); commandBuffer.Clear(); foreach (Outline outline in outlines)
{
LayerMask l = sourceCamera.cullingMask; // if (outline != null && l == (l | (1 << outline.gameObject.layer)))
if (outline != null)
{
for (int v = ; v < outline.SharedMaterials.Length; v++)
{
Material m = null; if (outline.SharedMaterials[v].mainTexture != null && outline.SharedMaterials[v])
{
foreach (Material g in materialBuffer)
{
if (g.mainTexture == outline.SharedMaterials[v].mainTexture)
{
if (outline.eraseRenderer && g.color == outlineEraseMaterial.color)
m = g;
else if (g.color == GetMaterialFromID(outline.color).color)
m = g;
}
} if (m == null)
{
if (outline.eraseRenderer)
m = new Material(outlineEraseMaterial);
else
m = new Material(GetMaterialFromID(outline.color));
m.mainTexture = outline.SharedMaterials[v].mainTexture;
materialBuffer.Add(m);
}
}
else
{
if (outline.eraseRenderer)
m = outlineEraseMaterial;
else
m = GetMaterialFromID(outline.color);
} if (backfaceCulling)
m.SetInt("_Culling", (int)UnityEngine.Rendering.CullMode.Back);
else
m.SetInt("_Culling", (int)UnityEngine.Rendering.CullMode.Off); commandBuffer.DrawRenderer(outline.Renderer, m, , );
MeshFilter mL = outline.MeshFilter;
if (mL)
{
if (mL.sharedMesh != null)
{
for (int i = ; i < mL.sharedMesh.subMeshCount; i++)
commandBuffer.DrawRenderer(outline.Renderer, m, i, );
}
}
SkinnedMeshRenderer sMR = outline.SkinnedMeshRenderer;
if (sMR)
{
if (sMR.sharedMesh != null)
{
for (int i = ; i < sMR.sharedMesh.subMeshCount; i++)
commandBuffer.DrawRenderer(outline.Renderer, m, i, );
}
}
}
}
} outlineCamera.Render();
} private void OnEnable()
{
//if (autoEnableOutlines)
//{
// Outline[] o = FindObjectsOfType<Outline>(); // foreach (Outline oL in o)
// {
// oL.enabled = false;
// oL.enabled = true;
// }
//}
} void OnDestroy()
{
if (renderTexture != null)
renderTexture.Release();
if (extraRenderTexture != null)
extraRenderTexture.Release();
DestroyMaterials();
} void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (outlineShaderMaterial != null)
{
outlineShaderMaterial.SetTexture("_OutlineSource", renderTexture); if (addLinesBetweenColors)
{
Graphics.Blit(source, extraRenderTexture, outlineShaderMaterial, );
outlineShaderMaterial.SetTexture("_OutlineSource", extraRenderTexture);
}
Graphics.Blit(source, destination, outlineShaderMaterial, );
}
} private void CreateMaterialsIfNeeded()
{
if (outlineShader == null)
outlineShader = Resources.Load<Shader>("Shaders/Outline/OutlineShader");
if (outlineBufferShader == null)
{
outlineBufferShader = Resources.Load<Shader>("Shaders/Outline/OutlineBufferShader");
}
if (outlineShaderMaterial == null)
{
outlineShaderMaterial = new Material(outlineShader);
outlineShaderMaterial.hideFlags = HideFlags.HideAndDontSave;
UpdateMaterialsPublicProperties();
}
if (outlineEraseMaterial == null)
outlineEraseMaterial = CreateMaterial(new Color(, , , ));
if (outline1Material == null)
outline1Material = CreateMaterial(new Color(, , , ));
if (outline2Material == null)
outline2Material = CreateMaterial(new Color(, , , ));
if (outline3Material == null)
outline3Material = CreateMaterial(new Color(, , , ));
if (outline4Material == null)
outline4Material = CreateMaterial(new Color(, , , ));
} private void DestroyMaterials()
{
foreach (Material m in materialBuffer)
DestroyImmediate(m);
materialBuffer.Clear();
DestroyImmediate(outlineShaderMaterial);
DestroyImmediate(outlineEraseMaterial);
DestroyImmediate(outline1Material);
DestroyImmediate(outline2Material);
DestroyImmediate(outline3Material);
outlineShader = null;
outlineBufferShader = null;
outlineShaderMaterial = null;
outlineEraseMaterial = null;
outline1Material = null;
outline2Material = null;
outline3Material = null;
outline4Material = null;
} public void UpdateMaterialsPublicProperties()
{
if (outlineShaderMaterial)
{
float scalingFactor = ;
if (scaleWithScreenSize)
{
// If Screen.height gets bigger, outlines gets thicker
scalingFactor = Screen.height / 360.0f;
} // If scaling is too small (height less than 360 pixels), make sure you still render the outlines, but render them with 1 thickness
if (scaleWithScreenSize && scalingFactor < )
{
if (UnityEngine.XR.XRSettings.isDeviceActive && sourceCamera.stereoTargetEye != StereoTargetEyeMask.None)
{
outlineShaderMaterial.SetFloat("_LineThicknessX", ( / 1000.0f) * (1.0f / UnityEngine.XR.XRSettings.eyeTextureWidth) * 1000.0f);
outlineShaderMaterial.SetFloat("_LineThicknessY", ( / 1000.0f) * (1.0f / UnityEngine.XR.XRSettings.eyeTextureHeight) * 1000.0f);
}
else
{
outlineShaderMaterial.SetFloat("_LineThicknessX", ( / 1000.0f) * (1.0f / Screen.width) * 1000.0f);
outlineShaderMaterial.SetFloat("_LineThicknessY", ( / 1000.0f) * (1.0f / Screen.height) * 1000.0f);
}
}
else
{
if (UnityEngine.XR.XRSettings.isDeviceActive && sourceCamera.stereoTargetEye != StereoTargetEyeMask.None)
{
outlineShaderMaterial.SetFloat("_LineThicknessX", scalingFactor * (lineThickness / 1000.0f) * (1.0f / UnityEngine.XR.XRSettings.eyeTextureWidth) * 1000.0f);
outlineShaderMaterial.SetFloat("_LineThicknessY", scalingFactor * (lineThickness / 1000.0f) * (1.0f / UnityEngine.XR.XRSettings.eyeTextureHeight) * 1000.0f);
}
else
{
outlineShaderMaterial.SetFloat("_LineThicknessX", scalingFactor * (lineThickness / 1000.0f) * (1.0f / Screen.width) * 1000.0f);
outlineShaderMaterial.SetFloat("_LineThicknessY", scalingFactor * (lineThickness / 1000.0f) * (1.0f / Screen.height) * 1000.0f);
}
}
outlineShaderMaterial.SetFloat("_LineIntensity", lineIntensity);
outlineShaderMaterial.SetFloat("_FillAmount", fillAmount);
outlineShaderMaterial.SetColor("_LineColor1", lineColor0 * lineColor0);
outlineShaderMaterial.SetColor("_LineColor2", lineColor1 * lineColor1);
outlineShaderMaterial.SetColor("_LineColor3", lineColor2 * lineColor2);
outlineShaderMaterial.SetColor("_LineColor4", lineColor3 * lineColor3);
if (flipY)
outlineShaderMaterial.SetInt("_FlipY", );
else
outlineShaderMaterial.SetInt("_FlipY", );
if (!additiveRendering)
outlineShaderMaterial.SetInt("_Dark", );
else
outlineShaderMaterial.SetInt("_Dark", );
if (cornerOutlines)
outlineShaderMaterial.SetInt("_CornerOutlines", );
else
outlineShaderMaterial.SetInt("_CornerOutlines", ); Shader.SetGlobalFloat("_OutlineAlphaCutoff", alphaCutoff);
}
} void UpdateOutlineCameraFromSource()
{
outlineCamera.CopyFrom(sourceCamera);
outlineCamera.renderingPath = RenderingPath.Forward;
outlineCamera.backgroundColor = new Color(0.0f, 0.0f, 0.0f, 0.0f);
outlineCamera.clearFlags = CameraClearFlags.SolidColor;
outlineCamera.rect = new Rect(, , , );
outlineCamera.cullingMask = ;
outlineCamera.targetTexture = renderTexture;
outlineCamera.enabled = false;
#if UNITY_EDITOR
outlineCamera.allowHDR = false;
#else
outlineCamera.allowHDR = false;
#endif
} public void AddOutline(Outline outline)
=> outlines.Add(outline); public void RemoveOutline(Outline outline)
=> outlines.Remove(outline);
}
}

LinkedSet.cs

实体高亮效果的集合相关逻辑类:

辅助OutlineEffect类

 using System;
using System.Collections.Generic; namespace Tx3d
{
/// <summary>
/// 具有列表的快速迭代时间、无重复和快速删除/包含HashSet时间的集合。
/// </summary>
public class LinkedSet<T> : IEnumerable<T>
{
private LinkedList<T> list;
private Dictionary<T, LinkedListNode<T>> dictionary; public LinkedSet()
{
list = new LinkedList<T>();
dictionary = new Dictionary<T, LinkedListNode<T>>();
} public LinkedSet(IEqualityComparer<T> comparer)
{
list = new LinkedList<T>();
dictionary = new Dictionary<T, LinkedListNode<T>>(comparer);
} /// <summary>
/// 如果项在LinkedSet中不存在,则返回true
/// </summary>
public bool Add(T t)
{
if (dictionary.ContainsKey(t))
return false; LinkedListNode<T> node = list.AddLast(t);
dictionary.Add(t, node);
return true;
} /// <summary>
/// 如果项之前确实存在于LinkedSet中,则返回true
/// </summary>
public bool Remove(T t)
{
LinkedListNode<T> node; if (dictionary.TryGetValue(t, out node))
{
dictionary.Remove(t);
list.Remove(node);
return true;
}
else
{
return false;
}
} public void Clear()
{
list.Clear();
dictionary.Clear();
} public bool Contains(T t)
=> dictionary.ContainsKey(t); public int Count
=> list.Count; public IEnumerator<T> GetEnumerator()
=> list.GetEnumerator(); System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
=> list.GetEnumerator();
}
}

Outline.cs

外边框高亮基本信息类:

该信息类需要的mesh渲染器所在的物体上的信息(谁有MeshRenderer和MeshFilter物体的信息)

     /// <summary>
/// 外边高亮基本信息类
/// </summary>
public class Outline
{
public Renderer Renderer { get; set; }
public SkinnedMeshRenderer SkinnedMeshRenderer { get; set; }
public MeshFilter MeshFilter { get; set; } public int color;
public bool eraseRenderer; private Material[] _SharedMaterials;
public Material[] SharedMaterials
{
get
{
if (_SharedMaterials == null)
_SharedMaterials = Renderer.sharedMaterials; return _SharedMaterials;
}
}
}

OutlineEffect.shader

计算所需要的shader

 Shader "Hidden/OutlineEffect"
{
Properties
{
_MainTex ("Base (RGB)", 2D) = "white" {} }
SubShader
{
Pass
{
Tags{ "RenderType" = "Opaque" }
LOD
ZTest Always
ZWrite Off
Cull Off CGPROGRAM #pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#include "UnityCG.cginc" sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _OutlineSource; struct v2f
{
float4 position : SV_POSITION;
float2 uv : TEXCOORD0;
}; v2f vert(appdata_img v)
{
v2f o;
o.position = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord; return o;
} float _LineThicknessX;
float _LineThicknessY;
int _FlipY;
uniform float4 _MainTex_TexelSize; half4 frag(v2f input) : COLOR
{
float2 uv = input.uv;
if (_FlipY == )
uv.y = uv.y;
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < )
uv.y = - uv.y;
#endif //half4 originalPixel = tex2D(_MainTex,input.uv, UnityStereoScreenSpaceUVAdjust(input.uv, _MainTex_ST));
half4 outlineSource = tex2D(_OutlineSource, UnityStereoScreenSpaceUVAdjust(uv, _MainTex_ST)); const float h = .95f; half4 sample1 = tex2D(_OutlineSource, uv + float2(_LineThicknessX,0.0));
half4 sample2 = tex2D(_OutlineSource, uv + float2(-_LineThicknessX,0.0));
half4 sample3 = tex2D(_OutlineSource, uv + float2(.,_LineThicknessY));
half4 sample4 = tex2D(_OutlineSource, uv + float2(.,-_LineThicknessY)); bool red = sample1.r > h || sample2.r > h || sample3.r > h || sample4.r > h;
bool green = sample1.g > h || sample2.g > h || sample3.g > h || sample4.g > h;
bool blue = sample1.b > h || sample2.b > h || sample3.b > h || sample4.b > h; if ((red && blue) || (green && blue) || (red && green))
return float4(,,,);
else
return outlineSource;
} ENDCG
} Pass
{
Tags { "RenderType"="Opaque" }
LOD
ZTest Always
ZWrite Off
Cull Off CGPROGRAM #pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#include "UnityCG.cginc" sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _OutlineSource; struct v2f {
float4 position : SV_POSITION;
float2 uv : TEXCOORD0;
}; v2f vert(appdata_img v)
{
v2f o;
o.position = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord; return o;
} float _LineThicknessX;
float _LineThicknessY;
float _LineIntensity;
half4 _LineColor1;
half4 _LineColor2;
half4 _LineColor3;
half4 _LineColor4;
int _FlipY;
int _Dark;
float _FillAmount;
int _CornerOutlines;
uniform float4 _MainTex_TexelSize; half4 frag (v2f input) : COLOR
{
float2 uv = input.uv;
if (_FlipY == )
uv.y = - uv.y;
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < )
uv.y = - uv.y;
#endif half4 originalPixel = tex2D(_MainTex, UnityStereoScreenSpaceUVAdjust(input.uv, _MainTex_ST));
half4 outlineSource = tex2D(_OutlineSource, UnityStereoScreenSpaceUVAdjust(uv, _MainTex_ST)); const float h = .95f;
half4 outline = ;
bool hasOutline = false; half4 sample1 = tex2D(_OutlineSource, uv + float2(_LineThicknessX,0.0));
half4 sample2 = tex2D(_OutlineSource, uv + float2(-_LineThicknessX,0.0));
half4 sample3 = tex2D(_OutlineSource, uv + float2(.,_LineThicknessY));
half4 sample4 = tex2D(_OutlineSource, uv + float2(.,-_LineThicknessY)); bool outside = outlineSource.a < h;
bool outsideDark = outside && _Dark; if (_CornerOutlines)
{
// TODO: Conditional compile
half4 sample5 = tex2D(_OutlineSource, uv + float2(_LineThicknessX, _LineThicknessY));
half4 sample6 = tex2D(_OutlineSource, uv + float2(-_LineThicknessX, -_LineThicknessY));
half4 sample7 = tex2D(_OutlineSource, uv + float2(_LineThicknessX, -_LineThicknessY));
half4 sample8 = tex2D(_OutlineSource, uv + float2(-_LineThicknessX, _LineThicknessY)); if (sample1.r > h || sample2.r > h || sample3.r > h || sample4.r > h ||
sample5.r > h || sample6.r > h || sample7.r > h || sample8.r > h)
{
outline = _LineColor1 * _LineIntensity * _LineColor1.a;
if (outsideDark)
originalPixel *= - _LineColor1.a;
hasOutline = true;
}
else if (sample1.g > h || sample2.g > h || sample3.g > h || sample4.g > h ||
sample5.g > h || sample6.g > h || sample7.g > h || sample8.g > h)
{
outline = _LineColor2 * _LineIntensity * _LineColor2.a;
if (outsideDark)
originalPixel *= - _LineColor2.a;
hasOutline = true;
}
else if (sample1.b > h || sample2.b > h || sample3.b > h || sample4.b > h ||
sample5.b > h || sample6.b > h || sample7.b > h || sample8.b > h)
{
outline = _LineColor3 * _LineIntensity * _LineColor3.a;
if (outsideDark)
originalPixel *= - _LineColor3.a;
hasOutline = true;
}
else if (sample1.a > h || sample2.a > h || sample3.a > h || sample4.a > h ||
sample5.a > h || sample6.a > h || sample7.a > h || sample8.a > h)
{
outline = _LineColor4 * _LineIntensity * _LineColor4.a;
if (outsideDark)
originalPixel *= - _LineColor4.a;
hasOutline = true;
} if (!outside)
outline *= _FillAmount;
}
else
{
if (sample1.r > h || sample2.r > h || sample3.r > h || sample4.r > h)
{
outline = _LineColor1 * _LineIntensity * _LineColor1.a;
if (outsideDark)
originalPixel *= - _LineColor1.a;
hasOutline = true;
}
else if (sample1.g > h || sample2.g > h || sample3.g > h || sample4.g > h)
{
outline = _LineColor2 * _LineIntensity * _LineColor2.a;
if (outsideDark)
originalPixel *= - _LineColor2.a;
hasOutline = true;
}
else if (sample1.b > h || sample2.b > h || sample3.b > h || sample4.b > h)
{
outline = _LineColor3 * _LineIntensity * _LineColor3.a;
if (outsideDark)
originalPixel *= - _LineColor3.a;
hasOutline = true;
}
else if (sample1.a > h || sample2.a > h || sample3.a > h || sample4.a > h)
{
outline = _LineColor4 * _LineIntensity * _LineColor4.a;
if (outsideDark)
originalPixel *= - _LineColor4.a;
hasOutline = true;
} if (!outside)
outline *= _FillAmount;
} //return outlineSource;
if (hasOutline)
return lerp(originalPixel + outline, outline, _FillAmount);
else
return originalPixel;
} ENDCG
}
} FallBack "Diffuse"
}

OutlineBufferEffect.shader

计算所需要的shader

 Shader "Hidden/OutlineBufferEffect" {
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (,,,)
[MaterialToggle] PixelSnap ("Pixel snap", Float) =
} SubShader
{
Tags
{
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
"PreviewType" = "Plane"
"CanUseSpriteAtlas" = "True"
} // Change this stuff in OutlineEffect.cs instead!
//ZWrite Off
//Blend One OneMinusSrcAlpha
Cull [_Culling]
Lighting Off CGPROGRAM #pragma surface surf Lambert vertex:vert nofog noshadow noambient nolightmap novertexlights noshadowmask nometa //keepalpha
#pragma multi_compile _ PIXELSNAP_ON sampler2D _MainTex;
fixed4 _Color;
float _OutlineAlphaCutoff; struct Input
{
float2 uv_MainTex;
//fixed4 color;
}; void vert(inout appdata_full v, out Input o)
{
#if defined(PIXELSNAP_ON)
v.vertex = UnityPixelSnap(v.vertex);
#endif UNITY_INITIALIZE_OUTPUT(Input, o);
//o.color = v.color;
} void surf(Input IN, inout SurfaceOutput o)
{
fixed4 c = tex2D(_MainTex, IN.uv_MainTex);// * IN.color;
if (c.a < _OutlineAlphaCutoff) discard; float alpha = c.a * ; o.Albedo = _Color * alpha;
o.Alpha = alpha;
o.Emission = o.Albedo;
} ENDCG
} Fallback "Transparent/VertexLit"
}

//测试代码

其中outline是上面的信息对象,通过OutlineEffect中的AddOutline函数以及 RemoveOutline函数对场景物体进行管理,将需要高亮的物体的mesh信息构建的基本信息类并使用AddOutline函数添加进去,才可以实现高亮,取消高亮即调用RemoveOutline移除取消高亮物体的信息

  // 实体是否高亮
public bool Highlight
{
get => highlight;
set
{
highlight = value; if (highlight)
{
if (gameObject != null)
{
outline = outline ?? new Outline();
outline.Renderer = gameObject.GetComponent<Renderer>();
outline.MeshFilter = gameObject.GetComponent<MeshFilter>();
outline.SkinnedMeshRenderer = gameObject.GetComponent<SkinnedMeshRenderer>();
OutlineEffect.Instance?.AddOutline(outline);
}
}
else
{
OutlineEffect.Instance?.RemoveOutline(outline);
}
}
}

ok,实现了,但是这里的shader是摘得,因为我还在shader的学习阶段,记录下功能吧也算是

最新:

按照上述方式实现外轮廓,会有很严重的锯齿,而且抗锯齿操作,由于OutlineCamera的 Renderertexture,本来渲的图就很糙,不规则很毛糙,直接边缘检测模糊处理起来也很糙,效果很差,所以不得不再找其他方式

处理前效果:

解决方案:高斯模糊,纵向模糊以及横向模糊两种模糊解决这个问题。

模糊效果:

具体步骤:

1.将outlineCamera的RendererTexture全部模糊。

2.再将轮廓线颜色再与主纹理混合

注:

1.r为1的地区,rbg=0,因为该区域应该事自己的颜色为非轮廓颜色

2.OnRenderImage函数中只有在最后返回时才能动主Camera的 RenderTexture。

主要代码:

      RenderTexture temp=null;

  private  void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (outlineShaderMaterial != null)
{
outlineShaderMaterial.SetTexture("_OutlineSource", renderTexture); if (temp==null)
{
temp = new RenderTexture(source.width,source.height, , RenderTextureFormat.Default);
} ////高斯模糊轮廓逻辑 if (outlines.Count != )
{
if (golMaterial != null)
{
int rtW = source.width / downSample;
int rtH = source.height / downSample; RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, );
buffer0.filterMode = FilterMode.Bilinear; //轮廓颜色
golMaterial.SetColor("_TargetColor", lineColor0); //将OutlineCamera的RendererTexture Copy 给buffer0
Graphics.Blit(renderTexture, buffer0); for (int i = ; i < iterations; i++)
{
golMaterial.SetFloat("_BlurSize", 1.0f + i * blurSpread); RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, ); // Render the vertical pass
Graphics.Blit(buffer0, buffer1, golMaterial, ); RenderTexture.ReleaseTemporary(buffer0);
buffer0 = buffer1;
buffer1 = RenderTexture.GetTemporary(rtW, rtH, ); // Render the horizontal pass
Graphics.Blit(buffer0, buffer1, golMaterial, ); RenderTexture.ReleaseTemporary(buffer0);
buffer0 = buffer1;
} //将模糊完的纹理传给混合Shader,去混合,buffer0混合完的纹理
golMaterial.SetTexture("_OutlineSource", buffer0); //混合纹理输出
Graphics.Blit(source, destination, golMaterial, );
RenderTexture.ReleaseTemporary(buffer0);
}
}
else
{
Graphics.Blit(source, destination);
}
}
}

高斯模糊Shader

 Shader "Unlit/MyShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_BlurSize("Blur Size",Float) =1.0
_TargetColor ("_TargetColor", Color) = (,,,)
}
SubShader
{
CGINCLUDE #include "UnityCG.cginc" sampler2D _MainTex;
sampler2D _OutlineSource;
half4 _MainTex_TexelSize;
float4 _MainTex_ST;
float _BlurSize;
fixed4 _TargetColor;
float _HighlightFlicker=0.0f; struct v2f
{
float4 pos : SV_POSITION;
half2 uv[] : TEXCOORD0;
}; struct v2
{
float4 pos : SV_POSITION;
half2 uv: TEXCOORD0;
}; v2f vertBlurVertical (appdata_img v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex); half2 uv=v.texcoord;
o.uv[]=uv;
o.uv[]=uv + float2(0.0,_MainTex_TexelSize.y * 1.0) * _BlurSize;
o.uv[]=uv - float2(0.0,_MainTex_TexelSize.y * 1.0) * _BlurSize;
o.uv[]=uv + float2(0.0,_MainTex_TexelSize.y * 2.0) * _BlurSize;
o.uv[]=uv - float2(0.0,_MainTex_TexelSize.y * 2.0) * _BlurSize; return o;
} v2f vertBlurHorizontal(appdata_img v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex); half2 uv = v.texcoord; o.uv[] = uv;
o.uv[] = uv + float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
o.uv[] = uv - float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
o.uv[] = uv + float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
o.uv[] = uv - float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize; return o;
} //融合
v2 vertBlur(appdata_img v)
{
v2 o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
return o;
} fixed4 fragBlur(v2f i) : SV_Target
{
float weight[] = {0.4026, 0.2442, 0.0545}; fixed3 sum = tex2D(_MainTex, i.uv[]).rgb * weight[];
fixed4 originalPixel = tex2D(_MainTex, UnityStereoScreenSpaceUVAdjust(i.uv[], _MainTex_ST)); for (int it = ; it < ; it++) {
sum += tex2D(_MainTex, i.uv[it*-]).rgb * weight[it];
sum += tex2D(_MainTex, i.uv[it*]).rgb * weight[it];
} return fixed4(sum, 1.0);
} //融合
fixed4 frag(v2 i) : SV_Target
{
fixed3 sum = tex2D(_OutlineSource, i.uv).rgb;
fixed4 originalPixel = tex2D(_MainTex, UnityStereoScreenSpaceUVAdjust(i.uv, _MainTex_ST));
// sum = sum.r > 0.95 ? 0 : sum;
fixed temp = 1.0 - abs((sum.r - 0.5) / 0.5);
fixed4 target=(1.0- temp)*originalPixel+_TargetColor*(temp); //闪烁
if(_HighlightFlicker==1.0f)
target= (1.0- temp)*originalPixel+abs(sin(_Time.y * 1.5f))*_TargetColor*(temp);
else
target= (1.0- temp)*originalPixel+_TargetColor*(temp);
return target;
} ENDCG ZTest Always Cull Off ZWrite Off Pass
{
NAME "GAUSSIAN_BLUR_VERTICAL" CGPROGRAM #pragma vertex vertBlurVertical
#pragma fragment fragBlur ENDCG
} Pass
{
NAME "GAUSSIAN_BLUR_HORIZONTAL" CGPROGRAM #pragma vertex vertBlurHorizontal
#pragma fragment fragBlur ENDCG
} Pass
{
NAME "GAUSSIAN_BLUR" CGPROGRAM #pragma vertex vertBlur
#pragma fragment frag ENDCG
}
}
FallBack "Diffuse"
}

效果:

Unity中实现网格轮廓效果,选中边框效果的更多相关文章

  1. Unity中利用柏林噪声(perlinnoise)制作摇摆效果

    perlinnoise是unity中Mathf下的一个函数,需要两个float参数x和y进行采样,返回一个0-1的float型. 项目里经常要随机摇摆某些东西,比如摄像机,某个随机运动的目标等等,都可 ...

  2. 关于Unity中Mesh网格的详解

    3D模型 通过3D建模软件所建出来的点和面,如以三角形为主的点和面,比如人的脑袋一个球,就是由各种各样的三角形组成的点和面. 点和面以及纹理坐标都是通过3D建模软件建模出来的. Unity会帮我们把模 ...

  3. 关于Unity中蒙皮网格和布料的使用

    所以物体的要绘制出来就必须要有网格组件+材质属性,如果还需要其他特效或丰富内容的话,还可以再加组件. 蒙皮网格和布料 1: 例如要模拟衣服,随风摆动,模拟布料需要用到蒙皮网格和布料;2: 蒙皮网格可以 ...

  4. Unity中的网格与材质球合并

    http://blog.csdn.net/dardgen2015/article/details/51517860

  5. Unity中调用Windows窗口句柄以及根据需求设置并且解决扩展屏窗体显示错乱/位置错误的Bug

    问题背景: 现在在搞PC端应用开发,我们开发中需要调用系统的窗口以及需要最大化最小化,缩放窗口拖拽窗口,以及设置窗口位置,去边框等功能 解决根据: 使用user32.dll解决 具体功能: Unity ...

  6. 实现单选框点击label标记中的文字也能选中

    实例: <label for="man"> <input type="radio" value="男" name=&quo ...

  7. 关于Unity中网格导航与寻路

    寻路思路 1.烘焙出地形数据,导航数据,区分哪些是路径,哪些是障碍物 2.给要寻路的角色添加寻路的组件,加好了以后就会有速度和目的地之类的参数设置 3.只要设置好目的地,角色就会根据烘焙好的地图自己走 ...

  8. Unity中做放大镜 效果

    孙广东  2015.8.16 事实上和 小地图都几乎相同了. 还是要借助 还有一个相机 目的: 这篇文章的主要目的是  要给你一个想法  怎样做放大境效果 . 在unity中能够简单的实现放大镜效果啊 ...

  9. unity 中UGUI制作滚动条视图效果(按钮)

    1.在unity中创建一个Image作为滚动条视图的背景: 2.在Image下创建一个空物体,在空物体下创建unity自带的Scroll View组件: 3.对滑动条视图的子物体进行调整: 4.添加滚 ...

随机推荐

  1. springboot核心原理

    1.基于你对springboot的理解描述一下什么是springboot 它是一个服务于spring框架的框架,能够简化配置文件,快速构建web应用, 内置tomcat,无需打包部署,直接运行. 2. ...

  2. MOV EAX,DWORD PTR SS:[EBP+8]

    nasm来写可以写成mov eax,dword ptr [ebp + 8]理由:ebp和esp默认是ss段,所以根本不用显式说明.          eax,ebx,ecx,edx,edi,esi默认 ...

  3. 多线程实现奇偶统计v1 - 暴力版

    #include <stdio.h> #include <stdlib.h> #include <time.h> #include "pthread.h& ...

  4. C语言中各种进制的表示

    #include<stdio.h> int main() { //默认情况下是十进制 ; // 二进制(0b或者0B开头) int number2 = 0b1100; //八进制(0开头) ...

  5. sql server 事务隔离性 snapshot 、read committed说明

    一. --该 read committed 默认事务隔离级别 在 systemuser修改事务未完成时 select * from [SystemUser] where id=62; 该语句是不可读取 ...

  6. 微信小程序app.json文件常用全局配置

    小程序根目录下的 app.json 文件用来对微信小程序进行全局配置,决定页面文件的路径.窗口表现.设置网络超时时间.设置多 tab 等. JOSN文件不允许注释,下面为了学习加上注释,粘贴需要的片段 ...

  7. LeetCode Array Easy 122. Best Time to Buy and Sell Stock II

    Description Say you have an array for which the ith element is the price of a given stock on day i. ...

  8. java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用?

    有两种实现方法,分别是继承Thread类与实现Runnable接口用synchronized关键字修饰同步方法反对使用stop(),是因为它不安全.它会解除由线程获取的所有锁定,而且如果对象处于一种不 ...

  9. 【JZOJ3674】【luoguP4042】【BZOJ3875】骑士游戏

    description 在这个游戏中,JYY一共有两种攻击方式,一种是普通攻击,一种是法术攻击.两种攻击方式都会消耗JYY一些体力.采用普通攻击进攻怪兽并不能把怪兽彻底杀死,怪兽的尸体可以变出其他一些 ...

  10. delphi 内存映射

    使用内存映射文件读写大文件 使用内存映射文件读写大文件 文件操作是应用程序最为基本的功能之一,Win32 API和MFC均提供有支持文件处理的函数和类.一般来说,这些函数可以满足大多数场合的要求,但是 ...