700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > unity shader基于后处理的硬描边

unity shader基于后处理的硬描边

时间:2020-11-20 06:48:03

相关推荐

unity shader基于后处理的硬描边

网上的描边实现大部分都是法线外扩、模板、后处理这种的,我想实现一个类似U3D编辑界面那种硬质描边,法线外扩的缺点大部分人都知道:遇到“硬边”,法线外扩就会造成描边断裂,非常影响美观。模板又要修改模型材质操作有点复杂。而后处理就挺简单修改模型的显示层单独渲染一遍就ok了,但是目前网上描边都做成了“自发光”,边不够“硬”,而且由于模型上需要绑碰撞,换层有点不“合理”;了解到CommandBuffer可以解决这个顾虑,需要操作的仅是把模型添加入CommandBuffer单独绘制即可,很满足我的需求,那就改一下吧。

首先需要软描边(类自发光)的大佬可以参考这篇文章,我也是参考这个大佬改的:传送门

具体操作是首先新建脚本OutLineCameraComponent.cs,拷贝上代码并挂载到主相机上。

/****************************************************文件:OutLineCameraComponent.cs名称:后处理描边相机组件说明:使用时需要将脚本挂载到三维模型相机上。主要参考文章:/vikingsc_1/article/details/76570931by--Sep*****************************************************/using System;using UnityEngine; using System.Collections.Generic;using UnityEngine.Rendering; // [ExecuteInEditMode]public class OutLineCameraComponent : MonoBehaviour{ private RenderTexture renderTexture = null; private CommandBuffer commandBuffer = null; private RenderTexture temp1 = null; private RenderTexture temp2 = null; [Header("单色shader")]public Shader preoutlineShader = null; [Header("描边shader")]public Shader shader = null;private Material _preoutlineMaterial = null;private Material _material = null;public Color outLineColor;public Material mMaterial{get{if (_material == null){_material = GenerateMaterial(shader);_material.SetColor("_OutlineColor", outLineColor); }return _material;}}[Header("降分辨率,与描边宽度对应")]public int downSample = 2; [Header("描边强度")][Range(0.0f, 10.0f)] public float outLineStrength = 3.0f; //目标对象 private BaseModel targetModel;private void Awake(){_preoutlineMaterial = GenerateMaterial(preoutlineShader);_preoutlineMaterial.SetColor("_MainColor",outLineColor);}//根据shader创建用于屏幕特效的材质protected Material GenerateMaterial(Shader shader){if (shader == null)return null;if (shader.isSupported == false)return null;Material material = new Material(shader);material.hideFlags = HideFlags.DontSave;if (material)return material;return null;}public void AddTarget(BaseModel target){targetModel = target;RefreshCommandBuff();}public void RemoveTarget(BaseModel target){RefreshCommandBuff();}public void RefreshCommandBuff(){if (renderTexture) RenderTexture.ReleaseTemporary(renderTexture);int width = Screen.width / downSample;int height = Screen.height / downSample;renderTexture = RenderTexture.GetTemporary(width, height, 0); temp1 = RenderTexture.GetTemporary(width, height, 0); temp2 = RenderTexture.GetTemporary(width, height, 0);commandBuffer = new CommandBuffer(); commandBuffer.SetRenderTarget(renderTexture); commandBuffer.ClearRenderTarget(true, true, Color.black);for (int i = 0; i < targetModel.meshRenders.Length; i++){// Renderer[] renderers = targetObjects[i].GetComponentsInChildren<Renderer>();// foreach (Renderer r in renderer)//commandBuffer.DrawRenderer(r, targetObjects[i].material);Renderer renderer = targetModel.meshRenders[i];commandBuffer.DrawRenderer(renderer, _preoutlineMaterial);}}/// <summary>/// 绘制描边/// </summary>/// <param name="source"></param>/// <param name="destination"></param>void OnRenderImage(RenderTexture source, RenderTexture destination) { if (mMaterial && renderTexture && commandBuffer != null) { Graphics.ExecuteCommandBuffer(commandBuffer); //查找边界Graphics.Blit(renderTexture, temp1, mMaterial, 0); //用模糊图和原始图计算出轮廓图mMaterial.SetTexture("_BlurTex", temp1); Graphics.Blit(renderTexture, temp2, mMaterial, 1); //轮廓图和场景图叠加 mMaterial.SetTexture("_BlurTex", temp2); mMaterial.SetFloat("_OutlineStrength", outLineStrength); Graphics.Blit(source, destination, mMaterial, 2); RenderTexture.ReleaseTemporary(temp1); RenderTexture.ReleaseTemporary(temp2); } else { Graphics.Blit(source, destination); } }private void OnDestroy(){if (renderTexture) { RenderTexture.ReleaseTemporary(renderTexture); renderTexture = null; } if (temp1) { RenderTexture.ReleaseTemporary(temp1); temp1 = null; } if (temp2) { RenderTexture.ReleaseTemporary(temp2); temp2 = null; } if (commandBuffer != null) { commandBuffer.Release(); commandBuffer = null; }}}

然后新建OutLineEffect.shader,再把shader代码怼进去:

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'Shader "SepShader/PostEffect/OutLineEffect" { Properties{ _MainTex("Base (RGB)", 2D) = "white" {} _BlurTex("Blur", 2D) = "white"{} } CGINCLUDE #include "UnityCG.cginc" //用于剔除中心留下轮廓 struct v2f_cull { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; }; //用于模糊 struct v2f_blur { float4 pos : SV_POSITION;float2 uv[9] : TEXCOORD0;}; //用于最后叠加 struct v2f_add { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float2 uv1 : TEXCOORD1; }; sampler2D _MainTex; float4 _MainTex_TexelSize; sampler2D _BlurTex; float4 _BlurTex_TexelSize; float4 _offsets; float _OutlineStrength;fixed4 _OutlineColor;//获得轮廓 pass 1v2f_cull vert_cull(appdata_img v) { v2f_cull o; o.pos = UnityObjectToClipPos(v.vertex); o.uv = v.texcoord.xy; //dx中纹理从左上角为初始坐标,需要反向 #if UNITY_UV_STARTS_AT_TOP if (_MainTex_TexelSize.y < 0) o.uv.y = 1 - o.uv.y; #endif return o; } fixed4 frag_cull(v2f_cull i) : SV_Target { fixed4 colorMain = tex2D(_MainTex, i.uv); fixed4 colorBlur = tex2D(_BlurTex, i.uv); return colorBlur - colorMain; } //高斯模糊 pass 0v2f_blur vert_blur(appdata_img v) { v2f_blur o; _offsets *= _MainTex_TexelSize.xyxy;float2 width = _MainTex_TexelSize.xyxy;o.pos = UnityObjectToClipPos(v.vertex);o.uv[0] = v.texcoord + half2(-1, -1) * width;o.uv[1] = v.texcoord + half2(0, -1) * width;o.uv[2] = v.texcoord + half2(1, -1) * width;o.uv[3] = v.texcoord + half2(-1, 0) * width;o.uv[4] = v.texcoord + half2(0, 0) * width;o.uv[5] = v.texcoord + half2(1, 0) * width;o.uv[6] = v.texcoord + half2(-1, 1) * width;o.uv[7] = v.texcoord + half2(0, 1) * width;o.uv[8] = v.texcoord + half2(1, 1) * width;return o; } fixed4 frag_blur(v2f_blur i) : SV_Target {half outValue = 0;outValue += tex2D(_MainTex, i.uv[0]).b; outValue += tex2D(_MainTex, i.uv[1]).b; outValue += tex2D(_MainTex, i.uv[2]).b; outValue += tex2D(_MainTex, i.uv[3]).b; outValue += tex2D(_MainTex, i.uv[4]).b; outValue += tex2D(_MainTex, i.uv[5]).b; outValue += tex2D(_MainTex, i.uv[6]).b; outValue += tex2D(_MainTex, i.uv[7]).b; outValue += tex2D(_MainTex, i.uv[8]).b; outValue = saturate(outValue);return outValue * _OutlineColor; } //最终叠加 pass 2v2f_add vert_final(appdata_img v) { v2f_add o; o.pos = UnityObjectToClipPos(v.vertex); o.uv.xy = v.texcoord.xy; o.uv1.xy = o.uv.xy; #if UNITY_UV_STARTS_AT_TOP if (_MainTex_TexelSize.y < 0) o.uv.y = 1 - o.uv.y; #endif return o; } fixed4 frag_final(v2f_add i) : SV_Target { fixed4 ori = tex2D(_MainTex, i.uv1); fixed4 blur = tex2D(_BlurTex, i.uv); fixed4 final = ori + blur * _OutlineStrength; return final; } ENDCG SubShader { //pass 0: 高斯模糊 Pass { ZTest Off Cull Off ZWrite Off Fog{ Mode Off } CGPROGRAM #pragma vertex vert_blur #pragma fragment frag_blur ENDCG } //pass 1: 剔除中心部分 Pass { ZTest Off Cull Off ZWrite Off Fog{ Mode Off } CGPROGRAM #pragma vertex vert_cull#pragma fragment frag_cullENDCG } //pass 2: 最终叠加 Pass { ZTest Off Cull Off ZWrite Off Fog{ Mode Off } CGPROGRAM #pragma vertex vert_final#pragma fragment frag_final ENDCG } } }

对了,还需要一个纯色的shader作减法抠出来显示本来的纹理:

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'Shader "SepShader/S_SingleColor"{Properties{_MainColor ("Main Color", Color) = (1,0,0,1)_Width ("width",int) = 1}SubShader{Tags { "RenderType"="Opaque" }LOD 100Pass{Cull BackCGPROGRAM#include "UnityCG.cginc" #pragma vertex vert#pragma fragment fragstruct appdata{float4 vertex : POSITION;};struct v2f{float4 vertex : SV_POSITION;};fixed4 _MainColor;int _Width;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);return o;}fixed4 frag (v2f i) : SV_Target{return _MainColor;}ENDCG}}}

然后你要做的就是把这两个shader拖到OutLineCameraComponent脚本中对应的shader位置,然后运行时就把模型的meshrenderer通过AddTarget(BaseModel target)的方法传进来就可以在这个模型周围产生描边了,BaseModel类是我自定义的类,存了一堆模型的参数,需要你们自己改一小点就可以用了。

注意一点我这个描边颜色是青色,若要其他颜色可以改脚本里的颜色,但是我描边的shader里判断的是颜色的b通道的值,你若改其他颜色比如红色就要改成R通道了。另外增加downSample可以增加描边的宽度。

在然后就是描边的效果图了:

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。