您现在的位置是:首页 >学无止境 >URP的多Pass和Features用法网站首页学无止境

URP的多Pass和Features用法

阿赵3D 2024-06-24 12:01:02
简介URP的多Pass和Features用法

回到目录

大家好,我是阿赵。这里用一个传统的描边例子来说明一下,URP下怎么使用多Pass和Features。

一、传统多Pass描边

最常用的制作描边方法,就是写多一个Cull Front的Pass,然后通过法线方向扩展顶点,模拟描边的效果。
如果用HLSL来写,代码会是这样的:

Shader "azhao/UnlitOutlineBase"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
		_outlineLen("outlineLen",float) = 0.02
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100


        Pass
        {
			Cull Back
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/core.hlsl"


            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 pos : SV_POSITION;
            };
			CBUFFER_START(UnityPerMaterial)
            sampler2D _MainTex;
            float4 _MainTex_ST;
			CBUFFER_END
            v2f vert (appdata v)
            {
                v2f o;
				VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz);
				o.pos = vertexInput.positionCS;
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                // sample the texture
				half4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDHLSL
        }

		Pass
		{
			Cull Front
			HLSLPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/core.hlsl"
			struct appdata
			{
				float4 vertex : POSITION;
				float3 normal :NORMAL;
			};

			struct v2f
			{
				float4 pos : SV_POSITION;
			};
			CBUFFER_START(UnityPerMaterial)
			float _outlineLen;
			CBUFFER_END
			v2f vert(appdata v)
			{
				v2f o;
				float4 pos = mul(UNITY_MATRIX_MV, v.vertex);
				float3 normal = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);
				normal.z = -0.5;
				pos = pos + float4(normalize(normal), 0) * _outlineLen;
				o.pos = mul(UNITY_MATRIX_P, pos);


				return o;
			}

			half4 frag(v2f i) : SV_Target
			{
				return half4(0,0,0,1);
			}
		ENDHLSL
	}
    }
}

在非URP的情况下,这个Shader可以正常的显示描边:
在这里插入图片描述

二、在URP下的多Pass情况

接下来,我们把项目改成URP渲染,具体方法可以参考之前的文章
Unity里URP项目的介绍和创建

切换完成后,会发现刚才那个shader不能正常显示描边了
在这里插入图片描述

这是因为,URP本身不鼓励在一个Shader里面写多Pass,所以如果不指定LightMode的情况下,只有第一个Pass生效,其他的Pass都不会生效。
知道了原因之后,那么给Pass加上Tags,应该就能解决这个问题。

Shader "azhao/UnlitOutlineMulPass"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
		_outlineLen("outlineLen",float) = 0.2
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100


        Pass
        {
			Tags{ "LightMode" = "LightweightForward" }
			Cull Back
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/core.hlsl"


            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 pos : SV_POSITION;
            };
			CBUFFER_START(UnityPerMaterial)
            sampler2D _MainTex;
            float4 _MainTex_ST;
			CBUFFER_END
            v2f vert (appdata v)
            {
                v2f o;
				VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz);
				o.pos = vertexInput.positionCS;
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                // sample the texture
				half4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDHLSL
        }

		Pass
		{
			Tags{ "LightMode" = "SRPDefaultUnlit" }
			Cull Front
			HLSLPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/core.hlsl"
			struct appdata
			{
				float4 vertex : POSITION;
				float3 normal :NORMAL;
			};

			struct v2f
			{
				float4 pos : SV_POSITION;
			};
			CBUFFER_START(UnityPerMaterial)
			float _outlineLen;
			CBUFFER_END
			v2f vert(appdata v)
			{
				v2f o;
				float4 pos = mul(UNITY_MATRIX_MV, v.vertex);
				float3 normal = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);
				normal.z = -0.5;
				pos = pos + float4(normalize(normal), 0) * _outlineLen;
				o.pos = mul(UNITY_MATRIX_P, pos);


				return o;
			}

			half4 frag(v2f i) : SV_Target
			{
				return half4(0,0,0,1);
			}
		ENDHLSL
	}
    }
}

可以看到,在第一个Pass加的是

Tags{ "LightMode" = "LightweightForward" }

而在第二个Pass加的是

Tags{ "LightMode" = "SRPDefaultUnlit" }

从URP的代码看

    m_ShaderTagIdList.Add(new ShaderTagId("UniversalForward"));
    m_ShaderTagIdList.Add(new ShaderTagId("LightweightForward"));
    m_ShaderTagIdList.Add(new ShaderTagId("SRPDefaultUnlit"));

应该来说,用UniversalForward、LightweightForward或者SRPDefaultUnlit都是可以的。
加完Tags之后,刚才那个Shader在URP渲染管线下,也能正常的渲染出描边了。

三、使用URP的Features

之前说过,URP本身并不鼓励在Shader里面写多Pass。这是因为URP的中心思想都是尽量用同一个Pass渲染尽量多的东西。所以其实我们通过Tags来强制开启多Pass,本身也不太符合URP的思想。
为了解决这个问题,URP提供了一个叫做Features的功能。
在这里插入图片描述

接下来我们就来用一下这个功能。

回到URP的Asset_Renderer文件的设置项,增加一个叫做outline的Feature
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在新建的Feature里面,指定了需要渲染的Layer,我这里新建了一个叫做outline的Layer。然后把刚才用于渲染带描边的小球的多个Pass的材质球拖到Overrides里面,然后指定Pass Index为1。
指定材质球并指定Pass这一步,其实是可以在一个多Pass的Shader里面指定用哪一个Pass作为这个Feature的渲染。
如果我们不用之前的Shader,单纯写一个只有一个描边Pass的Shader,也是可以的,比如

Shader "azhao/UnlitOutlineOnly"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
		_outlineLen("outlineLen",float) = 0.2
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

		Pass
		{
			Cull Front
			HLSLPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/core.hlsl"
			struct appdata
			{
				float4 vertex : POSITION;
				float3 normal :NORMAL;
			};

			struct v2f
			{
				float4 pos : SV_POSITION;
			};
			CBUFFER_START(UnityPerMaterial)
			float _outlineLen;
			CBUFFER_END
			v2f vert(appdata v)
			{
				v2f o;
				float4 pos = mul(UNITY_MATRIX_MV, v.vertex);
				float3 normal = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);
				normal.z = -0.5;
				pos = pos + float4(normalize(normal), 0) * _outlineLen;
				o.pos = mul(UNITY_MATRIX_P, pos);


				return o;
			}

			half4 frag(v2f i) : SV_Target
			{
				return half4(0,0,0,1);
			}
		ENDHLSL
	}
    }
}

那么Feature里面的设置就是
在这里插入图片描述

然后,回到小球身上,随便给一个默认的材质球给小球就行了,然后把小球的Layer设置成刚才新建的outline。这个时候
在这里插入图片描述

可以看到,小球是使用了默认的材质去渲染,只是额外叠加了一个描边的效果。
这样的处理就很符合URP的思想了,指定一个层,不管这个层原来的Shader是怎样显示的,都统一额外的用一个Pass,用于显示描边。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。