您的位置:首页 > 移动开发 > Unity3D

【Unity ImageEffect】一个用于角色外边缘发光的效果

2016-01-14 10:17 615 查看

前言

现在很多游戏都有选中角色后高亮显示选中目标的需求,但是Unity是没有提供这样的功能(很不爽)。虽然在Unity Store上有几款很不错的插件能满足需求,但是往往别人的东西过于复杂或者不够定制化,有些时候还是不如自己开发的好。在网上能搜索出几篇零零散散的文章有说如何实现,再结合分析了一个插件的原理后,自己边写了一个外边缘发光的镜头效果。

方案

原理跟大众一致,分为如下步骤:

渲染一张需要外发光的角色模板纹理
把这张纹理进行斜上下左右偏移,存为另外一张纹理
将偏移纹理进行模糊处理
用模糊纹理跟模板纹理进行像素比对

实现

效果



Blur.shader代码
Shader "Yogi/ImageEffect/Blur"
{
Properties
{
_MainTex("Base (RGB)", 2D) = "white" {}
_Offset("Offset", Vector) = (0, 0, 0, 0)
}

SubShader
{
Pass
{
ZTest Always
ZWrite Off
Cull Off
Fog{ Mode Off }

CGPROGRAM
#include "UnityCG.cginc"
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest

sampler2D _MainTex;
fixed2 _Offset;

struct a2v
{
fixed4 vertex : POSITION;
fixed2 texcoord : TEXCOORD0;
};

struct v2f
{
fixed4 vertex : SV_POSITION;
fixed2 uv : TEXCOORD0;
fixed2 offsets[4] : TEXCOORD1;
};

v2f vert(a2v v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);

o.uv = v.texcoord.xy;
o.offsets[0] = fixed2(_Offset.x, _Offset.y);
o.offsets[1] = fixed2(-_Offset.x, _Offset.y);
o.offsets[2] = fixed2(_Offset.x, -_Offset.y);
o.offsets[3] = fixed2(-_Offset.x, -_Offset.y);

return o;
}

fixed4 frag(v2f i) : SV_Target
{
fixed4 o = tex2D(_MainTex, i.uv);
o += tex2D(_MainTex, i.uv + i.offsets[0]);
o += tex2D(_MainTex, i.uv + i.offsets[1]);
o += tex2D(_MainTex, i.uv + i.offsets[2]);
o += tex2D(_MainTex, i.uv + i.offsets[3]);
o *= 0.2f;

return o;
}

ENDCG
}
}

Fallback off
}


Outline.shader代码
Shader "Yogi/ImageEffect/Outline"
{
Properties
{
_MainTex("Base (RGB)", 2D) = "white" {}
_StencilMap("StencilMap (RGB)", 2D) = "white" {}
_BlurMap("BlurMap (RGB)", 2D) = "white" {}
_OutlineColor("Outline Color", Color) = (1, 1, 1, 1)
_Intensity("Intensity", Float) = 1
}

SubShader
{
Pass
{
ZTest Always
ZWrite Off
Cull Off
Fog{ Mode Off }

CGPROGRAM
#include "UnityCG.cginc"
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest

sampler2D _MainTex;
sampler2D _StencilMap;
sampler2D _BlurMap;
fixed3 _OutlineColor;
fixed _Intensity;

struct a2v
{
fixed4 vertex : POSITION;
fixed2 texcoord : TEXCOORD0;
};

struct v2f
{
fixed4 vertex : SV_POSITION;
fixed2 uv : TEXCOORD0;
};

v2f vert(a2v v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord.xy;

return o;
}

fixed4 frag(v2f i) : SV_Target
{
fixed4 c = tex2D(_MainTex, i.uv);
fixed4 s = tex2D(_StencilMap, i.uv);
fixed4 b = tex2D(_BlurMap, i.uv);

fixed4 o = b - s;
o.rgb = c.rgb + saturate(o.a) * _Intensity * _OutlineColor;
o.a = c.a;

return o;
}

ENDCG
}
}

Fallback off
}


OutlineEffect.cs代码
using UnityEngine;

[RequireComponent(typeof(Camera))]
[ExecuteInEditMode]
public class OutlineEffect : MonoBehaviour
{
private const string NODE = "Outline Camera";

[Range(0.1f, 1.0f)]
public float sample = 0.25f;
[Range(0.0f, 1.0f)]
public float spread = 0.5f;
[Range(0, 4)]
public int iteration = 2;
[Range(0.0f, 10.0f)]
public float intensity = 1.0f;
public Color outlineColor = Color.white;
public LayerMask cullingMask;

private Camera outlineCamera
{
get
{
if (null == m_OutlineCamera)
{
Transform node = transform.FindChild(NODE);
if (null == node)
{
node = new GameObject(NODE).transform;
node.parent = transform;
node.localPosition = Vector3.zero;
node.localRotation = Quaternion.identity;
node.localScale = Vector3.one;
}

m_OutlineCamera = node.GetComponent<Camera>();
if (null == m_OutlineCamera)
{
m_OutlineCamera = node.gameObject.AddComponent<Camera>();
}

m_OutlineCamera.enabled = false;
m_OutlineCamera.clearFlags = CameraClearFlags.SolidColor;
m_OutlineCamera.backgroundColor = new Color(0, 0, 0, 0);
m_OutlineCamera.renderingPath = RenderingPath.VertexLit;
m_OutlineCamera.hdr = false;
m_OutlineCamera.useOcclusionCulling = false;
m_OutlineCamera.gameObject.hideFlags = HideFlags.HideAndDontSave;
}

return m_OutlineCamera;
}
}
private Camera m_OutlineCamera;

private Material outlineMaterial
{
get
{
if (m_OutlineMaterial == null)
{
m_OutlineMaterial = new Material(Shader.Find("Yogi/ImageEffect/Outline"));
m_OutlineMaterial.hideFlags = HideFlags.HideAndDontSave;
}

return m_OutlineMaterial;
}
}
private Material m_OutlineMaterial = null;

private Material blurMaterial
{
get
{
if (m_BlurMaterial == null)
{
m_BlurMaterial = new Material(Shader.Find("Yogi/ImageEffect/Blur"));
m_BlurMaterial.hideFlags = HideFlags.HideAndDontSave;
}

return m_BlurMaterial;
}
}
private Material m_BlurMaterial = null;

private RenderTexture stencilMap;

private void OnPreRender()
{
stencilMap = RenderTexture.GetTemporary(Screen.width, Screen.height, 0);

outlineCamera.fieldOfView = camera.fieldOfView;
outlineCamera.isOrthoGraphic = camera.isOrthoGraphic;
outlineCamera.nearClipPlane = camera.nearClipPlane;
outlineCamera.farClipPlane = camera.farClipPlane;
outlineCamera.cullingMask = cullingMask;
outlineCamera.targetTexture = stencilMap;
outlineCamera.Render();
}

private void OnPostRender()
{
RenderTexture.ReleaseTemporary(stencilMap);
}

private void OnRenderImage(RenderTexture sourceTexture, RenderTexture destTexture)
{
int width = (int)(sourceTexture.width * sample);
int height = (int)(sourceTexture.height * sample);
RenderTexture blurMap = RenderTexture.GetTemporary(width, height, 0);
Graphics.Blit(stencilMap, blurMap);

for (int i = 0; i < iteration; ++i)
{
RenderTexture buffer = RenderTexture.GetTemporary(width, height, 0);
float offset = spread * i + 0.5f;
blurMaterial.SetVector("_Offset", new Vector4(offset / width, offset / height));
Graphics.Blit(blurMap, buffer, blurMaterial);
RenderTexture.ReleaseTemporary(blurMap);
blurMap = buffer;
}

outlineMaterial.SetTexture("_StencilMap", stencilMap);
outlineMaterial.SetTexture("_BlurMap", blurMap);
outlineMaterial.SetColor("_OutlineColor", outlineColor);
outlineMaterial.SetFloat("_Intensity", intensity);
Graphics.Blit(sourceTexture, destTexture, outlineMaterial);

RenderTexture.ReleaseTemporary(blurMap);
}

private void OnDisable()
{
OnDestroy();
}

private void OnDestroy()
{
if (null != m_OutlineCamera)
{
if (Application.isPlaying)
{
Destroy(m_OutlineCamera.gameObject);
}
else
{
DestroyImmediate(m_OutlineCamera.gameObject);
}
}
}
}


最后

高亮效果不是每时每刻都打开的,所以还需开发者自己根据逻辑去控制开关。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: