Area of influence depends on followers MobState (#1439)
* simple anti-religion zone shader * observers refactor
This commit is contained in:
@@ -25,12 +25,15 @@ public sealed class CP14ReligionVisionOverlay : Overlay
|
||||
public override bool RequestScreenTexture => true;
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
private readonly ProtoId<CP14ReligionPrototype>? _religion = null;
|
||||
private readonly ProtoId<CP14ReligionPrototype>? _religion;
|
||||
|
||||
private readonly ShaderInstance _religionShader;
|
||||
private readonly Vector2[] _positions = new Vector2[MaxCount];
|
||||
private readonly float[] _radii = new float[MaxCount];
|
||||
private int _count = 0;
|
||||
private int _count;
|
||||
private readonly Vector2[] _antiPositions = new Vector2[MaxCount];
|
||||
private readonly float[] _antiRadii = new float[MaxCount];
|
||||
private int _antiCount;
|
||||
|
||||
public CP14ReligionVisionOverlay()
|
||||
{
|
||||
@@ -49,20 +52,21 @@ public sealed class CP14ReligionVisionOverlay : Overlay
|
||||
if (args.Viewport.Eye == null)
|
||||
return false;
|
||||
|
||||
_count = 0;
|
||||
|
||||
var clusters = new List<Cluster>();
|
||||
var antiClusters = new List<Cluster>();
|
||||
var religionQuery = _entManager.AllEntityQueryEnumerator<CP14ReligionObserverComponent, TransformComponent>();
|
||||
while (religionQuery.MoveNext(out var uid, out var rel, out var xform))
|
||||
while (religionQuery.MoveNext(out var uid, out var observer, out var xform))
|
||||
{
|
||||
if (_religion is null)
|
||||
continue;
|
||||
|
||||
var observation = rel.Observation;
|
||||
if (!observation.ContainsKey(_religion.Value))
|
||||
if (observer.Religion is null)
|
||||
continue;
|
||||
|
||||
if (!rel.Active || xform.MapID != args.MapId)
|
||||
if (observer.Radius <= 0f)
|
||||
continue;
|
||||
|
||||
if (!observer.Active || xform.MapID != args.MapId)
|
||||
continue;
|
||||
|
||||
var mapPos = _transform.GetWorldPosition(uid);
|
||||
@@ -72,23 +76,40 @@ public sealed class CP14ReligionVisionOverlay : Overlay
|
||||
var tempCoords = args.Viewport.WorldToLocal(mapPos);
|
||||
tempCoords.Y = args.Viewport.Size.Y - tempCoords.Y; // Local space to fragment space.
|
||||
|
||||
// try find cluster to merge with
|
||||
bool merged = false;
|
||||
foreach (var cluster in clusters)
|
||||
if (observer.Religion.Value == _religion.Value)
|
||||
{
|
||||
if ((cluster.Position - tempCoords).Length() < 150f)
|
||||
// try find cluster to merge with
|
||||
bool merged = false;
|
||||
foreach (var cluster in clusters)
|
||||
{
|
||||
cluster.Add(tempCoords, rel.Observation[_religion.Value]);
|
||||
merged = true;
|
||||
break;
|
||||
if ((cluster.Position - tempCoords).Length() < 150f)
|
||||
{
|
||||
cluster.Add(tempCoords, observer.Radius);
|
||||
merged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!merged && clusters.Count < MaxCount)
|
||||
clusters.Add(new Cluster(tempCoords, observer.Radius));
|
||||
}
|
||||
else
|
||||
{
|
||||
// try find cluster to merge with
|
||||
bool merged = false;
|
||||
foreach (var antiCluster in antiClusters)
|
||||
{
|
||||
if ((antiCluster.Position - tempCoords).Length() < 150f)
|
||||
{
|
||||
antiCluster.Add(tempCoords, observer.Radius);
|
||||
merged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!merged)
|
||||
clusters.Add(new Cluster(tempCoords, rel.Observation[_religion.Value]));
|
||||
|
||||
if (clusters.Count >= MaxCount)
|
||||
break;
|
||||
if (!merged && antiClusters.Count < MaxCount)
|
||||
antiClusters.Add(new Cluster(tempCoords, observer.Radius));
|
||||
}
|
||||
}
|
||||
|
||||
_count = 0;
|
||||
@@ -99,6 +120,14 @@ public sealed class CP14ReligionVisionOverlay : Overlay
|
||||
_count++;
|
||||
}
|
||||
|
||||
_antiCount = 0;
|
||||
foreach (var antiCluster in antiClusters)
|
||||
{
|
||||
_antiPositions[_antiCount] = antiCluster.Position;
|
||||
_antiRadii[_antiCount] = antiCluster.Radius;
|
||||
_antiCount++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -112,9 +141,15 @@ public sealed class CP14ReligionVisionOverlay : Overlay
|
||||
|
||||
_religionShader?.SetParameter("shaderColor", visionComponent.ShaderColor);
|
||||
_religionShader?.SetParameter("renderScale", args.Viewport.RenderScale * args.Viewport.Eye.Scale);
|
||||
|
||||
_religionShader?.SetParameter("count", _count);
|
||||
_religionShader?.SetParameter("position", _positions);
|
||||
_religionShader?.SetParameter("radius", _radii);
|
||||
|
||||
_religionShader?.SetParameter("anticount", _antiCount);
|
||||
_religionShader?.SetParameter("antiposition", _antiPositions);
|
||||
_religionShader?.SetParameter("antiradius", _antiRadii);
|
||||
|
||||
_religionShader?.SetParameter("SCREEN_TEXTURE", ScreenTexture);
|
||||
|
||||
var worldHandle = args.WorldHandle;
|
||||
|
||||
@@ -86,7 +86,12 @@ public sealed partial class CP14ReligionGodSystem : CP14SharedReligionGodSystem
|
||||
|
||||
foreach (var god in GetGods(follower.Religion.Value))
|
||||
{
|
||||
_magicEnergy.TransferEnergy((uid, energy), god.Owner, follower.EnergyToGodTransfer, out _, out _, safe: true);
|
||||
_magicEnergy.TransferEnergy((uid, energy),
|
||||
god.Owner,
|
||||
follower.EnergyToGodTransfer,
|
||||
out _,
|
||||
out _,
|
||||
safe: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,7 +116,12 @@ public sealed partial class CP14ReligionGodSystem : CP14SharedReligionGodSystem
|
||||
Timer.Spawn(333,
|
||||
() =>
|
||||
{
|
||||
_chatSys.TrySendInGameICMessage(speaker, message, type, ChatTransmitRange.Normal, nameOverride: MetaData(ent).EntityName, ignoreActionBlocker: true);
|
||||
_chatSys.TrySendInGameICMessage(speaker,
|
||||
message,
|
||||
type,
|
||||
ChatTransmitRange.Normal,
|
||||
nameOverride: MetaData(ent).EntityName,
|
||||
ignoreActionBlocker: true);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -126,14 +136,14 @@ public sealed partial class CP14ReligionGodSystem : CP14SharedReligionGodSystem
|
||||
|
||||
private void OnObserverInit(Entity<CP14ReligionObserverComponent> ent, ref ComponentInit args)
|
||||
{
|
||||
foreach (var (religion, _) in ent.Comp.Observation)
|
||||
{
|
||||
var gods = GetGods(religion);
|
||||
if (ent.Comp.Religion is null)
|
||||
return;
|
||||
|
||||
foreach (var god in gods)
|
||||
{
|
||||
UpdatePvsOverrides(god);
|
||||
}
|
||||
var gods = GetGods(ent.Comp.Religion.Value);
|
||||
|
||||
foreach (var god in gods)
|
||||
{
|
||||
UpdatePvsOverrides(god);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +173,9 @@ public sealed partial class CP14ReligionGodSystem : CP14SharedReligionGodSystem
|
||||
return;
|
||||
|
||||
var wrappedMessage =
|
||||
Loc.GetString("cp14-altar-wrapped-message", ("name", MetaData(args.Source).EntityName), ("msg", args.Message));
|
||||
Loc.GetString("cp14-altar-wrapped-message",
|
||||
("name", MetaData(args.Source).EntityName),
|
||||
("msg", args.Message));
|
||||
|
||||
SendMessageToGods(ent.Comp.Religion.Value, wrappedMessage, args.Source);
|
||||
}
|
||||
@@ -181,7 +193,14 @@ public sealed partial class CP14ReligionGodSystem : CP14SharedReligionGodSystem
|
||||
channels.Add(godActor.PlayerSession.Channel);
|
||||
}
|
||||
|
||||
_chat.ChatMessageToMany(ChatChannel.Notifications, msg, msg, source, false, true, channels, colorOverride: Color.Aqua);
|
||||
_chat.ChatMessageToMany(ChatChannel.Notifications,
|
||||
msg,
|
||||
msg,
|
||||
source,
|
||||
false,
|
||||
true,
|
||||
channels,
|
||||
colorOverride: Color.Aqua);
|
||||
}
|
||||
|
||||
public FixedPoint2 GetFollowerPercentage(Entity<CP14ReligionEntityComponent> god)
|
||||
@@ -221,10 +240,10 @@ public sealed partial class CP14ReligionGodSystem : CP14SharedReligionGodSystem
|
||||
var query = EntityQueryEnumerator<CP14ReligionObserverComponent>();
|
||||
while (query.MoveNext(out var uid, out var observer))
|
||||
{
|
||||
if (!observer.Observation.ContainsKey(ent.Comp.Religion.Value))
|
||||
if (!observer.Active)
|
||||
continue;
|
||||
|
||||
if (observer.Observation[ent.Comp.Religion.Value] <= ObservationOverrideRadius)
|
||||
if (observer.Radius <= ObservationOverrideRadius)
|
||||
continue;
|
||||
|
||||
ent.Comp.PvsOverridedObservers.Add(uid);
|
||||
@@ -240,9 +259,9 @@ public sealed partial class CP14ReligionGodSystem : CP14SharedReligionGodSystem
|
||||
if (ent.Comp.Session is null)
|
||||
return;
|
||||
|
||||
foreach (var altar in ent.Comp.PvsOverridedObservers)
|
||||
foreach (var observer in ent.Comp.PvsOverridedObservers)
|
||||
{
|
||||
_pvs.RemoveSessionOverride(altar, ent.Comp.Session);
|
||||
_pvs.RemoveSessionOverride(observer, ent.Comp.Session);
|
||||
}
|
||||
|
||||
ent.Comp.Session = null;
|
||||
@@ -259,10 +278,10 @@ public sealed partial class CP14ReligionGodSystem : CP14SharedReligionGodSystem
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class CP14SpokeAttemptEvent(string message, InGameICChatType type, ICommonSession? player) : CancellableEntityEventArgs
|
||||
public sealed class CP14SpokeAttemptEvent(string message, InGameICChatType type, ICommonSession? player)
|
||||
: CancellableEntityEventArgs
|
||||
{
|
||||
public string Message = message;
|
||||
public InGameICChatType Type = type;
|
||||
public ICommonSession? Player = player;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,10 @@ namespace Content.Shared._CP14.Religion.Components;
|
||||
public sealed partial class CP14ReligionObserverComponent : Component
|
||||
{
|
||||
[DataField, AutoNetworkedField]
|
||||
public Dictionary<ProtoId<CP14ReligionPrototype>, float> Observation = new(); //DAMNATION
|
||||
public ProtoId<CP14ReligionPrototype>? Religion;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public float Radius = 5f;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool Active = true;
|
||||
|
||||
@@ -11,8 +11,5 @@ public sealed partial class CP14ReligionPrototype : IPrototype
|
||||
[IdDataField] public string ID { get; } = default!;
|
||||
|
||||
[DataField]
|
||||
public float FollowerObservationRadius = 8f;
|
||||
|
||||
[DataField]
|
||||
public float AltarObservationRadius = 25f;
|
||||
public float FollowerObservationRadius = 10f;
|
||||
}
|
||||
|
||||
@@ -48,50 +48,6 @@ public abstract partial class CP14SharedReligionGodSystem
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public bool TryConvertAltar(EntityUid target, ProtoId<CP14ReligionPrototype> religion)
|
||||
{
|
||||
if (!_proto.TryIndex(religion, out var indexedReligion))
|
||||
return false;
|
||||
|
||||
EnsureComp<CP14ReligionAltarComponent>(target, out var altar);
|
||||
|
||||
if (!altar.CanBeConverted)
|
||||
return false;
|
||||
|
||||
var oldReligion = altar.Religion;
|
||||
altar.Religion = religion;
|
||||
Dirty(target, altar);
|
||||
|
||||
EditObservation(target, religion, indexedReligion.AltarObservationRadius);
|
||||
|
||||
var ev = new CP14ReligionChangedEvent(oldReligion, religion);
|
||||
RaiseLocalEvent(target, ev);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void DeconvertAltar(EntityUid target)
|
||||
{
|
||||
if (!TryComp<CP14ReligionAltarComponent>(target, out var altar))
|
||||
return;
|
||||
|
||||
if (altar.Religion is null)
|
||||
return;
|
||||
|
||||
if (!_proto.TryIndex(altar.Religion, out var indexedReligion))
|
||||
return;
|
||||
|
||||
EditObservation(target, altar.Religion.Value, -indexedReligion.AltarObservationRadius);
|
||||
|
||||
var oldReligion = altar.Religion;
|
||||
altar.Religion = null;
|
||||
|
||||
var ev = new CP14ReligionChangedEvent(oldReligion, null);
|
||||
RaiseLocalEvent(target, ev);
|
||||
|
||||
Dirty(target, altar);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
|
||||
@@ -40,16 +40,30 @@ public abstract partial class CP14SharedReligionGodSystem
|
||||
if (ent.Comp.Religion is null)
|
||||
return;
|
||||
|
||||
EnsureComp<CP14ReligionObserverComponent>(ent, out var observer);
|
||||
|
||||
if (!_proto.TryIndex(observer.Religion, out var indexedReligion))
|
||||
return;
|
||||
|
||||
var baseObservation = indexedReligion.FollowerObservationRadius;
|
||||
switch (args.NewMobState)
|
||||
{
|
||||
case MobState.Alive:
|
||||
observer.Radius = baseObservation;
|
||||
break;
|
||||
|
||||
case MobState.Critical:
|
||||
SendMessageToGods(ent.Comp.Religion.Value, Loc.GetString("cp14-critical-follower-message", ("name", MetaData(ent).EntityName)), ent);
|
||||
observer.Radius = baseObservation * 0.25f;
|
||||
break;
|
||||
|
||||
case MobState.Dead:
|
||||
SendMessageToGods(ent.Comp.Religion.Value, Loc.GetString("cp14-dead-follower-message", ("name", MetaData(ent).EntityName)), ent);
|
||||
observer.Radius = 0f;
|
||||
break;
|
||||
}
|
||||
|
||||
Dirty(ent.Owner, observer);
|
||||
}
|
||||
|
||||
private void OnGetAltVerbs(EntityUid uid, CP14ReligionFollowerComponent component, GetVerbsEvent<AlternativeVerb> args)
|
||||
@@ -152,12 +166,14 @@ public abstract partial class CP14SharedReligionGodSystem
|
||||
return false;
|
||||
|
||||
EnsureComp<CP14ReligionFollowerComponent>(pending, out var follower);
|
||||
EnsureComp<CP14ReligionObserverComponent>(pending, out var observer);
|
||||
observer.Religion = indexedReligion;
|
||||
observer.Radius = indexedReligion.FollowerObservationRadius;
|
||||
|
||||
var oldReligion = follower.Religion;
|
||||
follower.Religion = pending.Comp.Religion;
|
||||
Dirty(pending, follower);
|
||||
|
||||
EditObservation(pending, pending.Comp.Religion.Value, indexedReligion.FollowerObservationRadius);
|
||||
Dirty(pending, observer);
|
||||
|
||||
var ev = new CP14ReligionChangedEvent(oldReligion, pending.Comp.Religion);
|
||||
RaiseLocalEvent(pending, ev);
|
||||
@@ -178,11 +194,9 @@ public abstract partial class CP14SharedReligionGodSystem
|
||||
if (follower.Religion is null)
|
||||
return;
|
||||
|
||||
if (!_proto.TryIndex(follower.Religion, out var indexedReligion))
|
||||
return;
|
||||
RemCompDeferred<CP14ReligionObserverComponent>(target);
|
||||
|
||||
SendMessageToGods(follower.Religion.Value, Loc.GetString("cp14-remove-follower-message", ("name", MetaData(target).EntityName)), target);
|
||||
EditObservation(target, follower.Religion.Value, -indexedReligion.FollowerObservationRadius);
|
||||
|
||||
var oldReligion = follower.Religion;
|
||||
follower.Religion = null;
|
||||
|
||||
@@ -28,28 +28,6 @@ public abstract partial class CP14SharedReligionGodSystem
|
||||
args.Visibility &= ~MenuVisibility.NoFov;
|
||||
}
|
||||
|
||||
public void EditObservation(EntityUid target, ProtoId<CP14ReligionPrototype> religion, float range)
|
||||
{
|
||||
EnsureComp<CP14ReligionObserverComponent>(target, out var observer);
|
||||
|
||||
if (observer.Observation.ContainsKey(religion))
|
||||
{
|
||||
var newRange = Math.Clamp(observer.Observation[religion] + range, 0, float.MaxValue);
|
||||
|
||||
if (newRange <= 0)
|
||||
observer.Observation.Remove(religion);
|
||||
else
|
||||
observer.Observation[religion] = newRange;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, add a new observation for the religion.
|
||||
observer.Observation.Add(religion, range);
|
||||
}
|
||||
|
||||
Dirty(target, observer);
|
||||
}
|
||||
|
||||
public bool InVision(EntityUid target, Entity<CP14ReligionEntityComponent> user)
|
||||
{
|
||||
var position = Transform(target).Coordinates;
|
||||
@@ -62,29 +40,27 @@ public abstract partial class CP14SharedReligionGodSystem
|
||||
if (!HasComp<CP14ReligionVisionComponent>(user))
|
||||
return true;
|
||||
|
||||
if (user.Comp.Religion is null)
|
||||
return true;
|
||||
|
||||
var userXform = Transform(user);
|
||||
var query = EntityQueryEnumerator<CP14ReligionObserverComponent, TransformComponent>();
|
||||
while (query.MoveNext(out var uid, out var observer, out var xform))
|
||||
{
|
||||
if (!observer.Active)
|
||||
if (!observer.Active || observer.Religion is null || observer.Radius <= 0f)
|
||||
continue;
|
||||
|
||||
if (xform.MapID != userXform.MapID)
|
||||
continue;
|
||||
|
||||
if (user.Comp.Religion is null)
|
||||
if (observer.Religion.Value != user.Comp.Religion.Value)
|
||||
continue;
|
||||
|
||||
if (!observer.Observation.ContainsKey(user.Comp.Religion.Value))
|
||||
continue;
|
||||
|
||||
|
||||
var obsPos = _transform.GetWorldPosition(uid);
|
||||
var targetPos = coords.Position;
|
||||
if (Vector2.Distance(obsPos, targetPos) <= observer.Observation[user.Comp.Religion.Value])
|
||||
if (Vector2.Distance(obsPos, targetPos) <= observer.Radius)
|
||||
{
|
||||
// If the observer is within range of the target, they can see it.
|
||||
return true;
|
||||
return observer.Religion.Value == user.Comp.Religion.Value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -97,4 +97,4 @@
|
||||
- type: Tag
|
||||
tags:
|
||||
- FootstepSound
|
||||
- CP14Mosquito
|
||||
- CP14Mosquito
|
||||
@@ -12,6 +12,7 @@
|
||||
sprite: _CP14/Mobs/Demigods/temp_icons.rsi
|
||||
color: "#ffffff99"
|
||||
- type: PointLight
|
||||
energy: 2
|
||||
radius: 10
|
||||
softness: 1
|
||||
castShadows: false
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
- type: CP14ReligionAltar
|
||||
religion: Lumera
|
||||
- type: CP14ReligionObserver
|
||||
observation:
|
||||
Lumera: 10
|
||||
religion: Lumera
|
||||
radius: 15
|
||||
- type: SpawnPoint
|
||||
job_id: CP14GodLumera
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
# - type: CP14ReligionAltar
|
||||
# religion: Merkas
|
||||
# - type: CP14ReligionObserver
|
||||
# observation:
|
||||
# Merkas: 10
|
||||
# religion: Merkas
|
||||
# radius: 10
|
||||
# - type: SpawnPoint
|
||||
# job_id: CP14GodMerkas
|
||||
@@ -1,11 +1,17 @@
|
||||
uniform sampler2D SCREEN_TEXTURE;
|
||||
|
||||
uniform lowp int count;
|
||||
uniform highp vec2[64] position;
|
||||
uniform highp float[64] radius;
|
||||
|
||||
uniform lowp int anticount;
|
||||
uniform highp vec2[64] antiposition;
|
||||
uniform highp float[64] antiradius;
|
||||
|
||||
uniform highp vec2 renderScale;
|
||||
uniform highp vec3 shaderColor;
|
||||
|
||||
const highp float radiusFalloff = 60.0;
|
||||
const highp float radiusFalloff = 20.0;
|
||||
const highp float noiseScale = 6;
|
||||
const highp float timeScale = 0.1;
|
||||
|
||||
@@ -34,51 +40,67 @@ highp vec3 gradientColor(highp float t) {
|
||||
}
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
highp vec2 fragPos = FRAGCOORD.xy;
|
||||
highp float calculateVisibilityAtPoint(highp vec2 fragPos) {
|
||||
highp float visibility = 0.0;
|
||||
highp float edgeHighlight = 0.0;
|
||||
highp float scale = renderScale.x;
|
||||
|
||||
// Видимость (раскрытие)
|
||||
for (lowp int i = 0; i < 64 && i < count; i++) {
|
||||
highp float scaledRadius = radius[i] * 33.0 * scale;
|
||||
highp float dist = length(fragPos - position[i]);
|
||||
|
||||
highp float fade = smoothstep(scaledRadius - radiusFalloff * scale, scaledRadius, dist);
|
||||
visibility = max(visibility, 1.0 - fade);
|
||||
}
|
||||
|
||||
// Антивидимость (перекрытие)
|
||||
for (lowp int i = 0; i < 64 && i < anticount; i++) {
|
||||
highp float scaledRadius = antiradius[i] * 33.0 * scale;
|
||||
highp float dist = length(fragPos - antiposition[i]);
|
||||
|
||||
highp float fade = smoothstep(scaledRadius - radiusFalloff * scale, scaledRadius, dist);
|
||||
highp float antiVis = 1.0 - fade;
|
||||
|
||||
// Учитываем антивидимость как маску
|
||||
visibility = min(visibility, fade); // fade → 1 в центре антизоны (нулевая видимость)
|
||||
}
|
||||
|
||||
return visibility;
|
||||
}
|
||||
|
||||
highp float calculateEdgeHighlight(highp float visibility) {
|
||||
if (visibility >= 0.99 || visibility <= 0.0)
|
||||
return 0.0;
|
||||
|
||||
return visibility;
|
||||
}
|
||||
|
||||
|
||||
void fragment() {
|
||||
highp vec2 fragPos = FRAGCOORD.xy;
|
||||
highp vec2 uv = fragPos * SCREEN_PIXEL_SIZE;
|
||||
highp float t = TIME * timeScale;
|
||||
highp vec2 noiseUV = uv * noiseScale + vec2(t);
|
||||
|
||||
// Цветовая маска (по шуму)
|
||||
highp float n = noise(noiseUV);
|
||||
highp vec3 colorOverlay = gradientColor(n);
|
||||
|
||||
// Искажение позиции для эффекта "миража"
|
||||
highp float nX = noise(noiseUV + vec2(3.1, 7.7));
|
||||
highp float nY = noise(noiseUV + vec2(9.3, 2.4));
|
||||
highp vec2 distortion = vec2(nX - 0.5, nY - 0.5) * 15.0 * scale;
|
||||
highp vec2 distortion = vec2(nX - 0.5, nY - 0.5) * 15.0 * renderScale.x;
|
||||
|
||||
for (lowp int i = 0; i < 64 && i < count; i++) {
|
||||
highp float scaledRadius = radius[i] * 33.0 * scale;
|
||||
highp float dist = length((fragPos + distortion) - position[i]);
|
||||
|
||||
highp float fade = smoothstep(scaledRadius - radiusFalloff * scale, scaledRadius, dist);
|
||||
visibility = max(visibility, 1.0 - fade);
|
||||
|
||||
// Узкая кромка по расстоянию до радиуса
|
||||
highp float offset = -60;
|
||||
highp float edgeWidth = 8.0 * scale; // ширина кромки в пикселях
|
||||
highp float edgeDist = abs(dist - (scaledRadius + offset)) * (1 / scale);
|
||||
highp float edge = smoothstep(edgeWidth, 0.0, edgeDist);
|
||||
edgeHighlight = max(edgeHighlight, edge);
|
||||
}
|
||||
highp float visibility = calculateVisibilityAtPoint(fragPos + distortion);
|
||||
highp float edgeHighlight = calculateEdgeHighlight(visibility);
|
||||
|
||||
|
||||
highp vec4 baseColor = zTextureSpec(SCREEN_TEXTURE, fragPos * SCREEN_PIXEL_SIZE);
|
||||
highp vec4 baseColor = zTextureSpec(SCREEN_TEXTURE, uv);
|
||||
highp vec3 finalColor = mix(colorOverlay, baseColor.rgb, visibility);
|
||||
|
||||
// Наложим бордовую кромку
|
||||
finalColor = mix(
|
||||
finalColor,
|
||||
shaderColor,
|
||||
edgeHighlight * step(0.5, 1.0 - visibility)
|
||||
);
|
||||
finalColor = mix(finalColor, shaderColor, edgeHighlight);
|
||||
|
||||
highp float minAlpha = 1;
|
||||
COLOR = vec4(finalColor, mix(minAlpha, 1.0, visibility));
|
||||
highp float minAlpha = 1.0;
|
||||
COLOR = vec4(finalColor, mix(minAlpha, 1.0, edgeHighlight));
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user