Files
Emberwild/Assets/GAME/Shaders/Build/BuildGhost.shader
T
2026-07-08 18:43:19 +02:00

130 lines
4.5 KiB
Plaintext

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
}