improve noir glasses shader (#37996)

* improve noir glasses shader

* tweak values
This commit is contained in:
slarticodefast
2025-06-02 18:16:10 +02:00
committed by GitHub
parent 79e0ed38d1
commit 1441effed2
9 changed files with 131 additions and 17 deletions

View File

@@ -1,5 +1,4 @@
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -7,9 +6,7 @@ namespace Content.Client.Overlays;
public sealed partial class BlackAndWhiteOverlay : Overlay public sealed partial class BlackAndWhiteOverlay : Overlay
{ {
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
public override OverlaySpace Space => OverlaySpace.WorldSpace; public override OverlaySpace Space => OverlaySpace.WorldSpace;
public override bool RequestScreenTexture => true; public override bool RequestScreenTexture => true;
@@ -22,17 +19,6 @@ public sealed partial class BlackAndWhiteOverlay : Overlay
ZIndex = 10; // draw this over the DamageOverlay, RainbowOverlay etc. ZIndex = 10; // draw this over the DamageOverlay, RainbowOverlay etc.
} }
protected override bool BeforeDraw(in OverlayDrawArgs args)
{
if (!_entityManager.TryGetComponent(_playerManager.LocalEntity, out EyeComponent? eyeComp))
return false;
if (args.Viewport.Eye != eyeComp.Eye)
return false;
return true;
}
protected override void Draw(in OverlayDrawArgs args) protected override void Draw(in OverlayDrawArgs args)
{ {
if (ScreenTexture == null) if (ScreenTexture == null)

View File

@@ -1,7 +1,6 @@
using Content.Shared.Inventory.Events; using Content.Shared.Inventory.Events;
using Content.Shared.Overlays; using Content.Shared.Overlays;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.Player;
namespace Content.Client.Overlays; namespace Content.Client.Overlays;

View File

@@ -0,0 +1,33 @@
using Robust.Client.Graphics;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
namespace Content.Client.Overlays;
public sealed partial class NoirOverlay : Overlay
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public override bool RequestScreenTexture => true;
private readonly ShaderInstance _noirShader;
public NoirOverlay()
{
IoCManager.InjectDependencies(this);
_noirShader = _prototypeManager.Index<ShaderPrototype>("Noir").InstanceUnique();
ZIndex = 9; // draw this over the DamageOverlay, RainbowOverlay etc, but before the black and white shader
}
protected override void Draw(in OverlayDrawArgs args)
{
if (ScreenTexture == null)
return;
var handle = args.WorldHandle;
_noirShader.SetParameter("SCREEN_TEXTURE", ScreenTexture);
handle.UseShader(_noirShader);
handle.DrawRect(args.WorldBounds, Color.White);
handle.UseShader(null);
}
}

View File

@@ -0,0 +1,33 @@
using Content.Shared.Inventory.Events;
using Content.Shared.Overlays;
using Robust.Client.Graphics;
namespace Content.Client.Overlays;
public sealed partial class NoirOverlaySystem : EquipmentHudSystem<NoirOverlayComponent>
{
[Dependency] private readonly IOverlayManager _overlayMan = default!;
private NoirOverlay _overlay = default!;
public override void Initialize()
{
base.Initialize();
_overlay = new();
}
protected override void UpdateInternal(RefreshEquipmentHudEvent<NoirOverlayComponent> component)
{
base.UpdateInternal(component);
_overlayMan.AddOverlay(_overlay);
}
protected override void DeactivateInternal()
{
base.DeactivateInternal();
_overlayMan.RemoveOverlay(_overlay);
}
}

View File

@@ -85,6 +85,7 @@ public partial class InventorySystem
SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowSyndicateIconsComponent>>(RefRelayInventoryEvent); SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowSyndicateIconsComponent>>(RefRelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowCriminalRecordIconsComponent>>(RefRelayInventoryEvent); SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowCriminalRecordIconsComponent>>(RefRelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<BlackAndWhiteOverlayComponent>>(RefRelayInventoryEvent); SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<BlackAndWhiteOverlayComponent>>(RefRelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<NoirOverlayComponent>>(RefRelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, GetVerbsEvent<EquipmentVerb>>(OnGetEquipmentVerbs); SubscribeLocalEvent<InventoryComponent, GetVerbsEvent<EquipmentVerb>>(OnGetEquipmentVerbs);
SubscribeLocalEvent<InventoryComponent, GetVerbsEvent<InnateVerb>>(OnGetInnateVerbs); SubscribeLocalEvent<InventoryComponent, GetVerbsEvent<InnateVerb>>(OnGetInnateVerbs);

View File

@@ -0,0 +1,10 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Overlays;
/// <summary>
/// Makes the entity see everything with a sin city shader (everything in black and white, except red) by adding an overlay.
/// When added to a clothing item it will also grant the wearer the same overlay.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class NoirOverlayComponent : Component;

View File

@@ -281,7 +281,7 @@
- type: FlashImmunity - type: FlashImmunity
- type: EyeProtection - type: EyeProtection
protectionTime: 5 protectionTime: 5
- type: BlackAndWhiteOverlay - type: NoirOverlay
- type: Tag - type: Tag
tags: tags:
- HamsterWearable - HamsterWearable

View File

@@ -47,6 +47,12 @@
kind: source kind: source
path: "/Textures/Shaders/rainbow.swsl" path: "/Textures/Shaders/rainbow.swsl"
# sin city effect: everything is greyscale, except red
- type: shader
id: Noir
kind: source
path: "/Textures/Shaders/noir.swsl"
- type: shader - type: shader
id: CameraStatic id: CameraStatic
kind: source kind: source

View File

@@ -0,0 +1,46 @@
// Sin City style shader.
// Makes everything black and white, but keeps red colors.
uniform sampler2D SCREEN_TEXTURE;
// Sensitivity, in degrees.
const highp float RedHueRange = 5;
const highp float MinSaturation = 0.4;
// https://en.wikipedia.org/wiki/HSL_and_HSV#From_RGB
highp vec3 rgb2hsv(highp vec3 c) {
highp float xMax = max(c.r, max(c.g, c.b));
highp float xMin = min(c.r, min(c.g, c.b));
highp float delta = xMax - xMin;
highp float hue = 0.0;
if (delta > 0.0) {
if (xMax == c.r) {
hue = mod((c.g - c.b) / delta, 6.0);
} else if (xMax == c.g) {
hue = (c.b - c.r) / delta + 2.0;
} else {
hue = (c.r - c.g) / delta + 4.0;
}
hue *= 60.0;
if (hue < 0.0) hue += 360.0;
}
highp float sat = (xMax == 0.0) ? 0.0 : delta / xMax;
return vec3(hue, sat, xMax);
}
void fragment() {
highp vec4 color = zTextureSpec(SCREEN_TEXTURE, UV);
highp vec3 gray = vec3(zGrayscale(color.rgb));
highp vec3 hsv = rgb2hsv(color.rgb);
// Red is near 0 or 360 in hue
bool is_red = hsv.x < RedHueRange || hsv.x > (360.0 - RedHueRange);
bool saturated_enough = hsv.y > MinSaturation; // Avoid desaturated pinks/greys
if (is_red && saturated_enough) {
COLOR = color;
} else {
COLOR = vec4(gray, color.a);
}
}