62.屏幕后期处理效果-效果实现-边缘检测-加入纯色背景功能
62.1 知识点
什么是纯色背景功能
在进行边缘描边时,有时我们希望只保留描边的边缘线,而不显示原图的背景颜色。这个功能的目的是将原图的背景颜色替换为自定义的纯色,比如白色、黑色或其他颜色,同时保留图片的边缘线。通过这种方式,我们可以去掉原本图片的颜色信息,使得效果类似于一张只有边缘的描边图像。
加入纯色背景功能
主要步骤
修改Shader代码*
在上节课的Shader代码中进行修改,复制Lesson61_EdgeDetection
并将其命名为Lesson62_EdgeDetection
。新属性声明和映射
- 添加背景颜色程度变量
_BackgroundExtent
,其中0
表示保留图片的原始颜色,1
表示完全抛弃图片原始颜色,0~1
之间的值可以控制保留的程度。 - 添加自定义背景颜色
_BackgroundColor
,定义用于替换图片原始颜色的颜色。
- 添加背景颜色程度变量
修改片元着色器
- 利用插值运算,记录纯色背景中像素的描边颜色。
- 在原始图片描边和纯色图片描边之间,根据背景颜色程度变量进行插值控制。
修改C#代码
复制Lesson61_EdgeDetection
为Lesson62_EdgeDetection
的C#代码中进行修改:- 添加背景颜色程度变量。
- 添加自定义背景颜色变量。
- 在
UpdateProperty
函数中添加属性设置。
在上节课的Shader代码中进行修改,复制Lesson61_EdgeDetection,起名为Lesson62_EdgeDetection
Shader "Unlit/Lesson62_EdgeDetection"
{
Properties
{
// 主纹理
_MainTex ("Texture", 2D) = "white" {}
// 边缘线的颜色
_EdgeColor("EdgeColor", Color) = (0,0,0,0)
}
SubShader
{
Tags
{
"RenderType"="Opaque" // 渲染类型为不透明
}
Pass
{
ZTest Always // 始终通过深度测试
Cull Off // 关闭背面剔除
ZWrite Off // 关闭深度写入
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex; // 主纹理采样器
half4 _MainTex_TexelSize; // 用于存储纹理大小的内置变量
fixed4 _EdgeColor; // 边缘颜色
struct v2f
{
// 用于存储9个像素的uv坐标
half2 uv[9] : TEXCOORD0;
float4 vertex : SV_POSITION; // 顶点位置
};
// 顶点着色器
v2f vert(appdata_base appdata_base)
{
v2f v2f;
v2f.vertex = UnityObjectToClipPos(appdata_base.vertex); // 将顶点从物体空间转换为裁剪空间
// 当前顶点的纹理坐标
half2 uv = appdata_base.texcoord;
// 计算9个像素的uv坐标
v2f.uv[0] = uv + _MainTex_TexelSize.xy * half2(-1, -1);
v2f.uv[1] = uv + _MainTex_TexelSize.xy * half2(0, -1);
v2f.uv[2] = uv + _MainTex_TexelSize.xy * half2(1, -1);
v2f.uv[3] = uv + _MainTex_TexelSize.xy * half2(-1, 0);
v2f.uv[4] = uv + _MainTex_TexelSize.xy * half2(0, 0);
v2f.uv[5] = uv + _MainTex_TexelSize.xy * half2(1, 0);
v2f.uv[6] = uv + _MainTex_TexelSize.xy * half2(-1, 1);
v2f.uv[7] = uv + _MainTex_TexelSize.xy * half2(0, 1);
v2f.uv[8] = uv + _MainTex_TexelSize.xy * half2(1, 1);
return v2f;
}
// 计算颜色的灰度值
fixed calcGrayValue(fixed4 color)
{
return 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
}
// Sobel算子相关的卷积计算
half Sobel(v2f v2f)
{
// Sobel算子对应的两个卷积核
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 nowGrayValue; // 灰度值
half edgeX = 0; // 水平方向的梯度值
half edgeY = 0; // 垂直方向的梯度值
for (int i = 0; i < 9; i++)
{
// 使用对应uv采样颜色 并计算灰度值
nowGrayValue = calcGrayValue(tex2D(_MainTex, v2f.uv[i]));
// 得到灰度值和对应卷积核相乘后加到梯度值中
edgeX += nowGrayValue * Gx[i];
edgeY += nowGrayValue * Gy[i];
}
// 计算该像素的最终梯度值
return abs(edgeX) + abs(edgeY);
}
// 片段着色器
fixed4 frag(v2f v2f) : SV_Target
{
// 使用Sobel算子计算当前像素的梯度值
half edge = Sobel(v2f);
// 根据计算得到的梯度值,在原始颜色和边缘颜色之间进行插值
fixed4 color = lerp(tex2D(_MainTex, v2f.uv[4]), _EdgeColor, edge);
return color;
}
ENDCG
}
}
Fallback Off // 不使用任何备用着色器
}
添加背景颜色程度属性和自定义背景颜色并进行映射。背景颜色程度0表示保留图片原始颜色,1表示完全抛弃图片原始颜色,0~1之间可以自己控制保留程度。自定义背景颜色定义用于替换图片原始颜色的颜色
Properties
{
// 主纹理
_MainTex ("Texture", 2D) = "white" {}
// 边缘线的颜色
_EdgeColor("EdgeColor", Color) = (0,0,0,0)
//背景颜色程度 1为纯色 0为原始颜色
_BackgroundExtent("BackgroundExtent", Range(0,1)) = 0
//背景颜色
_BackgroundColor("BackgroundColor", Color) = (1,1,1,1)
}
sampler2D _MainTex; // 主纹理采样器
half4 _MainTex_TexelSize; // 用于存储纹理大小的内置变量
fixed4 _EdgeColor; // 边缘颜色
fixed _BackgroundExtent;//背景颜色程度
fixed4 _BackgroundColor;//背景颜色
修改片元函数,计算纯色上描边的颜色。通过背景颜色程度和原来的原始图片描边进行插值运算并返回
// 片段着色器
fixed4 frag(v2f v2f) : SV_Target
{
// 使用Sobel算子计算当前像素的梯度值
half edge = Sobel(v2f);
// 原始图片描边 根据计算得到的梯度值,在原始颜色和边缘颜色之间进行插值
fixed4 withEdgeColor = lerp(tex2D(_MainTex, v2f.uv[4]), _EdgeColor, edge);
// 纯色图上描边 计算纯色,纯色上描边
fixed4 onlyEdgeColor = lerp(_BackgroundColor, _EdgeColor, edge);
//通过程度变量 去控制 是纯色描边 还是 原始颜色描边 在两者之间 进行过渡
return lerp(withEdgeColor, onlyEdgeColor, _BackgroundExtent);
}
创建名为Lesson62_EdgeDetection的C#脚本 ,在Lesson61_EdgeDetection的C#代码中进行修改,添加背景颜色程度变量,添加自定义背景光颜色,在UpdateProperty函数中添加属性设置
public class Lesson62_EdgeDetection : PostEffectBase
{
public Color EdgeColor;
public Color BackgroundColor;
[Range(0, 1)] public float BackgroundExtent;
protected override void UpdateProperty()
{
if (material != null)
{
material.SetColor("_EdgeColor", EdgeColor);
material.SetColor("_BackgroundColor", BackgroundColor);
material.SetFloat("_BackgroundExtent", BackgroundExtent);
}
}
}
将这个Lesson62_EdgeDetection
脚本挂载到摄像机上,并通过调整变量值来观察效果。通过修改背景颜色程度和背景颜色,您可以看到不同程度的边缘效果变化。
62.2 知识点代码
Lesson62_屏幕后期处理效果_效果实现_边缘检测_加入纯色背景功能.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson62_屏幕后期处理效果_效果实现_边缘检测_加入纯色背景功能 : MonoBehaviour
{
void Start()
{
#region 知识点一 什么是纯色背景功能
//我们在边缘描边时,有时只想保留描边的边缘线
//不想要显示原图的背景颜色
//比如把整个背景变为白色、黑色、等等自定义颜色
//而抛弃掉原本图片的颜色信息
//效果就像是一张描边图片
#endregion
#region 知识点二 加入纯色背景功能
//在上节课的Shader代码中进行修改,复制Lesson61_EdgeDetection,起名为Lesson62_EdgeDetection
//1.新属性声明 属性映射
// 添加 背景颜色程度变量 _BackgroundExtent 0表示保留图片原始颜色,1表示完全抛弃图片原始颜色,0~1之间可以自己控制保留程度
// 添加自定义背景颜色 _BackgroundColor,定义用于替换图片原始颜色的颜色
//2.修改片元着色器
// 利用插值运算,记录纯色背景中像素描边颜色
// 利用插值运算,在 原始图片描边 和 纯色图片描边 之间用程度变量进行控制
//在Lesson61_EdgeDetection的C#代码中进行修改
// 添加背景颜色程度变量
// 添加自定义背景光颜色
// 在UpdateProperty函数中添加属性设置
#endregion
}
}
Lesson62_EdgeDetection.shader
Shader "Unlit/Lesson62_EdgeDetection"
{
Properties
{
// 主纹理
_MainTex ("Texture", 2D) = "white" {}
// 边缘线的颜色
_EdgeColor("EdgeColor", Color) = (0,0,0,0)
//背景颜色程度 1为纯色 0为原始颜色
_BackgroundExtent("BackgroundExtent", Range(0,1)) = 0
//背景颜色
_BackgroundColor("BackgroundColor", Color) = (1,1,1,1)
}
SubShader
{
Tags
{
"RenderType"="Opaque" // 渲染类型为不透明
}
Pass
{
ZTest Always // 始终通过深度测试
Cull Off // 关闭背面剔除
ZWrite Off // 关闭深度写入
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex; // 主纹理采样器
half4 _MainTex_TexelSize; // 用于存储纹理大小的内置变量
fixed4 _EdgeColor; // 边缘颜色
fixed _BackgroundExtent;//背景颜色程度
fixed4 _BackgroundColor;//背景颜色
struct v2f
{
// 用于存储9个像素的uv坐标
half2 uv[9] : TEXCOORD0;
float4 vertex : SV_POSITION; // 顶点位置
};
// 顶点着色器
v2f vert(appdata_base appdata_base)
{
v2f v2f;
v2f.vertex = UnityObjectToClipPos(appdata_base.vertex); // 将顶点从物体空间转换为裁剪空间
// 当前顶点的纹理坐标
half2 uv = appdata_base.texcoord;
// 计算9个像素的uv坐标
v2f.uv[0] = uv + _MainTex_TexelSize.xy * half2(-1, -1);
v2f.uv[1] = uv + _MainTex_TexelSize.xy * half2(0, -1);
v2f.uv[2] = uv + _MainTex_TexelSize.xy * half2(1, -1);
v2f.uv[3] = uv + _MainTex_TexelSize.xy * half2(-1, 0);
v2f.uv[4] = uv + _MainTex_TexelSize.xy * half2(0, 0);
v2f.uv[5] = uv + _MainTex_TexelSize.xy * half2(1, 0);
v2f.uv[6] = uv + _MainTex_TexelSize.xy * half2(-1, 1);
v2f.uv[7] = uv + _MainTex_TexelSize.xy * half2(0, 1);
v2f.uv[8] = uv + _MainTex_TexelSize.xy * half2(1, 1);
return v2f;
}
// 计算颜色的灰度值
fixed calcGrayValue(fixed4 color)
{
return 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
}
// Sobel算子相关的卷积计算
half Sobel(v2f v2f)
{
// Sobel算子对应的两个卷积核
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 nowGrayValue; // 灰度值
half edgeX = 0; // 水平方向的梯度值
half edgeY = 0; // 垂直方向的梯度值
for (int i = 0; i < 9; i++)
{
// 使用对应uv采样颜色 并计算灰度值
nowGrayValue = calcGrayValue(tex2D(_MainTex, v2f.uv[i]));
// 得到灰度值和对应卷积核相乘后加到梯度值中
edgeX += nowGrayValue * Gx[i];
edgeY += nowGrayValue * Gy[i];
}
// 计算该像素的最终梯度值
return abs(edgeX) + abs(edgeY);
}
// 片段着色器
fixed4 frag(v2f v2f) : SV_Target
{
// 使用Sobel算子计算当前像素的梯度值
half edge = Sobel(v2f);
// 原始图片描边 根据计算得到的梯度值,在原始颜色和边缘颜色之间进行插值
fixed4 withEdgeColor = lerp(tex2D(_MainTex, v2f.uv[4]), _EdgeColor, edge);
// 纯色图上描边 计算纯色,纯色上描边
fixed4 onlyEdgeColor = lerp(_BackgroundColor, _EdgeColor, edge);
//通过程度变量 去控制 是纯色描边 还是 原始颜色描边 在两者之间 进行过渡
return lerp(withEdgeColor, onlyEdgeColor, _BackgroundExtent);
}
ENDCG
}
}
Fallback Off // 不使用任何备用着色器
}
Lesson62_EdgeDetection.cs
using UnityEngine;
public class Lesson62_EdgeDetection : PostEffectBase
{
public Color EdgeColor;
public Color BackgroundColor;
[Range(0, 1)] public float BackgroundExtent;
protected override void UpdateProperty()
{
if (material != null)
{
material.SetColor("_EdgeColor", EdgeColor);
material.SetColor("_BackgroundColor", BackgroundColor);
material.SetFloat("_BackgroundExtent", BackgroundExtent);
}
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com