The exchange rate for a Kidney is $42,000. So yes, I will accept that ;D
It's pretty complex, but here you go:
OutlineShader.shader
/*
// Copyright (c) 2015 José Guerreiro. All rights reserved.
//
// MIT license, see http://www.opensource.org/licenses/mit-license.php
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
*/
Shader "Hidden/OutlineEffect"
{
Properties
{
_MainTex ("Base (RGB)", 2D) = "white" {}
_LineColor ("Line Color", Color) = (1,1,1,.5)
}
SubShader
{
Pass
{
Tags { "RenderType"="Opaque" }
LOD 200
ZTest Always
ZWrite Off
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#include "UnityCG.cginc"
sampler2D _MainTex;
sampler2D _OutlineSource;
struct v2f {
float4 position : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert(appdata_img v)
{
v2f o;
o.position = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord;
return o;
}
float _LineThicknessX;
float _LineThicknessY;
float _LineIntensity;
half4 _LineColor1;
half4 _LineColor2;
half4 _LineColor3;
int _FlipY;
half4 frag (v2f input) : COLOR
{
float2 uv = input.uv;
if (_FlipY == 1)
uv.y = 1 - uv.y;
half4 originalPixel = tex2D(_MainTex,input.uv);
half4 outlineSource = tex2D(_OutlineSource, uv);
float h = .95f;
half4 outline = 0;
half4 sample1 = tex2D(_OutlineSource, uv + float2(_LineThicknessX,0.0));
half4 sample2 = tex2D(_OutlineSource, uv + float2(-_LineThicknessX,0.0));
half4 sample3 = tex2D(_OutlineSource, uv + float2(.0,_LineThicknessY));
half4 sample4 = tex2D(_OutlineSource, uv + float2(.0,-_LineThicknessY));
if(outlineSource.a < h)
{
if(sample1.r > h || sample2.r > h || sample3.r > h || sample4.r > h)
outline = _LineColor1 * _LineIntensity;
else if(sample1.g > h || sample2.g > h || sample3.g > h || sample4.g > h)
outline = _LineColor2 * _LineIntensity;
else if(sample1.b > h || sample2.b > h || sample3.b > h || sample4.b > h)
outline = _LineColor3 * _LineIntensity;
}
//return outlineSource;
return originalPixel + outline;
}
ENDCG
}
}
FallBack "Diffuse"
}
OutlineBufferShader.shader
/*
// Copyright (c) 2015 José Guerreiro. All rights reserved.
//
// MIT license, see http://www.opensource.org/licenses/mit-license.php
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
*/
Shader "Hidden/OutlineBufferEffect" {
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
[MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
Cull Off
Lighting Off
ZWrite Off
Blend One OneMinusSrcAlpha
CGPROGRAM
#pragma surface surf Lambert vertex:vert nofog keepalpha
#pragma multi_compile _ PIXELSNAP_ON
sampler2D _MainTex;
fixed4 _Color;
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;
float alpha = c.a * 99999999;
o.Albedo = _Color * alpha;
o.Alpha = alpha;
}
ENDCG
}
Fallback "Transparent/VertexLit"
}
OutlineEffect.cs
You will need to tweak this to suit your project.
/*
// Copyright (c) 2015 José Guerreiro. All rights reserved.
//
// MIT license, see http://www.opensource.org/licenses/mit-license.php
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
*/
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Spine.Unity;
[RequireComponent(typeof(Camera))]
public class OutlineEffect : MonoBehaviour
{
public List<SkeletonRenderer> spineCharacters = new List<SkeletonRenderer>();
public List<Renderer> outlineRenderers = new List<Renderer>();
public List<int> outlineRendererColors = new List<int>();
public List<Renderer> eraseRenderers = new List<Renderer>();
public float lineThickness = 4f;
public float lineIntensity = .5f;
public Color lineColor1 = Color.red;
public Color lineColor2 = Color.green;
public Color lineColor3 = Color.blue;
public bool flipY = false;
private Material outline1Material;
private Material outline2Material;
private Material outline3Material;
private Material outlineEraseMaterial;
private Shader outlineShader;
private Shader outlineBufferShader;
private Shader outlineBufferShaderSpine;
private Material outlineShaderMaterial;
private RenderTexture renderTexture;
private Camera _camera;
Dictionary<Material, Material> materialMap = new Dictionary<Material, Material>();
List<Material[]> originalMaterials = new List<Material[]>();
int[] originalLayers = new int[1];
Material[] originalEraseMaterials = new Material[1];
int[] originalEraseLayers = new int[1];
void OnEnable()
{
CreateMaterialsIfNeeded();
}
void OnDisable()
{
DestroyMaterials();
if( _camera)
{
DestroyImmediate( _camera.gameObject);
_camera = null;
}
}
Material GetMaterialFromID(int ID)
{
if (ID == 0)
return outline1Material;
else if (ID == 1)
return outline2Material;
else
return outline3Material;
}
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", 0);
m.DisableKeyword("_ALPHATEST_ON");
m.EnableKeyword("_ALPHABLEND_ON");
m.DisableKeyword("_ALPHAPREMULTIPLY_ON");
m.renderQueue = 3000;
return m;
}
void Start ()
{
spineCharacters = new List<SkeletonRenderer>(Object.FindObjectsOfType<SkeletonRenderer>());
if (spineCharacters != null && spineCharacters.Count != 0)
{
foreach ( SkeletonRenderer sr in spineCharacters )
{
outlineRenderers.Add(sr.GetComponent<MeshRenderer>());
/* Good vs Evil
BaseUnit b = sr.GetComponentInParent<BaseUnit>();
if ( b[PS.Faction] == Faction.Player)
outlineRendererColors.Add(0);
else
outlineRendererColors.Add(1);
*/
}
}
CreateMaterialsIfNeeded();
}
void OnPreCull()
{
if (outlineRenderers != null && outlineRenderers.Count != 0)
{
Camera camera = GetComponent<Camera>();
int width = camera.pixelWidth;
int height = camera.pixelHeight;
renderTexture = RenderTexture.GetTemporary(width, height, 16, RenderTextureFormat.Default);
if (_camera == null)
{
GameObject cameraGameObject = new GameObject("OutlineCamera");
cameraGameObject.hideFlags = HideFlags.HideAndDontSave;
_camera = cameraGameObject.AddComponent<Camera>();
}
_camera.CopyFrom(camera);
_camera.renderingPath = RenderingPath.Forward;
_camera.enabled = false;
_camera.backgroundColor = new Color(0.0f, 0.0f, 0.0f, 0.0f);
_camera.clearFlags = CameraClearFlags.SolidColor;
_camera.cullingMask = LayerMask.GetMask("Outline");
if (outlineRenderers != null)
{
originalLayers = new int[outlineRenderers.Count];
for (int i = 0; i < outlineRenderers.Count; i++)
{
if (outlineRenderers[i] != null)
{
if ( i >= originalMaterials.Count )
originalMaterials.Add(outlineRenderers[i].sharedMaterials);
else
originalMaterials[i] = outlineRenderers[i].sharedMaterials;
originalLayers[i] = outlineRenderers[i].gameObject.layer;
Material[] newMats = outlineRenderers[i].sharedMaterials;
for (int j = 0; j < originalMaterials[i].Length; j++)
{
Material subMaterial = null;
if ( materialMap.TryGetValue(originalMaterials[i][j], out subMaterial) )
{
newMats[j] = subMaterial;
}
else
{
Debug.Log("Lol, you need to make a new texture~");
}
}
outlineRenderers[i].sharedMaterials = newMats;
outlineRenderers[i].gameObject.layer = LayerMask.NameToLayer("Outline");
}
}
}
if (eraseRenderers != null)
{
originalEraseMaterials = new Material[eraseRenderers.Count];
originalEraseLayers = new int[eraseRenderers.Count];
for (int i = 0; i < eraseRenderers.Count; i++)
{
if (eraseRenderers[i] != null)
{
originalEraseMaterials[i] = eraseRenderers[i].sharedMaterial;
originalEraseLayers[i] = eraseRenderers[i].gameObject.layer;
eraseRenderers[i].sharedMaterial = outlineEraseMaterial;
eraseRenderers[i].gameObject.layer = LayerMask.NameToLayer("Outline");
}
}
}
_camera.targetTexture = renderTexture;
_camera.Render();
if (outlineRenderers != null)
{
for (int i = 0; i < outlineRenderers.Count; i++)
{
if (outlineRenderers[i] != null)
{
outlineRenderers[i].sharedMaterials = originalMaterials[i];
outlineRenderers[i].gameObject.layer = originalLayers[i];
}
}
}
if (eraseRenderers != null)
{
for (int i = 0; i < eraseRenderers.Count; i++)
{
if (eraseRenderers[i] != null)
{
eraseRenderers[i].sharedMaterial = originalEraseMaterials[i];
eraseRenderers[i].gameObject.layer = originalEraseLayers[i];
}
}
}
}
}
void OnRenderImage( RenderTexture source, RenderTexture destination)
{
CreateMaterialsIfNeeded();
UpdateMaterialsPublicProperties();
outlineShaderMaterial.SetTexture("_OutlineSource", renderTexture);
Graphics.Blit(source, destination, outlineShaderMaterial);
RenderTexture.ReleaseTemporary(renderTexture);
}
private void CreateMaterialsIfNeeded()
{
if(outlineShader == null)
outlineShader = Resources.Load<Shader>("OutlineEffect/OutlineShader");
if (outlineBufferShader == null)
outlineBufferShader = Resources.Load<Shader>("OutlineEffect/OutlineBufferShader");
if(outlineShaderMaterial == null)
{
outlineShaderMaterial = new Material(outlineShader);
outlineShaderMaterial.hideFlags = HideFlags.HideAndDontSave;
UpdateMaterialsPublicProperties();
}
if(outlineEraseMaterial == null)
outlineEraseMaterial = CreateMaterial(new Color(0, 0, 0, 0));
if(outline1Material == null)
outline1Material = CreateMaterial(new Color(1, 0, 0, 0));
if(outline2Material == null)
outline2Material = CreateMaterial(new Color(0, 1, 0, 0));
if (outline3Material == null)
outline3Material = CreateMaterial(new Color(0, 0, 1, 0));
if (spineCharacters != null && spineCharacters.Count != 0)
{
SkeletonRenderer sr = spineCharacters[0];
var mr = sr.GetComponent<MeshRenderer>();
for( int i = 0; i < mr.sharedMaterials.Length; i++ )
{
var mat = mr.sharedMaterials[i];
Material newMat = null;
if (outlineRendererColors != null && outlineRendererColors.Contains(0))
newMat = new Material(GetMaterialFromID(outlineRendererColors[0]));
else
newMat = new Material(outline1Material);
#ifdef UNITY_4
var texture = mat.GetTexture(0);
newMat.SetTexture(0,texture);
materialMap[mat] = newMat;
#endif
#ifdef UNITY_5
newMat.mainTexture = original.mainTexture;
#endif
}
}
}
private void DestroyMaterials()
{
DestroyImmediate(outlineShaderMaterial);
DestroyImmediate(outlineEraseMaterial);
DestroyImmediate(outline1Material);
DestroyImmediate(outline2Material);
DestroyImmediate(outline3Material);
outlineShader = null;
outlineBufferShader = null;
outlineShaderMaterial = null;
outlineEraseMaterial = null;
outline1Material = null;
outline2Material = null;
outline3Material = null;
}
private void UpdateMaterialsPublicProperties()
{
if(outlineShaderMaterial)
{
outlineShaderMaterial.SetFloat("_LineThicknessX", lineThickness / 1000);
outlineShaderMaterial.SetFloat("_LineThicknessY", (lineThickness * 2) / 1000);
outlineShaderMaterial.SetFloat("_LineIntensity", lineIntensity);
outlineShaderMaterial.SetColor("_LineColor1", lineColor1);
outlineShaderMaterial.SetColor("_LineColor2", lineColor2);
outlineShaderMaterial.SetColor("_LineColor3", lineColor3);
if(flipY)
outlineShaderMaterial.SetInt("_FlipY", 1);
else
outlineShaderMaterial.SetInt("_FlipY", 0);
}
}
}
Attach the MonoBehaviour to your main camera, create a new Layer called "Outline".
Then set your camera to HDR mode if you want it to glow instead of just outline. Maybe some bloom if you feel like it.
22 Oct 2015, 21:07
If you make some improvements to this shader, please share with us 🙂