Editor/templates/DeferredDecal.cs

An editor-only shader template for a deferred decal, represented as a C# static class that exposes a Features dictionary and a Code string containing an HLSL shader template with placeholders. It defines which decal features are supported and supplies the shader source used by the editor ShaderGraphExtras.

File Access
namespace Editor.ShaderGraphExtras;
public static class SGEDeferredDecalTemplate
{
	public static Dictionary<string, bool> Features => new()
	{
		{ "SupportsAlbedo", true},
		{ "SupportsEmission", true },
		{ "SupportsOpacity", true},
		{ "SupportsNormal", true },
		{ "SupportsRoughness", true },
		{ "SupportsMetalness", true },
		{ "SupportsAmbientOcclusion", true },
		{ "SupportsPositionOffset", false },
		{ "SupportsPixelDepthOffset", false},

        {"SupportsLitShadingModel", true},
		{"SupportsUnlitShadingModel", true},
		{"SupportsCustomShadingModel", true},

		{"SupportsOpaqueBlendMode", false},
		{"SupportsMaskedBlendMode", false},
		{"SupportsTranslucentBlendMode", true},
		{"SupportsDynamicBlendMode", false},
		{"SupportsCustomBlendMode", false},
	};

	public static string Code => @"
HEADER
{{
	Description = ""{0}"";
}}

FEATURES
{{
	#include ""common/features.hlsl""
{1}
}}

MODES
{{
	Forward();
	Depth();
	ToolsShadingComplexity( ""tools_shading_complexity.shader"" );
}}

COMMON
{{
{2}
	#include ""common/shared.hlsl""
	#include ""procedural.hlsl""
}}

struct VertexInput
{{
	#include ""common/vertexinput.hlsl""
	float4 vColor : COLOR0 < Semantic( Color ); >;
{3}
}};

struct PixelInput
{{
	#include ""common/pixelinput.hlsl""
	float3 vDecalOrigin : TEXCOORD14;
	float3 vDecalRight : TEXCOORD15;
	float3 vDecalUp : TEXCOORD16;
	float3 vDecalForward : TEXCOORD17;
	float3 vDecalScale : TEXCOORD18;
	float3 vPositionOs : TEXCOORD19;
	float4 vColor : COLOR0;
	float4 vTintColor : COLOR1;
{4}
}};

VS
{{
	#include ""common/vertex.hlsl""
	#include ""instancing.fxc""
{5}{6}{7}
	PixelInput MainVs( VertexInput v )
	{{
		float3x4 matObjectToWorld = GetTransformMatrix( v.nInstanceTransformID );

		float3 vRight = mul( matObjectToWorld, float4( 1, 0, 0, 0 ) );
		float3 vUp = mul( matObjectToWorld, float4( 0, 1, 0, 0 ) );
		float3 vForward = mul( matObjectToWorld, float4( 0, 0, 1, 0 ) );

		float3 vScale = float3( length( vRight ), length( vUp ), length( vForward ) );

		float3 vBoundsHalfSize = abs( v.vPositionOs.xyz );

		vBoundsHalfSize *= vScale;

		float projectionDepth = 1;
		float3 vScaledPositionOs = v.vPositionOs.xyz;
		vScaledPositionOs.x *= projectionDepth;
		vBoundsHalfSize.x *= projectionDepth;

		float3 vPositionWs = mul( matObjectToWorld, float4( vScaledPositionOs, 1.0 ) );

		PixelInput i = ProcessVertex( v );

		i.vPositionOs = v.vPositionOs.xyz;
		i.vColor = v.vColor;

		ExtraShaderData_t extraShaderData = GetExtraPerInstanceShaderData( v.nInstanceTransformID );
		i.vTintColor = extraShaderData.vTint;

		i.vPositionPs = Position3WsToPs( vPositionWs );

		i.vDecalOrigin = mul( matObjectToWorld, float4( 0, 0, 0, 1.0 ) );

		i.vDecalScale = vBoundsHalfSize;

		i.vDecalRight = vRight / vScale.x;
		i.vDecalUp = vUp / vScale.y;
		i.vDecalForward = vForward / vScale.z;
{8}
		return FinalizeVertex( i );
	}}
}}

PS
{{
	#include ""common/pixel.hlsl""
	#include ""common/classes/Depth.hlsl""
{9}{10}{11}
	RenderState( BlendEnable, true );
	RenderState( SrcBlend, SRC_ALPHA );
	RenderState( DstBlend, INV_SRC_ALPHA );
	RenderState( DepthWriteEnable, false );
	RenderState( DepthEnable, false );
	RenderState( CullMode, Front );

	float4 MainPs( PixelInput i ) : SV_Target0
	{{
		float3 worldPos = Depth::GetWorldPosition( i.vPositionSs.xy );

		float3 vDelta = worldPos - i.vDecalOrigin;

		float3 vProjected = float3( dot( vDelta, i.vDecalRight ), dot( vDelta, i.vDecalUp ), dot( vDelta, i.vDecalForward ) );

		float3 vHalfBounds = i.vDecalScale;
		float3 vDecalBounds = vHalfBounds * 2.0;

		if( abs( vProjected.y ) > vHalfBounds.y || abs( vProjected.z ) > vHalfBounds.z )
			discard;

		float distanceFromBoxStart = vProjected.x + vHalfBounds.x;
		if( distanceFromBoxStart < 0.0 || distanceFromBoxStart > vDecalBounds.x )
			discard;

		float2 decalUV = (vProjected.yz / vDecalBounds.yz) + 0.5;

		i.vTextureCoords.xy = 1.0 - decalUV;

		i.vNormalWs = -i.vDecalRight;
		i.vTangentUWs = -i.vDecalUp;
		i.vTangentVWs = -i.vDecalForward;

		float3 rotatedY = float3( -vProjected.z, vProjected.y, vProjected.x );
		i.vPositionOs = float3( -rotatedY.x, -rotatedY.y, rotatedY.z );

		i.vPositionWithOffsetWs.xyz = worldPos - g_vHighPrecisionLightingOffsetWs.xyz;

		Material m = Material::Init( i );
		m.Albedo = float3( 1, 1, 1 );
		m.Normal = float3( 0, 0, 1 );
		m.Roughness = 1;
		m.Metalness = 0;
		m.AmbientOcclusion = 1;
		m.TintMask = 1;
		m.Opacity = 1;
		m.Emission = float3( 0, 0, 0 );
		m.Transmission = 0;
{12}
		m.AmbientOcclusion = saturate( m.AmbientOcclusion );
		m.Roughness = saturate( m.Roughness );
		m.Metalness = saturate( m.Metalness );
		m.Opacity = saturate( m.Opacity );

		// Toolvis:
		m.WorldTangentU = i.vTangentUWs;
		m.WorldTangentV = i.vTangentVWs;
		m.TextureCoords = i.vTextureCoords.xy;

{13}
	}}
}}";
}