这里总结了几种在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. JavaScript Date(日期)对象

    返回当日的日期和时间 如何使用 Date() 方法获得当日的日期. getFullYear() 使用 getFullYear() 获取年份. getTime() getTime() 返回从 1970 ...

  2. Cassandra Secondary Index 介绍

    摘要 本文主要介绍cassandra中的索引,物化视图,有些知识点需要对cassandra有基本的认识才能理解.比如数据在cassandra节点中如何分布.如果有不明白的地方可以看本专栏之前文章.或者 ...

  3. RAP在线接口管理统计部署

    文档: https://github.com/thx/RAP/wiki/home_cn centos上部署 参考:https://github.com/thx/RAP/wiki/deploy_on_c ...

  4. Java基础之枚举妙用

    对于枚举,初学Java的时候可能我们就已经接触过了,但是在毕业前,其实一直都不知道真正工作里面枚举是怎么用的,枚举有什么用?接下来,博主就介绍枚举在实际工作中的一种使用场景,本文只适合初级的小菜鸟看哈 ...

  5. C++ 虚函数表 多重继承

    上次研究的是单继承的情况,这次研究多重继承下的虚函数表的排列情况. 这次A,A1,A2,B这几个类的继承关系如下图: 测试代码如下: #include<iostream> using na ...

  6. Android自定义处理崩溃异常

    用过安卓手机的用户以及安卓开发者们会时长碰到程序异常退出的情况,普通用户遇到这种情况,肯定非常恼火,甚至会骂一生垃圾软件,然后卸载掉.那么开发者们在开发过程中遇到这种情况给怎么办呢,当然,你不可能世界 ...

  7. JavaScript与jQuery获取相邻控件

    原始代码如下,需求是onclick中的OpenIframe方法捕捉到input中的value值,由于某些限制无法使用正常的操作dom根据name值来取,所以决定通过相邻空间的方式获取 <div& ...

  8. Android高级控件(六)——自定义ListView高仿一个QQ可拖拽列表的实现

    Android高级控件(六)--自定义ListView高仿一个QQ可拖拽列表的实现 我们做一些好友列表或者商品列表的时候,居多的需求可能就是需要列表拖拽了,而我们选择了ListView,也是因为使用L ...

  9. android推荐使用dialogFrament而不是alertDialog

    DialogFragment在android 3.0时被引入.是一种特殊的Fragment,用于在Activity的内容之上展示一个模态的对话框.典型的用于:展示警告框,输入框,确认框等等. 在Dia ...

  10. UNIX网络编程——利用recv和readn函数实现readline函数

    在前面的文章中,我们为了避免粘包问题,实现了一个readn函数读取固定字节的数据.如果应用层协议的各字段长度固定,用readn来读是非常方便的.例如设计一种客户端上传文件的协议,规定前12字节表示文件 ...