🦔

URPのシェーダー入門

2023/11/13に公開

ベースとなるUnlitShaderを用意

UnlitShader
Shader "Custom/Unlit"
{
	Properties
	{
		[MainTexture] _MainTex ("Texture", 2D) = "white" {}
	}

	SubShader
	{
		Tags
		{
			"RenderPipeline"="UniversalPipeline"
			"RenderType"="Opaque"
		}
		Pass
		{
			Name "Universal Forward"
			Tags
			{
				"LightMode" = "UniversalForward"
			}

			Cull Back
			ZTest LEqual
			ZWrite On

			HLSLPROGRAM
			#pragma target 4.5
			#pragma vertex vert
			#pragma fragment frag

			#pragma multi_compile_instancing
			#pragma multi_compile _ DOTS_INSTANCING_ON

			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"


			struct appdata
			{
				float4 pos: POSITION;
				float2 uv : TEXCOORD0;
				UNITY_VERTEX_INPUT_INSTANCE_ID
			};

			struct v2f
			{
				float4 pos : SV_POSITION;
				float2 uv : TEXCOORD1;
				UNITY_VERTEX_INPUT_INSTANCE_ID
				UNITY_VERTEX_OUTPUT_STEREO
			};

			sampler2D _MainTex;

			CBUFFER_START(UnityPerMaterial)
			float4 _MainTex_ST;
			CBUFFER_END

			v2f vert(appdata i)
			{
				v2f o;
				UNITY_SETUP_INSTANCE_ID(i);
				UNITY_TRANSFER_INSTANCE_ID(i, o);
				VertexPositionInputs vInput = GetVertexPositionInputs(i.pos.xyz);
				o.pos = vInput.positionCS;
				o.uv = TRANSFORM_TEX(i.uv, _MainTex);
				return o;
			}

			half4 frag(v2f i) : SV_Target
			{
				UNITY_SETUP_INSTANCE_ID(i);
				half4 baseColor = tex2D(_MainTex, i.uv);
				return baseColor;
			}
			ENDHLSL
		}
	}
}

NormalMapを適用した版

NormalShader
Shader "Custom/Normal"
{
	Properties
	{
		[MainTexture] _MainTex ("Texture", 2D) = "white" {}
		[Normal] _NormalTex ("Normal map", 2D) = "bump" {}
		_Specular ("Specular", Range(1, 100)) = 0
	}

	SubShader
	{
		Tags
		{
			"RenderPipeline"="UniversalPipeline"
			"RenderType"="Opaque"
		}
		Pass
		{
			Name "Universal Forward"
			Tags
			{
				"LightMode" = "UniversalForward"
			}

			Cull Back
			ZTest LEqual
			ZWrite On

			HLSLPROGRAM
			#pragma target 4.5
			#pragma vertex vert
			#pragma fragment frag

			#pragma multi_compile_instancing
			#pragma multi_compile _ DOTS_INSTANCING_ON

			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl"


			struct appdata
			{
				float4 pos: POSITION;
				float2 uv : TEXCOORD0;
				half3 normal : NORMAL;
				half4 tangent : TANGENT;
				UNITY_VERTEX_INPUT_INSTANCE_ID
			};

			struct v2f
			{
				float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
				half3 normal : TEXCOORD1; // 法線
				half3 tangent : TEXCOORD2; // 接線
				half3 binormal : TEXCOORD3; // 従法線
				float4 worldPos : TEXCOORD4;
				UNITY_VERTEX_INPUT_INSTANCE_ID
				UNITY_VERTEX_OUTPUT_STEREO
			};

			sampler2D _MainTex;
			sampler2D _NormalTex;
			float _Specular;

			CBUFFER_START(UnityPerMaterial)
			float4 _MainTex_ST;
			CBUFFER_END

			v2f vert(appdata i)
			{
				v2f o;
				UNITY_SETUP_INSTANCE_ID(i);
				UNITY_TRANSFER_INSTANCE_ID(i, o);
				VertexPositionInputs vInput = GetVertexPositionInputs(i.pos.xyz);
				o.pos = vInput.positionCS;
				o.uv = TRANSFORM_TEX(i.uv, _MainTex);

				o.normal = TransformObjectToWorldNormal(i.normal);
				o.tangent = normalize(mul(unity_ObjectToWorld, i.tangent)).xyz;
				// 法線と接線から従法線を計算
				o.binormal = cross(i.normal, i.tangent) * i.tangent.w;
				o.binormal = normalize(mul(unity_ObjectToWorld, o.binormal));
				o.worldPos = i.pos;
				return o;
			}

			half4 frag(v2f i) : SV_Target
			{
				UNITY_SETUP_INSTANCE_ID(i);

				float3 lightDir = normalize(_MainLightPosition.xyz);
				half3 lightColor = _MainLightColor.xyz;

				half3 normalmap = UnpackNormal(tex2D(_NormalTex, i.uv));
				// 法線を合成
				float3 normal = (i.tangent * normalmap.x) + (i.binormal * normalmap.y) + (i.normal * normalmap.z);

				float t = dot(normal, lightDir);
				t = max(0, t); // t >= 0にする
				// 内積が0に近いほど黒く
				float3 diffuseLight = lightColor * t;

				// ライト方向と法線ベクトルから反射ベクトルを計算
				float3 reflectVec = reflect(-lightDir, normal);
				float3 eyeVec = normalize(GetCameraPositionWS() - i.worldPos); // 視線ベクトル

				t = dot(reflectVec, eyeVec);
				t = max(0, t); // t >= 0にする
				t = pow(t, _Specular);

				float3 specularLight = lightColor * t;

				float4 color = tex2D(_MainTex, i.uv);
				color.xyz *= (specularLight + diffuseLight);
				return color;
			}
			ENDHLSL
		}
	}
}

NormalMap適用した版で影を表示する

NormalShader
Shader "Custom/Normal"
{
	Properties
	{
		[MainTexture] _MainTex ("Texture", 2D) = "white" {}
		[Normal] _NormalTex ("Normal map", 2D) = "bump" {}
		_Specular ("Specular", Range(1, 100)) = 0
	}

	SubShader
	{
		Tags
		{
			"RenderPipeline"="UniversalPipeline"
			"RenderType"="Opaque"
		}
		Pass
		{
			Name "Universal Forward"
			Tags
			{
				"LightMode" = "UniversalForward"
			}

			Cull Back
			ZTest LEqual
			ZWrite On

			HLSLPROGRAM
			#pragma target 4.5
			#pragma vertex vert
			#pragma fragment frag

			#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
			#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE

			#pragma multi_compile_instancing
			#pragma multi_compile _ DOTS_INSTANCING_ON

			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"


			struct appdata
			{
				float4 pos: POSITION;
				float2 uv : TEXCOORD0;
				half3 normal : NORMAL;
				half4 tangent : TANGENT;
				UNITY_VERTEX_INPUT_INSTANCE_ID
			};

			struct v2f
			{
				float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
				half3 normal : TEXCOORD1; // 法線
				half3 tangent : TEXCOORD2; // 接線
				half3 binormal : TEXCOORD3; // 従法線
				float4 worldPos : TEXCOORD4;
				float4 shadowCoord : TEXCOORD5;
				UNITY_VERTEX_INPUT_INSTANCE_ID
				UNITY_VERTEX_OUTPUT_STEREO
			};

			sampler2D _MainTex;
			sampler2D _NormalTex;
			float _Specular;

			CBUFFER_START(UnityPerMaterial)
			float4 _MainTex_ST;
			CBUFFER_END

			v2f vert(appdata i)
			{
				v2f o;
				UNITY_SETUP_INSTANCE_ID(i);
				UNITY_TRANSFER_INSTANCE_ID(i, o);
				VertexPositionInputs vInput = GetVertexPositionInputs(i.pos.xyz);
				o.pos = vInput.positionCS;
				o.uv = TRANSFORM_TEX(i.uv, _MainTex);

				o.normal = TransformObjectToWorldNormal(i.normal);
				o.tangent = normalize(mul(unity_ObjectToWorld, i.tangent)).xyz;
				// 法線と接線から従法線を計算
				o.binormal = cross(i.normal, i.tangent) * i.tangent.w;
				o.binormal = normalize(mul(unity_ObjectToWorld, o.binormal));
				o.worldPos = i.pos;

				o.shadowCoord = GetShadowCoord(vInput);
				return o;
			}

			half4 frag(v2f i) : SV_Target
			{
				UNITY_SETUP_INSTANCE_ID(i);

				float3 lightDir = normalize(_MainLightPosition.xyz);
				half3 lightColor = _MainLightColor.xyz;

				half3 normalmap = UnpackNormal(tex2D(_NormalTex, i.uv));

				// 法線を合成
				float3 normal = (i.tangent * normalmap.x) + (i.binormal * normalmap.y) + (i.normal * normalmap.z);

				float t = dot(normal, lightDir);
				t = max(0, t); // t >= 0にする
				// 内積が0に近いほど黒く
				float3 diffuseLight = lightColor * t;

				// ライト方向と法線ベクトルから反射ベクトルを計算
				float3 reflectVec = reflect(-lightDir, normal);
				float3 eyeVec = normalize(GetCameraPositionWS() - i.worldPos); // 視線ベクトル

				t = dot(reflectVec, eyeVec);
				t = max(0, t); // t >= 0にする
				t = pow(t, _Specular);

				float3 specularLight = lightColor * t;

				Light light = GetMainLight(i.shadowCoord);
				half shadow = light.shadowAttenuation * light.distanceAttenuation;

				float4 color = tex2D(_MainTex, i.uv) * half4(shadow, shadow, shadow, 1);
				color.xyz *= (specularLight + diffuseLight);
				return color;
			}
			ENDHLSL
		}

		UsePass "Universal Render Pipeline/Lit/ShadowCaster"
	}
}

ライトの影響を受けるために以下の機能を追加

#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE

fragmentシェーダーにシャドウマップの情報を渡す

struct v2f
{
	float4 pos : SV_POSITION;
	float2 uv : TEXCOORD0;
	half3 normal : TEXCOORD1; // 法線
	half3 tangent : TEXCOORD2; // 接線
	half3 binormal : TEXCOORD3; // 従法線
	float4 worldPos : TEXCOORD4;
	float4 shadowCoord : TEXCOORD5;
	UNITY_VERTEX_INPUT_INSTANCE_ID
	UNITY_VERTEX_OUTPUT_STEREO
};

v2f vert(appdata i)
{
	v2f o;
	UNITY_SETUP_INSTANCE_ID(i);
	UNITY_TRANSFER_INSTANCE_ID(i, o);
	VertexPositionInputs vInput = GetVertexPositionInputs(i.pos.xyz);
	o.pos = vInput.positionCS;
	o.uv = TRANSFORM_TEX(i.uv, _MainTex);

	o.normal = TransformObjectToWorldNormal(i.normal);
	o.tangent = normalize(mul(unity_ObjectToWorld, i.tangent)).xyz;
	// 法線と接線から従法線を計算
	o.binormal = cross(i.normal, i.tangent) * i.tangent.w;
	o.binormal = normalize(mul(unity_ObjectToWorld, o.binormal));
	o.worldPos = i.pos;

	o.shadowCoord = GetShadowCoord(vInput);
	return o;
}

GetShadowCoord()を使用するためにLighting.hlslをインクルード

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

ShadowCasterのパスを追加

UsePass "Universal Render Pipeline/Lit/ShadowCaster"

Discussion