Unity实现物体外发光描边效果方式有好几种,如重叠放大模型描边Pass、卷积核描边、屏幕后处理等。

HIightingSytem使用了屏幕后期效果实现,效果如下:

整理出核心代码如下,主要分为4个步骤。

1 ,根据场景上所有需要描边的物体轮廓,将它们画到一张RenderTexture上,其中黑色部分A通道值为0。实现着色器为HighlightingOpaque.Shader。

2,将RenderTexture模糊化,通过偏移uv混合周围像素的值,包括混合A通道的值。实现着色器为HighlightingBlur.Shader。

混合后的RGB通道的值

混合后A通道的值:

3,通过模板测试,裁剪和场景中描边模型重叠的像素,即在着色器中通过设置重叠部分A通道的值为0。实现着色器为HighlightingCut.Shader。

4,根据RenderTexture的A通道的值,和屏幕后的Source纹理进行和RenderTexture的RGB值进行差值混合,得出描边效果。

实现着色器为HighlightingComposit.Shader。

整理出核心代码如下:

OutlineObject.cs

OutlineObjectRender.cs

HighlightingOpaque.shader

HighlightingBlur.shader

HighlightingCut.shader

HighlightingComposite.shader


OutlineObject.cs

一个场景可能挂多个,必须挂在场景物体的有MeshRender/SkinMeshRender的节点上!

OutlineObjectRender.cs

必须挂在有MainCamera的节点上!


OutlineObject.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class OutlineObject : MonoBehaviour
{public Color color = Color.red;[HideInInspector]public Renderer render;[HideInInspector]public Material objMat;private void Start(){render = GetComponent<MeshRenderer>();if (render == null)render = GetComponent<SkinnedMeshRenderer>();objMat = new Material(Shader.Find("Hidden/Highlighted/Opaque"));OutlineObjectRender.Instance.AddOutlineObject(this);}
}

OutlineObjectRender.cs

using UnityEngine;
using UnityEngine.Rendering;
using System.Collections.Generic;
public class OutlineObjectRender : MonoBehaviour
{public static OutlineObjectRender Instance;private List<OutlineObject> outlineObjs = new List<OutlineObject>();private CommandBuffer buf;//1,渲染OutlineObject对象成平面纹理;private Material blurMat;//2,将步骤1纹理模糊化,模糊边缘向平面周边扩散;private Material maskMat;//3,通过模板测试,不渲染步骤2纹理中和模型重叠的片元部分,只渲染模糊边框;private Material combineMat;//4,将当前相机渲染的屏幕结果,与步骤3得出的轮廓纹理结合得出描边效果。private RenderTextureDescriptor rtft;private RenderTexture rt;private RenderTargetIdentifier rtID;private int blur0;private int blur1;private void Awake(){Instance = this;blurMat = new Material(Shader.Find("Hidden/Highlighted/Blur"));maskMat = new Material(Shader.Find("Hidden/Highlighted/Cut"));combineMat = new Material(Shader.Find("Hidden/Highlighted/Composite"));blur0 = Shader.PropertyToID("Blur0");blur1 = Shader.PropertyToID("Blur1");buf = new CommandBuffer();Camera.main.AddCommandBuffer(CameraEvent.BeforeImageEffectsOpaque, buf);}public void AddOutlineObject(OutlineObject obj) {if (this.outlineObjs.Contains(obj) == false){this.outlineObjs.Add(obj);}}public void RemoveOutlineObject(OutlineObject obj) {if (this.outlineObjs.Contains(obj))this.outlineObjs.Remove(obj);}private void OnPreRender(){if (rt == null){Camera cam = GetComponent<Camera>();rtft = new RenderTextureDescriptor(cam.pixelWidth, cam.pixelHeight, RenderTextureFormat.ARGB32, 24);rtft.colorFormat = RenderTextureFormat.ARGB32;rtft.sRGB = QualitySettings.activeColorSpace == ColorSpace.Linear;rtft.useMipMap = false;rtft.msaaSamples = 8;rt = new RenderTexture(rtft);rt.filterMode = FilterMode.Point;rt.wrapMode = TextureWrapMode.Clamp;if (!rt.Create()){Debug.LogError("Failed to create RenderTexture!");}rtID = new RenderTargetIdentifier(rt);maskMat.SetFloat("_HighlightingFillAlpha", 0);}buf.Clear();buf.SetRenderTarget(rtID);buf.ClearRenderTarget(true, true, new Color(0, 0, 0, 0));RenderTextureDescriptor desc = rtft;desc.width = rt.width;desc.height = rt.height;desc.depthBufferBits = 0;for (int i = 0; i < this.outlineObjs.Count; i++){//第一步,将所有要描边的render GameObject用Opaque渲染成2D面颜色,Draw到buf里。//所有的需要描边的Render对象都要在这里处理一下,用CommandBuff统一画到同一个Texture上,交给下面的步骤处理。outlineObjs[i].objMat.SetColor("_HighlightingColor", outlineObjs[i].color);buf.DrawRenderer(outlineObjs[i].render, outlineObjs[i].objMat);}//第二步,对buf的texture进行模糊。//获取2张模板用于模糊处理。buf.GetTemporaryRT(blur0, desc, FilterMode.Bilinear);buf.GetTemporaryRT(blur1, desc, FilterMode.Bilinear);RenderTargetIdentifier blurID0 = new RenderTargetIdentifier(blur0);RenderTargetIdentifier blurID1 = new RenderTargetIdentifier(blur1);//开始模糊循环之前,将rtID传到blurID。buf.Blit(rtID, blurID0);bool oddEven = true;for (int i = 0; i < 4; i++){float off = 1.5f + 0.5f * i;buf.SetGlobalFloat(HighlightingSystem.ShaderPropertyID._HighlightingBlurOffset, off);if (oddEven){buf.Blit(blurID0, blurID1, blurMat);}else{buf.Blit(blurID1, blurID0, blurMat);}oddEven = !oddEven;}//第三步,对得到模糊纹理进行模板测试,去除和模型重叠的片元像素,通过buf.Blit(oddEven ? blurID0 : blurID1, rtID, maskMat);//buf.SetGlobalTexture("_HighlightingBuffer", rtID);buf.ReleaseTemporaryRT(blur0);buf.ReleaseTemporaryRT(blur1);}private void OnRenderImage(RenderTexture source, RenderTexture destination){//第四步,与当前屏幕效果混合Graphics.Blit(source, destination, combineMat);}
}

HighlightingOpaque.shader

用于将场景上需要描边的物体渲染到RenderTexture上。

Shader "Hidden/Highlighted/Opaque"
{Properties{[HideInInspector] _HighlightingColor ("", Color) = (1, 1, 1, 1)}SubShader{Lighting OffFog { Mode Off }ZWrite Off         // Manual depth testZTest Always        // Manual depth testPass{Stencil{Ref 1Comp AlwaysPass ReplaceZFail Keep}CGPROGRAM#pragma vertex vert#pragma fragment frag#pragma fragmentoption ARB_precision_hint_fastest#pragma target 2.0#pragma multi_compile __ HIGHLIGHTING_OVERLAY#include "UnityCG.cginc"uniform fixed4 _HighlightingColor;#ifndef HIGHLIGHTING_OVERLAYUNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);#endifstruct vs_input{float4 vertex : POSITION;UNITY_VERTEX_INPUT_INSTANCE_ID};struct ps_input{float4 pos : SV_POSITION;#ifndef HIGHLIGHTING_OVERLAYfloat4 screen : TEXCOORD0;#endif};ps_input vert(vs_input v){ps_input o;UNITY_SETUP_INSTANCE_ID(v);o.pos = UnityObjectToClipPos(v.vertex);#ifndef HIGHLIGHTING_OVERLAYo.screen = ComputeScreenPos(o.pos);COMPUTE_EYEDEPTH(o.screen.z);#endifreturn o;}fixed4 frag(ps_input i) : SV_Target{#ifndef HIGHLIGHTING_OVERLAYfloat z = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.screen));float perspZ = LinearEyeDepth(z);   // LinearEyeDepth automatically handles UNITY_REVERSED_Z case#if defined(UNITY_REVERSED_Z)z = 1 - z;#endiffloat orthoZ = _ProjectionParams.y + z * (_ProjectionParams.z - _ProjectionParams.y);  // near + z * (far - near)float sceneZ = lerp(perspZ, orthoZ, unity_OrthoParams.w);clip(sceneZ - i.screen.z + 0.01);#endifreturn _HighlightingColor;}ENDCG}}Fallback Off
}

HighlightingBlur.shader

将RenderTexture模糊化,并混合A通道的值。

Shader "Hidden/Highlighted/Blur"
{Properties{[HideInInspector] _MainTex ("", 2D) = "" {}[HideInInspector] _HighlightingIntensity ("", Range (0.25,0.5)) = 0.3}SubShader{Pass{ZTest AlwaysCull OffZWrite OffLighting OffFog { Mode Off }CGPROGRAM#pragma vertex vert#pragma fragment frag#pragma fragmentoption ARB_precision_hint_fastest#pragma target 2.0#pragma multi_compile DIAGONAL_DIRECTIONS STRAIGHT_DIRECTIONS ALL_DIRECTIONS#include "UnityCG.cginc"uniform sampler2D _MainTex;uniform float4 _MainTex_ST;uniform float4 _MainTex_TexelSize;uniform float _HighlightingBlurOffset;uniform half _HighlightingIntensity;struct vs_input{float4 vertex : POSITION;float2 texcoord : TEXCOORD0;};struct ps_input{float4 pos : SV_POSITION;#if defined(ALL_DIRECTIONS)float4 uv0 : TEXCOORD0;float4 uv1 : TEXCOORD1;float4 uv2 : TEXCOORD2;float4 uv3 : TEXCOORD3;#elsefloat4 uv0 : TEXCOORD0;float4 uv1 : TEXCOORD1;#endif};ps_input vert(vs_input v){ps_input o;o.pos = UnityObjectToClipPos(v.vertex);float2 uv = UnityStereoScreenSpaceUVAdjust(v.texcoord, _MainTex_ST);float2 offs = _HighlightingBlurOffset * _MainTex_TexelSize.xy;#if defined(ALL_DIRECTIONS)// Diagonalo.uv0.x = uv.x - offs.x;o.uv0.y = uv.y - offs.y;o.uv0.z = uv.x + offs.x;o.uv0.w = uv.y - offs.y;o.uv1.x = uv.x + offs.x;o.uv1.y = uv.y + offs.y;o.uv1.z = uv.x - offs.x;o.uv1.w = uv.y + offs.y;// Straighto.uv2.x = uv.x - offs.x;o.uv2.y = uv.y;o.uv2.z = uv.x + offs.x;o.uv2.w = uv.y;o.uv3.x = uv.x;o.uv3.y = uv.y - offs.y;o.uv3.z = uv.x;o.uv3.w = uv.y + offs.y;#elif defined(STRAIGHT_DIRECTIONS)// Straighto.uv0.x = uv.x - offs.x;o.uv0.y = uv.y;o.uv0.z = uv.x + offs.x;o.uv0.w = uv.y;o.uv1.x = uv.x;o.uv1.y = uv.y - offs.y;o.uv1.z = uv.x;o.uv1.w = uv.y + offs.y;#else // Diagonalo.uv0.x = uv.x - offs.x;o.uv0.y = uv.y - offs.y;o.uv0.z = uv.x + offs.x;o.uv0.w = uv.y - offs.y;o.uv1.x = uv.x + offs.x;o.uv1.y = uv.y + offs.y;o.uv1.z = uv.x - offs.x;o.uv1.w = uv.y + offs.y;#endifreturn o;}half4 frag(ps_input i) : SV_Target{half4 color1 = tex2D(_MainTex, i.uv0.xy);fixed4 color2;// For straight or diagonal directionscolor2 = tex2D(_MainTex, i.uv0.zw);color1.rgb = max(color1.rgb, color2.rgb);color1.a += color2.a;color2 = tex2D(_MainTex, i.uv1.xy);color1.rgb = max(color1.rgb, color2.rgb);color1.a += color2.a;color2 = tex2D(_MainTex, i.uv1.zw);color1.rgb = max(color1.rgb, color2.rgb);color1.a += color2.a;// For all directions#if defined(ALL_DIRECTIONS)color2 = tex2D(_MainTex, i.uv2.xy);color1.rgb = max(color1.rgb, color2.rgb);color1.a += color2.a;color2 = tex2D(_MainTex, i.uv2.zw);color1.rgb = max(color1.rgb, color2.rgb);color1.a += color2.a;color2 = tex2D(_MainTex, i.uv3.xy);color1.rgb = max(color1.rgb, color2.rgb);color1.a += color2.a;color2 = tex2D(_MainTex, i.uv3.zw);color1.rgb = max(color1.rgb, color2.rgb);color1.a += color2.a;#endifcolor1.a *= _HighlightingIntensity;return color1;}ENDCG}}Fallback off
}

HighlightingCut.shader

通过模板测试,将RenderTexture中和场景中重叠的像素A通道设置为0.

Shader "Hidden/Highlighted/Cut"
{Properties{[HideInInspector] _MainTex ("", 2D) = "" {}[HideInInspector] _HighlightingFillAlpha ("", Range(0.0, 1.0)) = 1.0}SubShader{Lighting OffFog { Mode off }ZWrite OffZTest AlwaysCull BackPass{Stencil{Ref 1Comp NotEqualPass KeepZFail Keep}CGPROGRAM#pragma vertex vert#pragma fragment frag#pragma fragmentoption ARB_precision_hint_fastest#pragma target 2.0#include "UnityCG.cginc"struct vs_input{float4 vertex : POSITION;half2 texcoord : TEXCOORD0;};struct ps_input{float4 pos : SV_POSITION;half2 uv : TEXCOORD0;};uniform sampler2D _MainTex;uniform float4 _MainTex_ST;ps_input vert(vs_input v){ps_input o;o.pos = UnityObjectToClipPos(v.vertex);o.uv = UnityStereoScreenSpaceUVAdjust(v.texcoord, _MainTex_ST);return o;}fixed4 frag(ps_input i) : SV_Target{return tex2D(_MainTex, i.uv);}ENDCG}Pass{Stencil{Ref 1Comp EqualPass KeepZFail Keep}ColorMask ACGPROGRAM#pragma vertex vert#pragma fragment frag#pragma fragmentoption ARB_precision_hint_fastest#pragma target 2.0#include "UnityCG.cginc"struct vs_input{float4 vertex : POSITION;};struct ps_input{float4 pos : SV_POSITION;};uniform float _HighlightingFillAlpha;ps_input vert(vs_input v){ps_input o;o.pos = UnityObjectToClipPos(v.vertex);return o;}fixed4 frag() : SV_Target{return fixed4(0, 0, 0, _HighlightingFillAlpha);}ENDCG}}FallBack Off
}

HighlightingComposite.shader

根据RenderTexture中A通道的值,将RenderTexture的RGB值和屏幕纹理插值叠加。得出最后的描边效果。

Shader "Hidden/Highlighted/Composite"
{Properties{[HideInInspector] _MainTex ("", 2D) = "" {}[HideInInspector] _HighlightingBuffer ("", 2D) = "" {}}SubShader{Pass{Lighting OffFog { Mode off }ZWrite OffZTest AlwaysCull OffCGPROGRAM#pragma vertex vert#pragma fragment frag#pragma fragmentoption ARB_precision_hint_fastest#pragma target 2.0#include "UnityCG.cginc"struct vs_input{float4 vertex : POSITION;float2 texcoord : TEXCOORD0;};struct ps_input{float4 pos : SV_POSITION;half2 uv0 : TEXCOORD0;half2 uv1 : TEXCOORD1;};uniform sampler2D _MainTex;uniform float4 _MainTex_ST;uniform float4 _MainTex_TexelSize;uniform sampler2D _HighlightingBuffer;ps_input vert(vs_input v){ps_input o;o.pos = UnityObjectToClipPos(v.vertex);o.uv0 = UnityStereoScreenSpaceUVAdjust(v.texcoord, _MainTex_ST);o.uv1 = o.uv0;#if UNITY_UV_STARTS_AT_TOPif (_MainTex_TexelSize.y < 0){o.uv1.y = 1-o.uv1.y;}#endifreturn o;}fixed4 frag(ps_input i) : SV_Target{fixed4 c1 = tex2D(_MainTex, i.uv0);fixed4 c2 = tex2D(_HighlightingBuffer, i.uv1);c1.rgb = lerp(c1.rgb, c2.rgb, c2.a);return c1;}ENDCG}}FallBack Off
}

Unity HilightingSystem屏幕后实现物体外发光描边效果相关推荐

  1. Unity使用Shader实现3D模型外描边效果ObjectOutline.shader

    一.前言 有同学问我3D模型的外描边怎么弄,其实网上有很多文章写了实现方式,我就再写个简单的实现和操作流程吧~ 二.3D模型外描边效果 三.如何制作 将最下面的shader代码保存为ObjectOut ...

  2. Unity使用Shader实现3D模型外描边效果

    文章目录 一.前言 二.3D模型外描边效果 三.如何制作 三.shader代码 一.前言 有同学问我3D模型的外描边怎么弄,其实网上有很多文章写了实现方式,我就再写个简单的实现和操作流程吧~ 二.3D ...

  3. Unity Shader 屏幕后效果——Bloom外发光

    Bloom的原理很简单,主要是提取渲染图像中的亮部区域,并对亮部区域进行模糊处理,再与原始图像混合而成. 一般对亮部进行模糊处理的部分采用高斯模糊,关于高斯模糊,详见之前的另一篇博客: https:/ ...

  4. Unity Shader 屏幕后效果——高斯模糊

    高斯模糊是图像模糊处理中非常经典和常见的一种算法,也是Bloom屏幕效果的基础. 实现高斯模糊同样用到了卷积的概念,关于卷积的概念和原理详见我的另一篇博客: https://www.cnblogs.c ...

  5. Unity Shader - 描边效果

    原文链接:http://blog.csdn.net/puppet_master/article/details/54000951 简介 描边效果是游戏里面非常常用的一种效果,一般是为了凸显游戏中的某个 ...

  6. Unity Shader 卡通渲染 实时模型动画描边的研究

    前言 卡通渲染也叫非真实感渲染(英文简写:NPR),"描边"在图形学和数字图像里都叫边缘检测.因此你可以在很多文献网站上面找到很多这类文献,但最后我发现基于图形学使用的方式基本都是 ...

  7. unity 3d物体描边效果_从零开始的卡通渲染描边篇

    序言: 一直对卡通渲染非常感兴趣,前后翻找了不少的文档,做了一些工作.前段时间<从零开始>的手游上线了,试着渲染了一下的其中模型,觉得效果很不错.打算写一个专栏记录其中的渲染技术.在后面的 ...

  8. unity 给图片边缘_Unity Shader 屏幕后效果——边缘检测

    关于屏幕后效果的控制类详细见之前写的另一篇博客: 这篇主要是基于之前的控制类,实现另一种常见的屏幕后效果--边缘检测. 概念和原理部分: 首先,我们需要知道在图形学中经常处理像素的一种操作--卷积. ...

  9. Unity Shader-Command Buffer的使用(景深与描边效果重置版)

    Unity Shader-Command Buffer的使用(景深与描边效果重置版) https://blog.csdn.net/puppet_master/article/details/72669 ...

最新文章

  1. 快递打印云服务器_企业微信支持寄快递查快递,实现员工寄件自由
  2. 安装rlwrap 的简单方法,亲测好用
  3. 实现简单的shell sed替换功能
  4. Linux 2.6 完全公平调度算法CFS(Completely Fair Scheduler) 分析
  5. ConsurrentDictionary并发字典知多少?
  6. sql跨表查询_跨表更新,看到自己写的SQL像个憨憨
  7. 跨域问题及CORS机制
  8. 使用var声明的变量 和 直接赋值并未声明的变量的区别
  9. 你可能不知道的CSS3属性: object-fit,object-position的妙用
  10. 《树莓派Python编程入门与实战(第2版)》——2.2 使用Raspbian命令行
  11. 在TabActivity中无法使用bindService的解决方法
  12. 互联网公司愚人节策划大盘点,在恶搞界谁是老大?
  13. 惠普笔记本的Windows10和Ubuntu20.04双系统安装教程
  14. amigo幸运字符什么意思_做个爬虫比你想象中简单!爬虫是什么?怎么做?
  15. FFplay文档解读-47-多媒体过滤器一
  16. C语言计算大写字母的个数
  17. 特征选择 Relief 方法
  18. mesos集群模式安装部署
  19. js点击图片打印图像
  20. 计算机音乐念诗之王,念诗之王怎么做出来的 念诗之王完整顺口溜

热门文章

  1. 深度学习经典教材中英文对照,好棒!
  2. 什么是docker和docker的架构
  3. msf连接mysql数据库_msf链接数据库
  4. 查看fna文件Linux,asn,faa,ffn,fna等文件的含义
  5. hrbust 1401 九连环(矩阵快速幂)
  6. 用stream流来遍历处理list,筛选出符合条件的list,并对其中某些值求和
  7. php实现dota天梯、wow竞技场、lol排位赛匹配加分算法ELO
  8. 海思Hi3536开发——处理器整体架构了解
  9. 混合精度训练-Automatic Mixed Precision
  10. python用QQ邮箱给多人发送邮件