这里总结了几种在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. Python 3.3.2 round函数并非"四舍五入"

    对于一些貌似很简单常见的函数,最好还是去读一下Python文档,否则当你被某个BUG折磨得死去活来时,还不知根源所在.尤其是Python这种不断更新的语言.(python 2.7 的round和3.3 ...

  2. Redis源码学习:字符串

    Redis源码学习:字符串 1.初识SDS 1.1 SDS定义 Redis定义了一个叫做sdshdr(SDS or simple dynamic string)的数据结构.SDS不仅用于 保存字符串, ...

  3. 计算机网络之IP地址

    IP地址的分类 整个的因特网就是一个单一的.抽象的网络.IP地址就是给因特网上的每一个主机(或路由器)的每一个接口分配一个在全世界范围内唯一的32位的标识符. 所谓分类的IP地址,就是将IP地址划分为 ...

  4. 初识RecyclerView

    初识RecyclerView 效果图 RecyclerView与ListView对比(官方) RecyclerView 小组件比 ListView 更高级且更具灵活性. 此小组件是一个用于显示庞大数据 ...

  5. Android Multimedia框架总结(十六)Camera2框架之openCamera及session过程

    转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52942533 前言:前一篇介绍了 ...

  6. Gazebo機器人仿真學習探索筆記(五)環境模型

    環境模型構建可以通過向其中添加模型實現,待之後補充,比較有趣的是建築物模型, 可以編輯多層樓層和房間,加入樓梯,窗戶和牆壁等,具體可以參考附錄,等有空再補充. 起伏地形環境構建可以參考之前內容:在Ga ...

  7. x264源代码简单分析:宏块编码(Encode)部分

    ===================================================== H.264源代码分析文章列表: [编码 - x264] x264源代码简单分析:概述 x26 ...

  8. Java基本语法-----java标识符

    1什么是标识符 就是程序员在定义java程序时,自定义的一些名字,例如helloworld 程序里关键字class 后跟的Demo,就是我们定义的类名.类名就属于标识符的一种. 标识符除了应用在类名上 ...

  9. 六星经典CSAPP笔记(1)计算机系统巡游

    CSAPP即<Computer System: A Programmer Perspective>的简称,中文名为<深入理解计算机系统>.相信很多程序员都拜读过,之前买的旧版没 ...

  10. Struts 2 标签库

    <s:if>标签 拥有一个test属性,其表达式的值用来决定标签里内容是否显示 <s:if test="#request.username=='clf'"> ...