US4L2——模型描边效果
US4L2——模型描边效果
知识回顾 —— 边缘检测
边缘检测效果,是一种用于突出图像中的边缘,使物体的轮廓更加明显的图像处理技术。
我们目前学习的实现方式有两种,都是基于屏幕后期处理效果的:
基于卷积运算的:
用像素周围的9个像素的灰度值和Sobel算子进行卷积计算得到梯度值决定是否在边缘上
基于深度+法线纹理的:
基于Roberts交叉算子,通过比较对角线上的的像素的深度和法线值,判断是否在边缘上
这两种方式会对屏幕图像中每个像素产生影响,主要是针对全局性效果的,一个用于2D图像,一个用于3D场景
模型描边效果
在 Unity 中,模型描边效果也可以称为边缘描边、轮廓描边、轮廓线效果。
它实现出来的效果类似边缘检测,主要是为了让单个 3D 模型产生描边效果,使模型的轮廓更加突出。
这种效果一般用在卡通渲染、手绘风格、漫画风格的游戏中,还可以用来制作对象选中效果。
模型描边效果的基本原理
一句话描述模型描边效果的基本原理:
两个 Pass
渲染对象,一个 Pass
用于渲染沿法线方向放大的模型,一个 Pass
用于正常渲染正常模型。
相当于先用纯色渲染一次放大后的模型,再用模型本来的颜色覆盖重合部分
关键点:
-
如何放大模型
在第一个
Pass
中,我们可以在顶点着色器函数中,将顶点沿着法线方向进行偏移(相当于膨胀了模型),
偏移的距离可以是一个自定义参数,它可以用来控制边缘线的粗细。
在片元着色器函数中直接返回一个自定义颜色即可,它决定了边缘线的颜色。注意:此方案对于棱角分明的几何体(例如立方体)效果较差,更好最简单的方法是采用外壳模型(也就是额外使用一个更大的模型)的方案
-
如何覆盖重合部分
在第二个
Pass
中,我们只需要正常按需求(是否受光照影响等)渲染模型即可,
但是正常情况下,放大的部分会直接挡住正常大小的模型内容,这是因为第二个Pass
的渲染的重合部分无法通过深度写入,直接被抛弃了
要解决这个问题很简单,直接让第一个
Pass
关闭深度写入即可,这样重合部分,就会显示为第二个 Pass
绘制的结果
总结:使用两个 Pass
渲染对象,一个 Pass
用于渲染沿法线方向放大的模型,一个 Pass
用于正常渲染正常模型。
相当于先用纯色渲染一次放大后的模型,再用模型本来的颜色覆盖重合部分。
为了让重合部分能够正常显示为第二个 Pass
的渲染内容,需要关闭第一个 Pass
的深度写入!
解决关闭深度写入带来的问题
虽然关闭第一个 Pass
的深度写入可以帮助我们实现出想要的效果,但是它会带来一个问题,就是物体后方的内容会覆盖掉边缘线!
解决这个问题的方案是:设置该模型描边效果 Shader 的渲染队列为 Transparent,让它晚于几何体进行渲染
模型描边效果 具体实现
-
新建 Shader 取名
OutLine
删除无用代码
-
属性声明
- 边缘线颜色
_OutLineColor
- 边缘线粗细
_OutLineWidth
1
2
3
4
5
6Properties
{
_MainTex ("Texture", 2D) = "white"{}
_OutLineColor("OutLineColor", Color) = (1,1,1,1)
_OutLineWidth("OutLineWidth", Float) = 0.01
} - 边缘线颜色
-
修改渲染队列为
Transparent
1
2
3
4
5SubShader
{
Tags { "RenderType"="Opaque" "Queue"="Transparent" }
// ...
} -
复制一个
Pass
-
编写第一个
Pass
-
关闭深度写入
1
2
3
4
5
6
7
8Pass
{
ZWrite Off // 关闭深度写入,目的是让第二个Pass能够覆盖重合的地方
CGPROGRAM
//...
ENDCG
} -
属性映射
-
结构体相关
不需要纹理坐标,因为描边颜色是固定的,不需要采样
1
2
3
4
5
6
7
8struct v2f
{
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
fixed4 _OutLineColor; // 边缘线颜色
fixed _OutLineWidth; // 边缘线粗细 -
顶点着色器
顶点朝法线方向进行偏移后再转换到裁剪空间
1
2
3
4
5
6
7
8v2f vert (appdata_base v)
{
v2f o;
// 偏移顶点位置,朝法线方向偏移
float4 newVertex = v.vertex + float4(normalize(v.normal) * _OutLineWidth, 0);
o.vertex = UnityObjectToClipPos(newVertex); // 顶点转换到裁剪空间
return o;
} -
片元着色器
直接返回边缘线颜色
1
2
3
4fixed4 frag (v2f i) : SV_Target
{
return _OutLineColor;
}
-
-
编写第二个
Pass
编写传统的纹理采样
Pass
进行测试即可(如果有光照相关的需求 自定添加即可)这里直接复用标准漫反射的两个Pass,代码详见:US3S7L1——标准漫反射Shader
1
2
3
4
5// 基础渲染通道 Base Pass
UsePass "TeachShader/BumpedDiffuse/BasePass"
// 附加渲染通道 Additional Pass
UsePass "TeachShader/BumpedDiffuse/AdditionalPass" -
FallBack "Diffuse"
完整 Shader 如下:
1 | Shader "TeachShader/OutLine" |
显示效果(边缘线颜色为白色,边缘线粗细为0.01):
可见,模型边缘显示出了白色的描边