这里总结了几种在unity实现描边效果的方法,首先准备一个模型导入在unity中,使用默认shader,上传一张原始图,以便后面实现功能效果的对比

一、边缘光,这里参照官方的一个SurfaceShader Example,Rim Lighting

1.在unity创建一个SurfaceShader,命名RimLighting

Shader "Custom/RimLighting" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
//边缘光颜色
_RimColor("Rim Color",Color) =(1,1,1,1)
//边缘光强度
_RimPower("Rim Power", Range(0.5,8.0)) = 3.0
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200 CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows // Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0 sampler2D _MainTex; struct Input {
float2 uv_MainTex;
//法线
float3 worldNormal;
//视角方向
float3 viewDir;
}; fixed4 _Color;
fixed4 _RimColor;
half _RimPower; void surf (Input IN, inout SurfaceOutputStandard o) {
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Alpha = c.a; half rim = 1.0 - saturate(dot(normalize(IN.viewDir), IN.worldNormal));
o.Emission = _RimColor.rgb * pow(rim, _RimPower);
}
ENDCG
}
FallBack "Diffuse"
}

2.将模型材质的shader改为刚才所写的shader,Custom/RimLighting

3.更改后,具体效果如下

二、法线外拓,用一个Pass渲染边框,一个Pass渲染实物

  1. 创建一个UnlitShader,命名为NormalUnlitShader

Shader "Custom/NormalUnlitShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Outline("Out Line",Range(0.001,0.005))=0.002
_Color("Color",Color)=(1,1,1,1)
} CGINCLUDE
#include "UnityCG.cginc"
struct v2f
{
float4 pos:POSITION;
float4 color:COLOR;
}; sampler2D _MainTex;
float _Outline;
fixed4 _Color; v2f vert(appdata_base v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
float3 norm = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);
float2 offset = TransformViewToProjection(norm.xy);
o.pos.xy += offset * o.pos.z * _Outline;
o.color = _Color;
return o;
}
ENDCG SubShader
{
Cull Front
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
fixed4 frag (v2f i) : COLOR
{
return i.color;
}
ENDCG
} CGPROGRAM
#pragma surface surf Lambert  
struct Input {
float2 uv_MainTex;
};
void surf(Input IN, inout SurfaceOutput o) {
fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
}

2.这里再换成新建的NormalUnlitShader,就会发现一些问题,他会在一些我们并不像描边的地方也改变了颜色,这就是因为这根据模型法线并不是全部都均匀的向外扩展,才导致这样的情况

3.再换一个Sphere模型应用与怪物同一个材质球,就会发现Sphere模型,是能达到我们的需求的,也能很明显的看出两者的差别,因为球的法线都是均匀的往外扩展的,这个方法的使用就需要以后根据实际的要求来使用

三、屏幕特效,描边效果

  1. 新建一个辅助摄像机,设置参数如下,并将模型的Layer设置为Monster,这样辅助摄像机就能单独看见这个怪物模型

2.写一个纯色shader,辅助摄像机用RenderWithShader,纯色渲染一张纹理处理

Shader "Custom/UnlitSolidColor"
{
SubShader
{
Pass
{
//返回蓝色
Color(0,0,1,1)
}
}
}

3.将纯色纹理,可以模糊放大几次,次数越多,边框就越宽,这里需要使用到一个像素偏移函数,Graphics.BlitMultiTap和一个Blur效果shader

Shader "Custom/OutterLineBlur" {

Properties {
_MainTex ("", 2D) = "white" {}
} Category {
ZTest Always Cull Off ZWrite Off Fog { Mode Off } Subshader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest #include "UnityCG.cginc" struct v2f {
float4 pos : POSITION;
half4 uv[2] : TEXCOORD0;
}; float4 _MainTex_TexelSize;
float4 _BlurOffsets; v2f vert (appdata_img v)
{
v2f o;
float offX = _MainTex_TexelSize.x * _BlurOffsets.x;
float offY = _MainTex_TexelSize.y * _BlurOffsets.y; o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
float2 uv = MultiplyUV (UNITY_MATRIX_TEXTURE0, v.texcoord.xy-float2(offX, offY)); o.uv[0].xy = uv + float2( offX, offY);
o.uv[0].zw = uv + float2(-offX, offY);
o.uv[1].xy = uv + float2( offX,-offY);
o.uv[1].zw = uv + float2(-offX,-offY);
return o;
} sampler2D _MainTex;
fixed4 _Color; fixed4 frag( v2f i ) : COLOR
{
fixed4 c;
c  = tex2D( _MainTex, i.uv[0].xy );
c += tex2D( _MainTex, i.uv[0].zw );
c += tex2D( _MainTex, i.uv[1].xy );
c += tex2D( _MainTex, i.uv[1].zw );
return c /2 ;
}
ENDCG
}
}
}
Fallback off
}

4.将扩大后的纹理与原来的纹理,做一个对比,并依据原来纹理剔除掉中间部分,就只剩下一个边框纹理,这里需要使用一个剔除shader

Shader "Custom/OutterLineCutoff" {
Properties {
_MainTex ("", 2D) = "white" {}
}
Category {
BlendOp RevSub
Blend One One
ZTest Always Cull Off ZWrite Off Fog { Mode Off } Subshader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest  sampler2D _MainTex;
sampler2D _MainTex1;
struct appdata
{
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
};
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert (appdata v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord.xy;
return o;
}
half4 frag(v2f i) : COLOR
{
fixed4 c =  tex2D(_MainTex, i.uv);
return c;
}
ENDCG
}
}
}
FallBack "Diffuse"
}

5.在主摄像机上,使用OnRenderImage函数,将得到的轮廓纯色纹理与摄像机的图像使用混合shader进行混合

Shader "Custom/OutterLineComposer" {

Properties {
_MainTex ("", 2D) = "white" {}
} Category {
ZTest Always Cull Off ZWrite Off Fog { Mode Off }
Blend SrcAlpha OneMinusSrcAlpha Subshader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest #include "UnityCG.cginc" struct v2f {
float4 pos : POSITION;
half2 uv : TEXCOORD0;
}; v2f vert (appdata_img v)
{
v2f o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord.xy;
return o;
} sampler2D _MainTex; fixed4 frag( v2f i ) : COLOR
{
return tex2D( _MainTex, i.uv );
}
ENDCG
}
}
}
Fallback off
}

6.绑定在主摄像机的脚本

using UnityEngine;
using System.Collections; [ExecuteInEditMode]
public class outline : MonoBehaviour {
    /// <summary>
    /// 辅助摄像机
    /// </summary>
    public Camera outlineCamera;     #region 纯红色材质 solidColorMaterail
    public Shader solidColorShader;
    private Material m_solid=null;
    private Material solidMaterail
    {
        get 
        {
            if (m_solid == null)
            {
                m_solid = new Material(solidColorShader);
            }
            return m_solid;
        }
    }
    #endregion
   
    #region 合并材质 compositeMaterial
    public Shader compositeShader;
    private Material m_composite=null;
    private Material compositeMaterial
    {
        get
        {
            if (m_composite == null)
                m_composite = new Material(compositeShader);
            return m_composite;
        }
    }
    #endregion
    
    #region 模糊材质 blurMaterial
    public Shader blurShader;
    private Material m_blur=null;
    private Material blurMaterial
    {
        get 
        {
            if (m_blur == null)
                m_blur = new Material(blurShader);
            return m_blur;
        }
    }
    #endregion
    
    #region 剔除材质 cutoffShader
    public Shader cutoffShader;
    private Material m_cutoff=null;
    private Material cutoffMaterial
    {
        get
        {
            if (m_cutoff == null)
                m_cutoff = new Material(cutoffShader);
            return m_cutoff;
        }
    }
    #endregion
    /// <summary>
    /// 辅助摄像机渲染的RenderTexture
    /// </summary>
    private RenderTexture outlineRenderTex;
    /// <summary>
    /// 模糊扩大次数
    /// </summary>
    public int Iterations = 2;
    // Use this for initialization
    void Start () {
        outlineRenderTex = new RenderTexture((int)outlineCamera.pixelWidth, (int)outlineCamera.pixelHeight, 16);
    } // Update is called once per frame
void Update () {
}     void OnPreRender()
    {
        outlineCamera.targetTexture = outlineRenderTex;
        outlineCamera.RenderWithShader(solidMaterail.shader, "");
    }     void OnRenderImage(RenderTexture source, RenderTexture desture)
    {
        RenderTexture _renderTexture = RenderTexture.GetTemporary(outlineRenderTex.width, outlineRenderTex.height, 0);         MixRender(outlineRenderTex,ref _renderTexture);
       
        Graphics.Blit(_renderTexture, desture, compositeMaterial);
        RenderTexture.ReleaseTemporary(_renderTexture);
    }     
    void MixRender(RenderTexture in_outerTexture, ref RenderTexture _renderTexture)
    {
        RenderTexture buffer = RenderTexture.GetTemporary(in_outerTexture.width, in_outerTexture.height, 0);
        RenderTexture buffer2 = RenderTexture.GetTemporary(in_outerTexture.width, in_outerTexture.height, 0);         Graphics.Blit(in_outerTexture, buffer);         //多次模糊放大
        for (int i = 0; i < Iterations; i++)
        {
            FourTapCone(buffer, buffer2, i);
            Graphics.Blit(buffer2, buffer);
        }
        Graphics.Blit(in_outerTexture, buffer, cutoffMaterial);
        Graphics.Blit(buffer, _renderTexture);
      
        RenderTexture.ReleaseTemporary(buffer);
        RenderTexture.ReleaseTemporary(buffer2);
    }     float Spread = 0.8f;
    public void FourTapCone(RenderTexture source, RenderTexture dest, int iteration)
    {
        float off = 0.5f + iteration * Spread;
        Graphics.BlitMultiTap(source, dest, blurMaterial,
                               new Vector2(off, off),
                               new Vector2(-off, off),
                               new Vector2(off, -off),
                               new Vector2(-off, -off)
                               );
    } }

7.具体效果如下,因为这里是在主摄像机设置的屏幕特效,他可以忽略掉所有的遮挡,这是优点也是弊端

unity描边效果的更多相关文章

  1. Unity Shader实现描边效果

    http://gad.qq.com/article/detail/28346 描边效果是游戏里面非常常用的一种效果,一般是为了凸显游戏中的某个对象,会给对象增加一个描边效果.本篇文章和大家介绍下利用S ...

  2. UE4实现描边效果

    描边效果属于常见常用的功能,现VR项目中,也需要射线选中一个物体,使物体高亮. 于是在网上找了部分资料,同时也感谢群里的一位大神的提点,总算将描边的功能实现了,这里也写一个简单的示例步骤. 1.我并不 ...

  3. Unity镜子效果的实现(无需镜子Shader)

    Unity镜子效果制作教程 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- 心分享. ...

  4. [Flutter] 支持描边效果的Text

    新版的flutter已经自带这个功能了.TextSyle 中一个shadow . 目前flutter中没找到很好的办法给Text增加描边.自己扩展了一个TextEx,可以实现简单的描边效果,能满足大部 ...

  5. osgEarth2.8加载矢量数据描边效果

    通过修改osgearth自带的agglite插件,实现矢量描边效果,可以自定义描边的颜色和宽度(单位像素) 测试文件osgearth_features.cpp #include <osg/Not ...

  6. three.js使用卷积法实现物体描边效果

    法线延展法 网上使用法线延展法实现物体描边效果的文章比较多,这里不再描述. 但是这种方法有个缺点:当两个面的法线夹角差别较大时,两个面的描边无法完美连接.如下图所示: 卷积法 这里使用另一种方法卷积法 ...

  7. 使用 WPF 做个 PowerPoint 系列 基于 OpenXML 解析实现 PPT 文本描边效果

    本文是使用 WPF 做个 PowerPoint 系列的博客,本文来告诉大家如何解析 PPT 里面的文本描边效果,在 WPF 应用中绘制出来,实现像素级相同 背景知识 在开始之前,期望你了解了 PPT ...

  8. Unity—2D边缘检测(描边效果)

    一.ShaderLab 1.Alpha值边缘检测 根据图片的Alpha值边缘判定,向内扩一段距离做边缘,颜色设置未描边颜色: 片元着色阶段,向上下左右四个方向做检测,有一个点的透明度为0,判定为边缘: ...

  9. 用CSS3实现文字描边效果【效果在这儿,创意在你!】

    CSS3作为新兴的前端技术可以实现很多复杂变化的效果,比如文字描边. 这里主要用到text-shadow属性,顾名思义就是为文字加上阴影效果.例: text-shadow:10px 5px 2px # ...

随机推荐

  1. Java内存泄漏分析系列之六:JVM Heap Dump(堆转储文件)的生成和MAT的使用

    原文地址:http://www.javatang.com JVM Heap Dump(堆转储文件)的生成 正如Thread Dump文件记录了当时JVM中线程运行的情况一样,Heap Dump记录了J ...

  2. Android中典型的ROOT原理(5)

    ROOT的作用 Customization 用户的个人定制,如删除一些预安装,定制开机动画等. 特权操作 所有需要特权操作的基本都是要通过ROOT,这也是ROOT的初衷. ROOT的第一步:寻找漏洞并 ...

  3. ejabberd为游戏免除注册限制

    ejabberd为游戏免除注册限制 (金庆的专栏 2016.11) ejabberd聊天服务器默认会限制同一IP注册帐号须间隔600s. 在游戏中需要为每个角色注册一个聊天帐号,不应该有此限制. 可以 ...

  4. Android属性动画完全解析(中),ValueAnimator和ObjectAnimator的高级用法

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/43536355 大家好,在上一篇文章当中,我们学习了Android属性动画的基本用法 ...

  5. [csdn markdown]使用摘记三 简便快捷的流程图

    在线编写文字就可以实现复杂的流程图,再也不需要纠结了! 开始 操作流程 条件 结束 开始 st=>start: 开始 操作流程 st->op->cond 条件 cond=>co ...

  6. 使用maven执行单元测试总结

    maven本身没有单元测试框架,但是maven的default生命周期的test阶段绑定了maven-surefire-plugin插件,该插件可以调用Junit3.Junit4.TestNG等Jav ...

  7. Servlet - Listener、Filter、Decorator

    Servlet 标签 : Java与Web Listener-监听器 Listener为在Java Web中进行事件驱动编程提供了一整套事件类和监听器接口.Listener监听的事件源分为Servle ...

  8. 为什么不要在viewDidLoad方法中设置开始监听键盘通知

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 一个普遍的错误是,程序猿(媛)试图在view controll ...

  9. Dynamics CRM 后台通过组织服务获取时间字段值的准确转换

    做CRM开发的都知道,在系统时间字段的处理上是有讲究的,因为数据库中存的是UTC时间,CRM的界面时间字段会根据个人设置中的时区以及格式自动调整,这是最基本的一面,那还有很多使用时间的场景,比如脚本使 ...

  10. Android 四种常见的线程池

    引入线程池的好处 1)提升性能.创建和消耗对象费时费CPU资源 2)防止内存过度消耗.控制活动线程的数量,防止并发线程过多. 我们来看一下线程池的简单的构造 public ThreadPoolExec ...