// BlendUnlit.shader // Soullies - Hand-written URP 17 Unlit Sprite/Mesh Shader // Derived from TrackShader (ASE), rewritten for clarity and extensibility. // // Core Feature: Base Color * Texture, optionally multiplied by an HDR Emission Color, // allowing illuminated objects to retain proper transparency. // New Feature : Runtime-selectable Blend Mode (Alpha, Additive, Multiply, Premultiplied). Shader "Soullies/BlendUnlit" { Properties { [Header(Texture)] _MainTex ("Main Texture", 2D) = "white" {} [Space(8)] [Header(Color)] [MainColor] _BaseColor ("Base Color", Color) = (1, 1, 1, 1) [Space(8)] [Header(Emission)] [Toggle] _EnableEmission ("Enable Emission", Float) = 0 [HDR] _EmissionColor ("Emission Color (HDR)", Color) = (0, 0, 0, 1) [Space(8)] [Header(Alpha Source)] // When toggled ON : uses the Red channel of the texture as Alpha (for single-channel masks). // When toggled OFF : uses the A channel of the texture (standard RGBA sprite). [Toggle] _UseRedAsAlpha ("Use Red Channel as Alpha", Float) = 0 [Space(8)] [Header(Blend Mode)] // 0 = Alpha Blend (SrcAlpha, OneMinusSrcAlpha) – standard transparent // 1 = Additive (SrcAlpha, One) – add light, black = invisible // 2 = Multiply (DstColor, Zero) – darken underlying pixels // 3 = Premultiplied(One, OneMinusSrcAlpha) – for pre-multiplied alpha textures [KeywordEnum(Alpha, Additive, Multiply, Premultiplied)] _BlendMode ("Blend Mode", Float) = 0 [Space(8)] [Header(Render State)] [Enum(UnityEngine.Rendering.CullMode)] _CullMode ("Cull Mode", Float) = 2 // Back [Enum(Off, 0, On, 1)] _ZWrite ("Z Write", Float) = 0 // Internal blend equation floats – managed by BlendUnlitShaderGUI. // Default values = Alpha blend: SrcAlpha(5), OneMinusSrcAlpha(10). // MUST be declared here so Unity serialises them into the .mat asset // and they survive domain reloads / Ctrl+S without resetting to 0(Zero,Zero=black). [HideInInspector] _SrcBlendRGB ("Src Blend (Internal)", Float) = 5 [HideInInspector] _DstBlendRGB ("Dst Blend (Internal)", Float) = 10 } SubShader { Tags { "RenderPipeline" = "UniversalPipeline" "RenderType" = "Transparent" "Queue" = "Transparent" "UniversalMaterialType" = "Unlit" "IgnoreProjector" = "True" "PreviewType" = "Plane" "CanUseSpriteAtlas" = "True" } // --------------------------------------------------------------------------- // Blend equations for each mode (controlled by shader_feature variants): // Alpha : SrcAlpha, OneMinusSrcAlpha (standard transparency) // Additive : SrcAlpha, One (screen-style additive, black is invisible) // Multiply : DstColor, Zero (multiply blend, darkens below) // Premultiplied : One, OneMinusSrcAlpha (for pre-multiplied textures/HDR) // --------------------------------------------------------------------------- Cull [_CullMode] ZWrite [_ZWrite] ZTest LEqual // --------------------------------------------------------------------------- // Pass 1 – Universal 2D (sprite renderer main path) // --------------------------------------------------------------------------- Pass { Name "BlendUnlit_Universal2D" Tags { "LightMode" = "Universal2D" } // Blend state driven by shader_feature variant Blend [_SrcBlendRGB] [_DstBlendRGB] HLSLPROGRAM #pragma target 3.5 #pragma prefer_hlslcc gles #pragma vertex vert #pragma fragment frag #pragma multi_compile_instancing #pragma multi_compile_vertex _ SKINNED_SPRITE // Blend mode variants (local_fragment: blend equation changes are per-draw, not per-pass) #pragma shader_feature_local _BLENDMODE_ALPHA _BLENDMODE_ADDITIVE _BLENDMODE_MULTIPLY _BLENDMODE_PREMULTIPLIED #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/Shaders/2D/Include/Core2D.hlsl" // ----------------------------------------------------------------------- // Shared CBUFFER (SRP Batcher compatible) // ----------------------------------------------------------------------- TEXTURE2D(_MainTex); SAMPLER(sampler_MainTex); CBUFFER_START(UnityPerMaterial) float4 _MainTex_ST; half4 _BaseColor; half4 _EmissionColor; float _EnableEmission; float _UseRedAsAlpha; float _ZWrite; float _SrcBlendRGB; float _DstBlendRGB; CBUFFER_END // ----------------------------------------------------------------------- // Vertex / Fragment structs // ----------------------------------------------------------------------- struct Attributes { float3 positionOS : POSITION; float2 uv : TEXCOORD0; half4 color : COLOR; UNITY_SKINNED_VERTEX_INPUTS UNITY_VERTEX_INPUT_INSTANCE_ID }; struct Varyings { float4 positionCS : SV_POSITION; float2 uv : TEXCOORD0; half4 color : COLOR; UNITY_VERTEX_INPUT_INSTANCE_ID UNITY_VERTEX_OUTPUT_STEREO }; // ----------------------------------------------------------------------- // Shared pixel logic (inlined as a function to avoid copy-paste) // ----------------------------------------------------------------------- half4 ComputeColor(float2 uv, half4 vertexColor) { half4 texSample = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv); // Alpha channel selection (Lerp instead of macro avoids variant stripping issues) half texAlpha = lerp(texSample.a, texSample.r, _UseRedAsAlpha); // Reconstruct colour from texture half4 texColor = half4(texSample.rgb, texAlpha); // Base colour multiply (tint) half4 color = texColor * _BaseColor; // Emission multiply half4 emissionMult = lerp(half4(1, 1, 1, 1), _EmissionColor, _EnableEmission); color *= emissionMult; // Vertex color (Sprite tint / SpriteRenderer.color) color *= vertexColor; return color; } // ----------------------------------------------------------------------- // Vertex Shader // ----------------------------------------------------------------------- Varyings vert(Attributes IN) { Varyings OUT = (Varyings)0; UNITY_SETUP_INSTANCE_ID(IN); UNITY_TRANSFER_INSTANCE_ID(IN, OUT); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT); UNITY_SKINNED_VERTEX_COMPUTE(IN); // Respect SpriteRenderer Flip X/Y IN.positionOS = UnityFlipSprite(IN.positionOS, unity_SpriteProps.xy); VertexPositionInputs vpi = GetVertexPositionInputs(IN.positionOS); OUT.positionCS = vpi.positionCS; OUT.uv = TRANSFORM_TEX(IN.uv, _MainTex); OUT.color = IN.color * unity_SpriteColor; return OUT; } // ----------------------------------------------------------------------- // Fragment Shader // ----------------------------------------------------------------------- half4 frag(Varyings IN) : SV_Target { UNITY_SETUP_INSTANCE_ID(IN); UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(IN); return ComputeColor(IN.uv, IN.color); } ENDHLSL } // --------------------------------------------------------------------------- // Pass 2 – Universal Forward (MeshRenderer / 3D object fallback path) // --------------------------------------------------------------------------- Pass { Name "BlendUnlit_Forward" Tags { "LightMode" = "UniversalForwardOnly" } Blend [_SrcBlendRGB] [_DstBlendRGB] HLSLPROGRAM #pragma target 3.5 #pragma prefer_hlslcc gles #pragma vertex vert #pragma fragment frag #pragma multi_compile_instancing #pragma multi_compile_fog #pragma shader_feature_local _BLENDMODE_ALPHA _BLENDMODE_ADDITIVE _BLENDMODE_MULTIPLY _BLENDMODE_PREMULTIPLIED #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/Shaders/2D/Include/Core2D.hlsl" TEXTURE2D(_MainTex); SAMPLER(sampler_MainTex); CBUFFER_START(UnityPerMaterial) float4 _MainTex_ST; half4 _BaseColor; half4 _EmissionColor; float _EnableEmission; float _UseRedAsAlpha; float _ZWrite; float _SrcBlendRGB; float _DstBlendRGB; CBUFFER_END struct Attributes { float3 positionOS : POSITION; float2 uv : TEXCOORD0; half4 color : COLOR; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct Varyings { float4 positionCS : SV_POSITION; float2 uv : TEXCOORD0; half4 color : COLOR; half fogFactor : TEXCOORD1; UNITY_VERTEX_INPUT_INSTANCE_ID UNITY_VERTEX_OUTPUT_STEREO }; half4 ComputeColor(float2 uv, half4 vertexColor) { half4 texSample = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv); half texAlpha = lerp(texSample.a, texSample.r, _UseRedAsAlpha); half4 texColor = half4(texSample.rgb, texAlpha); half4 color = texColor * _BaseColor; half4 emissionMult = lerp(half4(1, 1, 1, 1), _EmissionColor, _EnableEmission); color *= emissionMult; color *= vertexColor; return color; } Varyings vert(Attributes IN) { Varyings OUT = (Varyings)0; UNITY_SETUP_INSTANCE_ID(IN); UNITY_TRANSFER_INSTANCE_ID(IN, OUT); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT); VertexPositionInputs vpi = GetVertexPositionInputs(IN.positionOS); OUT.positionCS = vpi.positionCS; OUT.uv = TRANSFORM_TEX(IN.uv, _MainTex); OUT.color = IN.color; OUT.fogFactor = ComputeFogFactor(vpi.positionCS.z); return OUT; } half4 frag(Varyings IN) : SV_Target { UNITY_SETUP_INSTANCE_ID(IN); UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(IN); half4 color = ComputeColor(IN.uv, IN.color); color.rgb = MixFog(color.rgb, IN.fogFactor); return color; } ENDHLSL } // --------------------------------------------------------------------------- // Pass 3 – Scene Selection (Editor picking highlight) // --------------------------------------------------------------------------- Pass { Name "SceneSelectionPass" Tags { "LightMode" = "SceneSelectionPass" } Cull Off HLSLPROGRAM #pragma target 3.5 #pragma vertex vert #pragma fragment frag #pragma multi_compile_instancing #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" CBUFFER_START(UnityPerMaterial) float4 _MainTex_ST; half4 _BaseColor; half4 _EmissionColor; float _ZWrite; float _SrcBlendRGB; float _DstBlendRGB; CBUFFER_END int _ObjectId; int _PassValue; struct Attributes { float3 positionOS : POSITION; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct Varyings { float4 positionCS : SV_POSITION; UNITY_VERTEX_INPUT_INSTANCE_ID }; Varyings vert(Attributes IN) { Varyings OUT = (Varyings)0; UNITY_SETUP_INSTANCE_ID(IN); UNITY_TRANSFER_INSTANCE_ID(IN, OUT); OUT.positionCS = TransformObjectToHClip(IN.positionOS); return OUT; } half4 frag(Varyings IN) : SV_Target { return half4(_ObjectId, _PassValue, 1.0, 1.0); } ENDHLSL } // --------------------------------------------------------------------------- // Pass 4 – Scene Picking (Editor object picking) // --------------------------------------------------------------------------- Pass { Name "ScenePickingPass" Tags { "LightMode" = "Picking" } Cull Off HLSLPROGRAM #pragma target 3.5 #pragma vertex vert #pragma fragment frag #pragma multi_compile_instancing #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" CBUFFER_START(UnityPerMaterial) float4 _MainTex_ST; half4 _BaseColor; half4 _EmissionColor; float _ZWrite; float _SrcBlendRGB; float _DstBlendRGB; CBUFFER_END float4 _SelectionID; struct Attributes { float3 positionOS : POSITION; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct Varyings { float4 positionCS : SV_POSITION; UNITY_VERTEX_INPUT_INSTANCE_ID }; Varyings vert(Attributes IN) { Varyings OUT = (Varyings)0; UNITY_SETUP_INSTANCE_ID(IN); UNITY_TRANSFER_INSTANCE_ID(IN, OUT); OUT.positionCS = TransformObjectToHClip(IN.positionOS); return OUT; } half4 frag(Varyings IN) : SV_Target { return unity_SelectionID; } ENDHLSL } } // --------------------------------------------------------------------------- // Custom Editor: drives the Blend Mode property → actual GPU Blend state // --------------------------------------------------------------------------- CustomEditor "BlendUnlitShaderGUI" FallBack "Hidden/Universal Render Pipeline/FallbackError" }