Shader "GAME/BuildGhost" { Properties { [Header(Ghost Tint set to green when placeable red when blocked)][Space] [HDR] _BaseColor("Ghost Color", Color) = (0.2, 1.0, 0.35, 1) _FillOpacity("Fill Opacity (body see through amount)", Range(0,1)) = 0.15 [Header(Fresnel Rim glowing silhouette edge)][Space] _RimPower("Rim Power (higher = thinner edge)", Range(0.5,12)) = 2.5 _RimIntensity("Rim Intensity", Range(0,8)) = 2.5 [Header(Scan Pulse gentle breathing glow)][Space] _PulseSpeed("Pulse Speed", Float) = 2.5 _PulseMin("Pulse Min", Range(0,2)) = 0.7 _PulseMax("Pulse Max", Range(0,2)) = 1.15 [Header(Rendering)][Space] [Enum(UnityEngine.Rendering.CullMode)] _Cull("Cull", Float) = 2 } SubShader { Tags { "RenderType" = "Transparent" "RenderPipeline" = "UniversalPipeline" "Queue" = "Transparent" "IgnoreProjector" = "True" } // Shared declarations for every pass. HLSLINCLUDE #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" CBUFFER_START(UnityPerMaterial) half4 _BaseColor; half _FillOpacity; half _RimPower; half _RimIntensity; float _PulseSpeed; half _PulseMin; half _PulseMax; CBUFFER_END ENDHLSL // ===================================================================== // Ghost — unlit, additive-ish transparent body with a bright fresnel // rim so the silhouette reads clearly. The whole look is driven by the // single _BaseColor tint, so placement code just swaps green <-> red. // ===================================================================== Pass { Name "Ghost" Tags { "LightMode" = "UniversalForward" } Blend SrcAlpha OneMinusSrcAlpha ZWrite Off ZTest LEqual Cull [_Cull] HLSLPROGRAM #pragma target 3.0 #pragma vertex GhostVertex #pragma fragment GhostFragment #pragma multi_compile_instancing #pragma multi_compile_fog struct Attributes { float4 positionOS : POSITION; float3 normalOS : NORMAL; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct Varyings { float4 positionCS : SV_POSITION; float3 positionWS : TEXCOORD0; half3 normalWS : TEXCOORD1; float fogFactor : TEXCOORD2; UNITY_VERTEX_INPUT_INSTANCE_ID UNITY_VERTEX_OUTPUT_STEREO }; Varyings GhostVertex(Attributes IN) { Varyings OUT = (Varyings)0; UNITY_SETUP_INSTANCE_ID(IN); UNITY_TRANSFER_INSTANCE_ID(IN, OUT); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT); VertexPositionInputs posInput = GetVertexPositionInputs(IN.positionOS.xyz); VertexNormalInputs normalInput = GetVertexNormalInputs(IN.normalOS); OUT.positionCS = posInput.positionCS; OUT.positionWS = posInput.positionWS; OUT.normalWS = normalInput.normalWS; OUT.fogFactor = ComputeFogFactor(posInput.positionCS.z); return OUT; } half4 GhostFragment(Varyings IN) : SV_Target { UNITY_SETUP_INSTANCE_ID(IN); UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(IN); half3 normalWS = normalize(IN.normalWS); half3 viewDirWS = normalize(GetWorldSpaceViewDir(IN.positionWS)); // Fresnel rim: bright on grazing angles so the edge glows. half fresnel = pow(1.0 - saturate(dot(normalWS, viewDirWS)), _RimPower); half rim = fresnel * _RimIntensity; // Breathing pulse so the ghost feels alive without moving. half pulse = lerp(_PulseMin, _PulseMax, sin(_Time.y * _PulseSpeed) * 0.5 + 0.5); half3 color = _BaseColor.rgb * pulse; half alpha = saturate(_FillOpacity + rim) * pulse * _BaseColor.a; color = MixFog(color, IN.fogFactor); return half4(color, saturate(alpha)); } ENDHLSL } } FallBack Off }