如何在Unity URP中利用Sobel添加屏幕空间深度边缘光

仔细观察原神中的人物,可以发现角色周围有一层白色的内描边边缘光:

如何在Unity URP中实现类似的效果呢?参考了Adrian Mendez大佬的Youtbue教程,我也尝试着用添加Render Feature的方式实现该效果。实现大致思路是使用Sobel算子对相机深度图像进行边缘检测:

根据大佬提供的思路写了一个Shader,大概就是在原图的基础上把边缘光再叠加上去:

Shader "Unlit/SobelRimLight"
{
	Properties
	{
		_MainTex("Texture", 2D) = "white" {}
		_ThicknessX("ThicknessX", Float) = 0.01
		_ThicknessY("ThicknessY", Float) = 0.01
		_MaxThickness("MaxThickness", Float) = 0.01
		_Intensity("Intensity", Range(0,1)) = 0.01
		_LerpValue("LerpValue", Range(0,1)) = 1
		_Distance("Distance", Float) = 1
		_Color("RimLightColor",Color) = (0,0,0)
	}
	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;
				float4 scrPos : TEXCOORD2;
				UNITY_FOG_COORDS(1)
				float4 vertex : SV_POSITION;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			float _ThicknessX;
			float _ThicknessY;
			float _MaxThickness;
			float _Intensity;
			float _LerpValue;
			float _Distance;
			sampler2D _CameraDepthTexture;
			float4 _CameraDepthTexture_ST;
			float4 _Color;

			static float2 sobelSamplePoints[9] = {
				float2(-1,1),float2(0,1),float2(1,1),
				float2(-1,0),float2(0,0),float2(1,0),
				float2(-1,-1),float2(0,-1),float2(1,-1),
			};

			static float sobelXMatrix[9] = {
				1,0,-1,
				2,0,-2,
				1,0,-1,
			};
			static float sobelYMatrix[9] = {
				1,2,1,
				0,0,0,
				-1,-2,-1,
			};

			v2f vert(appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				UNITY_TRANSFER_FOG(o,o.vertex);
				o.scrPos = ComputeScreenPos(o.vertex);
				return o;
			}

			fixed4 frag(v2f i) : SV_Target
			{
				float2 sobel = 0;
				//获得深度
				float originDepth = tex2D(_CameraDepthTexture, i.uv);
				//转化为线性0-1深度,这样0.5就表示处于相机到farPlane一半的位置了
				originDepth= Linear01Depth(originDepth);
				//找到该像素离相机的真实距离,_ProjectionParams.z表示_Camera_FarPlane的距离。https://docs.unity.cn/Packages/com.unity.shadergraph@6.9/manual/Camera-Node.html				
				float depthDistance = _ProjectionParams.z*originDepth;				
				float2 adaptiveThickness = float2(_ThicknessX, _ThicknessY);
				if (depthDistance <= 0)
					adaptiveThickness = float2(_MaxThickness, _MaxThickness);
				else 
				{//根据距离对边缘光厚度进行线性缩放
					adaptiveThickness = adaptiveThickness / depthDistance;
				}
				adaptiveThickness = min(adaptiveThickness, float2(_MaxThickness, _MaxThickness));

				
				for (int id = 0; id < 9; id++) 
				{
					float2 screenPos = i.uv + sobelSamplePoints[id] * adaptiveThickness;					
					float depth = tex2D(_CameraDepthTexture, screenPos);					
					depth = Linear01Depth(depth);

					sobel += depth * float2(sobelXMatrix[id], sobelYMatrix[id]);					
				}		
				fixed4 previousColor = tex2D(_MainTex, i.uv);
				fixed4 rimLightColor = _Color * step(_Distance,length(sobel)*_ProjectionParams.z);

				//叠加到原来的颜色上
				fixed4 col = previousColor + lerp(previousColor*rimLightColor, rimLightColor, _LerpValue) * _Intensity;

				return col;
			}
			ENDCG
		}
	}
}

由于是专门用来后处理的Shader,使用的话先创建一个材质球,可以看到有如下参数(本菜鸟自己改写的,参数定义可能不太符合规范):

ThicknessX和Y分别控制横向和纵向的边缘光宽度,MaxThickness控制最大边缘光宽度;Intensity控制边缘光的强度;LerpValue越大,边缘光颜色就越像设置的RimLightColor的颜色;Distance控制边缘的深度阈值。不过这种方法计算的边缘光对distance阈值的设置比较敏感,如果过大的话会导致物体重叠处的边缘光消失。比如自我遮挡的时候深度变化比较小时就没有边缘光了,设置太小的话就感觉有点像基于深度的描边。另外我看原神里的貌似竖直方向几乎没有边缘光,所以把ThicknessY设置为0了,可能是为了避免角色鞋底出现所谓边缘光的缘故吧...

这里还是像之前一样使用添加Blit Renderer Feature的方式,先把相关代码从Github弄下来,然后可以在UniversalRenderPipelineAsset_Renderer上点击Add Renderer Feature添加一个Blit Feature,再把材质拖进去就行了:

效果对比,用Sobel边缘光应该能让人物看上去更加精致?

不知道是不是好看了一些,但原神都有这样的效果那肯定是合理的。

传统N dot V的方法特定视角下会出现看上去不太正常的大片边缘光,Sobel就不会有这个问题。但由于是基于深度的屏幕后处理,Sobel会有物体重叠时边缘光消失的现象:

反正我觉得Sobel边缘光效果更好看一点点,大不了也可以两个一起用...

参考链接

Genshin Impact Character Shader Breakdown Unity URP
【JTRP】屏幕空间深度边缘光 Screen Space Depth Rimlight
如何在Unity URP中自定义ToneMapping
URP_BlitRenderFeature

posted @ 2022-05-28 14:41  菜鸟少侠  阅读(1320)  评论(0编辑  收藏  举报