Unity中实现网格轮廓效果,选中边框效果
问题背景:
最近要实现选中实体的高亮效果,要那种类似于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中实现网格轮廓效果,选中边框效果的更多相关文章
- Unity中利用柏林噪声(perlinnoise)制作摇摆效果
perlinnoise是unity中Mathf下的一个函数,需要两个float参数x和y进行采样,返回一个0-1的float型. 项目里经常要随机摇摆某些东西,比如摄像机,某个随机运动的目标等等,都可 ...
- 关于Unity中Mesh网格的详解
3D模型 通过3D建模软件所建出来的点和面,如以三角形为主的点和面,比如人的脑袋一个球,就是由各种各样的三角形组成的点和面. 点和面以及纹理坐标都是通过3D建模软件建模出来的. Unity会帮我们把模 ...
- 关于Unity中蒙皮网格和布料的使用
所以物体的要绘制出来就必须要有网格组件+材质属性,如果还需要其他特效或丰富内容的话,还可以再加组件. 蒙皮网格和布料 1: 例如要模拟衣服,随风摆动,模拟布料需要用到蒙皮网格和布料;2: 蒙皮网格可以 ...
- Unity中的网格与材质球合并
http://blog.csdn.net/dardgen2015/article/details/51517860
- Unity中调用Windows窗口句柄以及根据需求设置并且解决扩展屏窗体显示错乱/位置错误的Bug
问题背景: 现在在搞PC端应用开发,我们开发中需要调用系统的窗口以及需要最大化最小化,缩放窗口拖拽窗口,以及设置窗口位置,去边框等功能 解决根据: 使用user32.dll解决 具体功能: Unity ...
- 实现单选框点击label标记中的文字也能选中
实例: <label for="man"> <input type="radio" value="男" name=&quo ...
- 关于Unity中网格导航与寻路
寻路思路 1.烘焙出地形数据,导航数据,区分哪些是路径,哪些是障碍物 2.给要寻路的角色添加寻路的组件,加好了以后就会有速度和目的地之类的参数设置 3.只要设置好目的地,角色就会根据烘焙好的地图自己走 ...
- Unity中做放大镜 效果
孙广东 2015.8.16 事实上和 小地图都几乎相同了. 还是要借助 还有一个相机 目的: 这篇文章的主要目的是 要给你一个想法 怎样做放大境效果 . 在unity中能够简单的实现放大镜效果啊 ...
- unity 中UGUI制作滚动条视图效果(按钮)
1.在unity中创建一个Image作为滚动条视图的背景: 2.在Image下创建一个空物体,在空物体下创建unity自带的Scroll View组件: 3.对滑动条视图的子物体进行调整: 4.添加滚 ...
随机推荐
- springboot核心原理
1.基于你对springboot的理解描述一下什么是springboot 它是一个服务于spring框架的框架,能够简化配置文件,快速构建web应用, 内置tomcat,无需打包部署,直接运行. 2. ...
- MOV EAX,DWORD PTR SS:[EBP+8]
nasm来写可以写成mov eax,dword ptr [ebp + 8]理由:ebp和esp默认是ss段,所以根本不用显式说明. eax,ebx,ecx,edx,edi,esi默认 ...
- 多线程实现奇偶统计v1 - 暴力版
#include <stdio.h> #include <stdlib.h> #include <time.h> #include "pthread.h& ...
- C语言中各种进制的表示
#include<stdio.h> int main() { //默认情况下是十进制 ; // 二进制(0b或者0B开头) int number2 = 0b1100; //八进制(0开头) ...
- sql server 事务隔离性 snapshot 、read committed说明
一. --该 read committed 默认事务隔离级别 在 systemuser修改事务未完成时 select * from [SystemUser] where id=62; 该语句是不可读取 ...
- 微信小程序app.json文件常用全局配置
小程序根目录下的 app.json 文件用来对微信小程序进行全局配置,决定页面文件的路径.窗口表现.设置网络超时时间.设置多 tab 等. JOSN文件不允许注释,下面为了学习加上注释,粘贴需要的片段 ...
- 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. ...
- java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用?
有两种实现方法,分别是继承Thread类与实现Runnable接口用synchronized关键字修饰同步方法反对使用stop(),是因为它不安全.它会解除由线程获取的所有锁定,而且如果对象处于一种不 ...
- 【JZOJ3674】【luoguP4042】【BZOJ3875】骑士游戏
description 在这个游戏中,JYY一共有两种攻击方式,一种是普通攻击,一种是法术攻击.两种攻击方式都会消耗JYY一些体力.采用普通攻击进攻怪兽并不能把怪兽彻底杀死,怪兽的尸体可以变出其他一些 ...
- delphi 内存映射
使用内存映射文件读写大文件 使用内存映射文件读写大文件 文件操作是应用程序最为基本的功能之一,Win32 API和MFC均提供有支持文件处理的函数和类.一般来说,这些函数可以满足大多数场合的要求,但是 ...