【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 后面我再看看 别人是怎么玩的~ 笑
上一篇: WPF随笔(十二)--使用MVVM模式
下一篇: day6