【Unity】Shader之对指定物体进行描边效果

参考文章:https://blog.csdn.net/l773575310/article/details/78701756

方法一:对指定Layer层(Outline层)的物体进行描边处理

原理:使用一个只渲染Outline层的摄像机进行渲染,将渲染结果存入临时渲染纹理rt,再利用Graphics.Blit(rt, destination, targetMat, 0); 函数使用targetMat材质的shader的第一个Pass进行渲染,即对Outline层的物体进行边缘检测,然后将被认定为边缘的屏幕像素点替换成边缘颜色,否则保留原色。

边缘检测算法介绍:https://blog.csdn.net/qq_39574690/article/details/100182265

1、准备一个C#脚本,放于主摄像机上

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DrawOutlineMilkTest : MonoBehaviour
{
    public Shader drawOutlineShader;
    public Material targetMat;
    //渲染Outline层的摄像机
    public Camera cameraDrawOutline;
    public float far = 11;

    private void Awake()
    {
        cameraDrawOutline.CopyFrom(Camera.main);
        cameraDrawOutline.farClipPlane = far; //控制描边的最大范围
        cameraDrawOutline.clearFlags = CameraClearFlags.Color;
        cameraDrawOutline.backgroundColor = Color.black;
        cameraDrawOutline.cullingMask = 1 << LayerMask.NameToLayer("Outline"); //摄像机只渲染Outline层
    }

    //颜色
    public Color color = Color.white;
    //边框宽度
    public float width;    

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if (targetMat != null && drawOutlineShader != null)
        {
            //临时渲染纹理
            RenderTexture rt = RenderTexture.GetTemporary(source.width, source.height, 0);
            rt.Create();
            //获取摄像机原本的渲染纹理
            RenderTexture rawRt = cameraDrawOutline.targetTexture;
            //指定摄像机的渲染纹理为rt
            cameraDrawOutline.targetTexture = rt;
            //使用drawOutlineShader进行渲染,其渲染结果放于rt
            cameraDrawOutline.RenderWithShader(drawOutlineShader, "");

            //注意:shader里面还有一个参数是:_MainTex,这个是由Graphics.Blit函数将第一个参数即rt赋值给了shader的_MainTex,
            //所以才能只单单对Outline层的物体进行边缘检测!也就是只对这个层级的物体进行描边!ojbk~
            //设定材质参数,_SceneTex是屏幕纹理
            targetMat.SetTexture("_SceneTex", source);
            //边框颜色
            targetMat.SetColor("_Color", color);
            //边框宽度
            targetMat.SetFloat("_Width", width);            
            
            Graphics.Blit(rt, destination, targetMat, 0);            
            cameraDrawOutline.targetTexture = rawRt;
            rt.Release();
            rt = null;
        }
        else
        {
            Graphics.Blit(source, destination);
        }
    }
}

2、创建一个只渲染Outline层的摄像机,将其放于主摄像机下作为其子物体,并且保持位置旋转大小等等参数和主摄像机一致,并且禁用摄像机!

注意:Culling Mask是

禁用掉摄像机!

3、创建一个胶囊体,设定它的层级为Outline(这个是自定义层级!什么!你不会自定义Layer?百度!)

4、创建一个Shader,用于给Outline摄像机渲染的(其实这个可以不用shader就单纯拿到摄像机的当前渲染纹理应该可以的)

Shader "Unlit/DrawOccupiedMilk"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		LOD 100

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			// make fog work
			#pragma multi_compile_fog
			
			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float2 uv : TEXCOORD0;
				UNITY_FOG_COORDS(1)
				float4 vertex : SV_POSITION;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				UNITY_TRANSFER_FOG(o,o.vertex);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				// sample the texture
				fixed4 col = tex2D(_MainTex, i.uv);
				// apply fog
				UNITY_APPLY_FOG(i.fogCoord, col);
				return col;
			}
			ENDCG
		}
	}
}

赋值到这个参数上。

5、创一个材质球EdgeLayerLimitMilk 和 其对应的Shader(不多BB代码贴上)

Shader "Unlit/EdgeLayerLimitMilk"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}  //Outline层摄像机渲染出的纹理
		_SceneTex("Scene Text" ,2D) = "white"{}//屏幕纹理
		_Color("Color", Color) = (1,1,1,1)     //边框颜色
		_Width("Width", Float) = 1		       //边框宽度
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		LOD 100

		Pass
		{

			ZTest Always ZWrite Off Cull Off
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float2 uv[9] : TEXCOORD0;
				float4 vertex : SV_POSITION;				
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			float2 _MainTex_TexelSize;
			sampler2D _SceneTex;
			float _Width;
			fixed4 _Color;			
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				//准备九个坐标点
				o.uv[0] = v.uv + _MainTex_TexelSize.xy * float2(-1, -1) * _Width;
				o.uv[1] = v.uv + _MainTex_TexelSize.xy * float2(0, -1) * _Width;
				o.uv[2] = v.uv + _MainTex_TexelSize.xy * float2(1, -1) * _Width;
				o.uv[3] = v.uv + _MainTex_TexelSize.xy * float2(-1, 0) * _Width;
				o.uv[4] = v.uv + _MainTex_TexelSize.xy * float2(0, 0) * _Width;
				o.uv[5] = v.uv + _MainTex_TexelSize.xy * float2(1, 0) * _Width;
				o.uv[6] = v.uv + _MainTex_TexelSize.xy * float2(-1, 1) * _Width;
				o.uv[7] = v.uv + _MainTex_TexelSize.xy * float2(0, 1) * _Width;
				o.uv[8] = v.uv + _MainTex_TexelSize.xy * float2(1, 1) * _Width;
				return o;
			}
			fixed luminance(fixed4 color) {
				return 0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b;
			}

			//利用Sobel算子 进行计算梯度值
			half Sobel(v2f input) {				
				half Gx[9] = {
					-1,-2,-1,
					0,0,0,
					1,2,1
				};
				half Gy[9] = {
						-1,0,1,
						-2,0,2,
						-1,0,1
				};
				half edgeX = 0;
				half edgeY = 0;
				half lum;
				for (int i = 0; i < 9; i++)
				{
					//注意_MainTex是Outline层的渲染纹理,即只会对Layer为Outline的物体进行边缘检测,如果你改为_SceneTex就会对场上所有物体进行描边了
					//lum = luminance(tex2D(_MainTex, input.uv[i]));
					lum = Luminance(tex2D(_MainTex, input.uv[i]));//Luminance是内置函数,将颜色转为饱和度为0的颜色分量, 饱和度为0的颜色为(lum,lum,lum)
					edgeX += lum * Gx[i];
					edgeY += lum * Gy[i];
				}
				return (abs(edgeX) + abs(edgeY));
			}
			
			fixed4 frag (v2f i) : SV_Target
			{				
				//edge 0~1 越大就越接近边缘
				half edge = min(1, Sobel(i));
				//col是屏幕颜色
				fixed4 col = tex2D(_SceneTex, i.uv[4]);
				//使用edge插值屏幕颜色和边框颜色
				return lerp(col, _Color, edge);
			}
			ENDCG
		}
	}
}

材质球拖拽到这个参数上面

6、将渲染Outline层的摄像机拖拽到脚本的如下参数(妈的废话一堆,但是,可能会有人不理解这是啥呢呵呵)

7、设定Color颜色纸 特别要注意:Alpha不能是0!即透明通道不能是0!

方式二:emmmm 后面我再看看 别人是怎么玩的~ 笑

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值