原连接:http://www.cnblogs.com/dosomething/archive/2012/08/04/2622488.html
根据上面链接,写出的代码。
轮廓描边是游戏中的细节 但是一个有特色的效果还是会让人眼前一亮
Glow + Outline 的效果就像求生之路2和暗黑3的轮廓描边界一样 对轮廓描边后再进行模糊处理
如图: 求生之路2
暗黑3
一种思路为:
1、在RTT中绘制单一像素
2、对1绘制后的RTT进行blur处理
3、对2处理后的RTT与原始场景进行叠加
4、绘制原始模型
另一种思路:
1、绘制原始模型到RTT
2、对1绘制的RTT中原始模型进行Sobel描边
3、对2描边后的RenderTexture进行blur处理
4、叠加1和3的RenderTexture
5、4与场景进行叠加
在unity3d中的实现
1、场景图像渲染之前
void OnPreRender
{
......
另外添加一个摄像机A
这里必须将GlowOutlineCamera摄像机关闭
并且清除标识为纯色
制定其渲染目标为RTT
清空其背景
摄像机A在主摄像机渲染之前通过Shader去绘制指定渲染类型的GameObject 也就是需要描边的GameObject
(即摄像机A.RenderWithShader(RTT, "XXXX");)
......
}
2、场景图像渲染之后
void OnRenderImage(RenderTexture source, RenderTexture destination)
{
......
将摄像机A所渲染的目标纹理进行blur处理
将blur后的RTT与场景图像渲染后的RTT进行合成叠加
拷贝到目的渲染纹理上
......
}
效果1 未被遮挡
效果2 被遮挡
效果3 描边相交 未被遮挡
效果4 描边相交 被遮挡
最后附上一张火炬之光2的轮廓描边效果
unity实现起来要简单的多 不属于postprocess
只需要对需要描边的物体写一个Rimlight边缘高光的shader即可
国内好像有个unity制作的网页游戏也是用的这种方式描边
一下就是代码:
首先是主相机中的代码:
using UnityEngine;
public class GlowEffect : MonoBehaviour {
public Material glowMaterial;
public Shader glowReplaceShader;
// Toggle between using the object's alpha channel for glow. This setting is good for mobile devices -
// it uses less memory and doesn't cause as many draw calls though it uses the object's alpha channel.
public bool useAlphaChannelForGlow = false;
// Toggle between using the simple glow effect. This setting is good for older mobile devices.
// sIt further reduces the amount of memory required and the number of draw calls.
public bool useSimpleGlow = false;
// The number of times the glow texture should be blurred. The more blur iterations the wider the glow. This value is only used if useSimpleGlow is false.
public int blurIterations = 4;
// The distance of the samples taken for the blurred glow. Too big of a value will cause noise in the blur. This value is only used if useSimpleGlow is false.
public float blurSpread = 1.0f;
// Multiplies the glow color by this value.
public float glowMultiplier = 1.2f;
// Multiplies the glow color by this color.
public Color glowColorMultiplier = Color.white;
[HideInInspector]
public RenderTexture postEffectsRenderTexture;
private RenderTexture cameraRenderTexture;
private Camera shaderCamera;
private RenderTexture replaceRenderTexture;
private RenderTexture blurA;
private RenderTexture blurB;
public void OnEnable()
{
if (!useAlphaChannelForGlow) {
replaceRenderTexture = new RenderTexture(Screen.width, Screen.height, 16, RenderTextureFormat.ARGB32);
replaceRenderTexture.wrapMode = TextureWrapMode.Clamp;
replaceRenderTexture.useMipMap = false;
replaceRenderTexture.isPowerOfTwo = true;
replaceRenderTexture.filterMode = FilterMode.Bilinear;
replaceRenderTexture.Create();
glowMaterial.SetTexture("_Glow", replaceRenderTexture);
shaderCamera = new GameObject("Glow Effect", typeof(Camera)).GetComponent<Camera>();
shaderCamera.gameObject.hideFlags = HideFlags.HideAndDontSave;
}
if (!useSimpleGlow) {
blurA = new RenderTexture(256, 256, 0, RenderTextureFormat.ARGB32);
blurA.wrapMode = TextureWrapMode.Clamp;
blurA.useMipMap = false;
blurA.filterMode = FilterMode.Bilinear;
blurA.Create();
blurB = new RenderTexture(256, 256, 0, RenderTextureFormat.ARGB32);
blurB.wrapMode = TextureWrapMode.Clamp;
blurB.useMipMap = false;
blurB.filterMode = FilterMode.Bilinear;
blurB.Create();
if (blurIterations % 2 == 0) {
glowMaterial.SetTexture("_Glow", blurA);
} else {
glowMaterial.SetTexture("_Glow", blurB);
}
}
#if !UNITY_EDITOR && (UNITY_IPHONE || UNITY_ANDROID)
// this will be used as the target texture so it can be blitted to another camera
cameraRenderTexture = new RenderTexture(Screen.width, Screen.height, 16, RenderTextureFormat.ARGB32);
cameraRenderTexture.wrapMode = TextureWrapMode.Clamp;
cameraRenderTexture.useMipMap = false;
cameraRenderTexture.isPowerOfTwo = false;
cameraRenderTexture.filterMode = FilterMode.Bilinear;
cameraRenderTexture.Create();
camera.targetTexture = cameraRenderTexture;
camera.depthTextureMode = DepthTextureMode.None;
// create a render texture to blit to the final camera
postEffectsRenderTexture = new RenderTexture(Screen.width, Screen.height, 16, RenderTextureFormat.ARGB32);
postEffectsRenderTexture.wrapMode = TextureWrapMode.Clamp;
postEffectsRenderTexture.useMipMap = false;
postEffectsRenderTexture.isPowerOfTwo = false;
postEffectsRenderTexture.filterMode = FilterMode.Bilinear;
postEffectsRenderTexture.Create();
#endif
glowMaterial.SetFloat("_BlurSpread", blurSpread);
glowMaterial.SetFloat("_GlowMultiplier", glowMultiplier);
glowMaterial.SetColor("_GlowColorMultiplier", glowColorMultiplier);
}
public void OnDisable()
{
glowMaterial.mainTexture = null;
GetComponent<Camera>().targetTexture = null;
DestroyObject(shaderCamera);
DestroyObject(blurA);
DestroyObject(blurB);
}
public void OnPreRender()
{
if (!useAlphaChannelForGlow) {
shaderCamera.CopyFrom(GetComponent<Camera>());
shaderCamera.backgroundColor = Color.clear;
shaderCamera.clearFlags = CameraClearFlags.SolidColor;
shaderCamera.renderingPath = RenderingPath.Forward;
shaderCamera.targetTexture = replaceRenderTexture;
shaderCamera.RenderWithShader(glowReplaceShader, "RenderType");
}
}
// UNITY_IPHONE and UNITY_ANDROID can't use OnRenderImage because the source and destination
// render texture are in RGB565 format and we need the alpha layer.
#if !UNITY_EDITOR && (UNITY_IPHONE || UNITY_ANDROID)
public void OnPostRender()
{
calculateGlow(cameraRenderTexture, postEffectsRenderTexture);
}
#else
public void OnRenderImage(RenderTexture source, RenderTexture destination)
{
calculateGlow(source, destination);
}
#endif
private void calculateGlow(RenderTexture source, RenderTexture destination)
{
if (!useSimpleGlow) {
// blur
if (useAlphaChannelForGlow) {
Graphics.Blit(source, blurB, glowMaterial, 2);
} else {
Graphics.Blit(replaceRenderTexture, blurB, glowMaterial, 1);
}
for (int i = 1; i < blurIterations; ++i) {
if (i % 2 == 0) {
Graphics.Blit(blurA, blurB, glowMaterial, 1);
} else {
Graphics.Blit(blurB, blurA, glowMaterial, 1);
}
}
// calculate glow
Graphics.Blit(source, destination, glowMaterial, 0);
} else {
Graphics.Blit(source, destination, glowMaterial, (useAlphaChannelForGlow ? 4 : 3));
}
}
}
接着就是Glow Material材质中的Shader代码:
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
// Main shader which computes the glow effect
Shader "Glow Effect/Glow Effect" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
}
SubShader {
Pass { // pass 0 - glow using glow texture
name "Glow"
ZTest Always Cull Off ZWrite Off
Fog { Mode Off }
Blend 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 sampler2D _Glow;
uniform float _GlowMultiplier;
uniform float4 _GlowColorMultiplier;
struct v2f {
half4 pos : SV_POSITION;
half2 uv : TEXCOORD0;
half2 uv1 : TEXCOORD1;
};
v2f vert (appdata_img v)
{
v2f o;
o.pos = UnityObjectToClipPos (v.vertex);
o.uv = v.texcoord;
o.uv1 = v.texcoord;
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0) {
o.uv1.y = 1 - o.uv1.y;
}
#endif
return o;
}
half4 frag (v2f i) : COLOR
{
half4 mainTex = tex2D(_MainTex, i.uv);
half4 glow = tex2D(_Glow, i.uv1) * _GlowMultiplier * _GlowColorMultiplier;
return mainTex + glow;
}
ENDCG
}
pass { // pass 1 - blur the main texture
name "SimpleBlur"
ZTest Always Cull Off ZWrite Off
Fog { Mode Off }
Blend 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 float _BlurSpread;
struct v2f {
half4 pos : SV_POSITION;
half2 uv : TEXCOORD0;
half2 uv2[4] : TEXCOORD1;
};
v2f vert (appdata_img v)
{
v2f o;
o.pos = UnityObjectToClipPos (v.vertex);
o.uv = v.texcoord;
o.uv2[0] = v.texcoord + _MainTex_TexelSize.xy * half2(_BlurSpread, _BlurSpread);
o.uv2[1] = v.texcoord + _MainTex_TexelSize.xy * half2(-_BlurSpread, -_BlurSpread);
o.uv2[2] = v.texcoord + _MainTex_TexelSize.xy * half2(_BlurSpread, -_BlurSpread);
o.uv2[3] = v.texcoord + _MainTex_TexelSize.xy * half2(-_BlurSpread, _BlurSpread);
return o;
}
half4 frag ( v2f i ) : COLOR
{
half4 blur = tex2D(_MainTex, i.uv ) * 0.4;
blur += tex2D(_MainTex, i.uv2[0]) * 0.15;
blur += tex2D(_MainTex, i.uv2[1]) * 0.15;
blur += tex2D(_MainTex, i.uv2[2]) * 0.15;
blur += tex2D(_MainTex, i.uv2[3]) * 0.15;
return blur;
}
ENDCG
}
pass { // pass 2 - blur the main texture and multiple the rgb channels by the alpha
name "BlurandAlphaMultRGB"
ZTest Always Cull Off ZWrite Off
Fog { Mode Off }
Blend 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 float _BlurSpread;
struct v2f {
half4 pos : SV_POSITION;
half2 uv : TEXCOORD0;
half2 uv2[4] : TEXCOORD1;
};
v2f vert (appdata_img v)
{
v2f o;
o.pos = UnityObjectToClipPos (v.vertex);
o.uv = v.texcoord;
o.uv2[0] = v.texcoord + _MainTex_TexelSize.xy * half2(_BlurSpread, _BlurSpread);
o.uv2[1] = v.texcoord + _MainTex_TexelSize.xy * half2(-_BlurSpread, -_BlurSpread);
o.uv2[2] = v.texcoord + _MainTex_TexelSize.xy * half2(_BlurSpread, -_BlurSpread);
o.uv2[3] = v.texcoord + _MainTex_TexelSize.xy * half2(-_BlurSpread, _BlurSpread);
return o;
}
half4 frag ( v2f i ) : COLOR
{
half4 mainTex = tex2D(_MainTex, i.uv);
mainTex *= mainTex.a * 0.4f;
half4 blur = mainTex;
mainTex = tex2D(_MainTex, i.uv2[0]);
mainTex *= mainTex.a * 0.15;
blur += mainTex;
mainTex = tex2D(_MainTex, i.uv2[1]);
mainTex *= mainTex.a * 0.15;
blur += mainTex;
mainTex = tex2D(_MainTex, i.uv2[2]);
mainTex *= mainTex.a * 0.15;
blur += mainTex;
mainTex = tex2D(_MainTex, i.uv2[3]);
mainTex *= mainTex.a * 0.15;
blur += mainTex;
return blur;
}
ENDCG
}
pass { // pass 3 - blur the glow texture and apply that glow to the main texture
name "SimpleBlurandGlow"
ZTest Always Cull Off ZWrite Off
Fog { Mode Off }
Blend 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 sampler2D _Glow;
uniform half4 _Glow_TexelSize;
uniform float _GlowMultiplier;
uniform float4 _GlowColorMultiplier;
struct v2f {
half4 pos : SV_POSITION;
half2 uv : TEXCOORD0;
half2 uv1 : TEXCOORD1;
half2 uv2[4] : TEXCOORD2;
};
v2f vert (appdata_img v)
{
v2f o;
o.pos = UnityObjectToClipPos (v.vertex);
o.uv = v.texcoord;
o.uv1 = v.texcoord;
o.uv2[0] = v.texcoord + _Glow_TexelSize.xy * half2(2.5,2.5);
o.uv2[1] = v.texcoord + _Glow_TexelSize.xy * half2(-2.5,-2.5);
o.uv2[2] = v.texcoord + _Glow_TexelSize.xy * half2(2.5,-2.5);
o.uv2[3] = v.texcoord + _Glow_TexelSize.xy * half2(-2.5,2.5);
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0) {
o.uv1.y = 1 - o.uv1.y;
o.uv2[0].y = 1 - o.uv2[0].y;
o.uv2[1].y = 1 - o.uv2[1].y;
o.uv2[2].y = 1 - o.uv2[2].y;
o.uv2[3].y = 1 - o.uv2[3].y;
}
#endif
return o;
}
half4 frag ( v2f i ) : COLOR
{
half4 blur = tex2D(_Glow, i.uv1 ) * 0.3;
blur += tex2D(_Glow, i.uv2[0]) * 0.175;
blur += tex2D(_Glow, i.uv2[1]) * 0.175;
blur += tex2D(_Glow, i.uv2[2]) * 0.175;
blur += tex2D(_Glow, i.uv2[3]) * 0.175;
blur *= _GlowMultiplier * _GlowColorMultiplier;
return tex2D(_MainTex, i.uv) + blur;
}
ENDCG
}
Pass { // pass 4 - apply a glow to the blurred main texture based on the alpha channel of the glow
name "SimpleBlurGlowFromAlpha"
ZTest Always Cull Off ZWrite Off
Fog { Mode Off }
Blend 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 float _GlowMultiplier;
uniform float4 _GlowColorMultiplier;
struct v2f {
half4 pos : SV_POSITION;
half2 uv : TEXCOORD0;
half2 uv2[4] : TEXCOORD1;
};
v2f vert (appdata_img v)
{
v2f o;
o.pos = UnityObjectToClipPos (v.vertex);
o.uv = v.texcoord;
o.uv2[0] = v.texcoord + _MainTex_TexelSize.xy * half2(2.5,2.5);
o.uv2[1] = v.texcoord + _MainTex_TexelSize.xy * half2(-2.5,-2.5);
o.uv2[2] = v.texcoord + _MainTex_TexelSize.xy * half2(2.5,-2.5);
o.uv2[3] = v.texcoord + _MainTex_TexelSize.xy * half2(-2.5,2.5);
return o;
}
half4 frag ( v2f i ) : COLOR
{
half4 blur = tex2D(_MainTex, i.uv ) * 0.3;
blur += tex2D(_MainTex, i.uv2[0]) * 0.175;
blur += tex2D(_MainTex, i.uv2[1]) * 0.175;
blur += tex2D(_MainTex, i.uv2[2]) * 0.175;
blur += tex2D(_MainTex, i.uv2[3]) * 0.175;
blur *= _GlowMultiplier * _GlowColorMultiplier;
return tex2D(_MainTex, i.uv) + blur * blur.a;
}
ENDCG
}
}
Fallback Off
}
然后就是glow Replace Shader代码(该部分代码主要用于勾画需要glow对象的轮廓):
Shader "Hidden/Glow Effect/Glow Replace" {
SubShader
{
Tags { "RenderType" = "Glow" }
Pass {
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
uniform sampler2D _GlowTex;
uniform float4 _GlowColorMult;
half4 frag(v2f_img i) : COLOR
{
return tex2D(_GlowTex,i.uv) * _GlowColorMult;
}
ENDCG
}
}
SubShader {
Tags { "RenderType" = "Opaque" }
Pass {
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
half4 frag(v2f_img i) : COLOR
{
return half4(0,0,0,0);
}
ENDCG
}
}
SubShader {
Tags { "RenderType" = "Transparent" }
Pass {
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
half4 frag(v2f_img i) : COLOR
{
return half4(0,0,0,0);
}
ENDCG
}
}
SubShader
{
Tags { "RenderType" = "GlowTransparent" }
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Pass {
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
uniform sampler2D _GlowTex;
uniform sampler2D _MainTex;
uniform float4 _GlowColorMult;
half4 frag(v2f_img i) : COLOR
{
return half4((tex2D(_GlowTex,i.uv).rgb * _GlowColorMult) + .0001, tex2D(_MainTex,i.uv).a);
}
ENDCG
}
}
SubShader {
Tags { "RenderType" = "TransparentCutout" }
Pass {
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
half4 frag(v2f_img i) : COLOR
{
return half4(0,0,0,0);
}
ENDCG
}
}
SubShader {
Tags { "RenderType" = "Background" }
Pass {
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
half4 frag(v2f_img i) : COLOR
{
return half4(0,0,0,0);
}
ENDCG
}
}
SubShader {
Tags { "RenderType" = "Overlay" }
Pass {
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
half4 frag(v2f_img i) : COLOR
{
return half4(0,0,0,0);
}
ENDCG
}
}
SubShader {
Tags { "RenderType" = "TreeOpaque" }
Pass {
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
half4 frag(v2f_img i) : COLOR
{
return half4(0,0,0,0);
}
ENDCG
}
}
SubShader {
Tags { "RenderType" = "TreeTransparentCutout" }
Pass {
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
half4 frag(v2f_img i) : COLOR
{
return half4(0,0,0,0);
}
ENDCG
}
}
SubShader {
Tags { "RenderType" = "TreeBillboard" }
Pass {
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
half4 frag(v2f_img i) : COLOR
{
return half4(0,0,0,0);
}
ENDCG
}
}
SubShader {
Tags { "RenderType" = "Grass" }
Pass {
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
half4 frag(v2f_img i) : COLOR
{
return half4(0,0,0,0);
}
ENDCG
}
}
SubShader {
Tags { "RenderType" = "GrassBillboard" }
Pass {
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
half4 frag(v2f_img i) : COLOR
{
return half4(0,0,0,0);
}
ENDCG
}
}
Fallback Off
}
最后的就是Golw对象本身的Shader代码:
// A simple unlit shader. _GlowTex is not used in this shader but it is used by the replacement shader.
Shader "Glow Effect/Glow"
{
Properties
{
_MainTex ("Main Texture", 2D) = "white" {}
_GlowTex ("Glow Texture", 2D) = "white" {}
_GlowColorMult ("Glow Color Multiplier", Color) = (1, 1, 1, 1)
}
SubShader
{
Tags { "RenderType" = "Glow" "Queue" = "Geometry" }
Pass {
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
uniform sampler2D _MainTex;
half4 frag(v2f_img i) : COLOR
{
return tex2D(_MainTex,i.uv);
}
ENDCG
}
}
Fallback "Diffuse"
}
结束。实现之后呈现的效果是这样的: