欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-07-13 22:30:11
...

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

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

方法一:对指定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#脚本,放于主摄像机上

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

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层的摄像机,将其放于主摄像机下作为其子物体,并且保持位置旋转大小等等参数和主摄像机一致,并且禁用摄像机!

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

注意:Culling Mask是【Unity】Shader之对指定物体进行描边效果

【Unity】Shader之对指定物体进行描边效果禁用掉摄像机!

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

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

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
		}
	}
}

【Unity】Shader之对指定物体进行描边效果赋值到这个参数上。

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
		}
	}
}

【Unity】Shader之对指定物体进行描边效果材质球拖拽到这个参数上面

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

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

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

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