Classic Ultima Online on Godot 4 .NET
GUO can give the world a different look: cel shading, ink outlines, black and white, sepia, noir, colour grades, a vignette, a glow around lights, an EGA or handheld palette, CRT scanlines, and colour-blind help. Effects apply to the world only, at the art’s own pixels, before the window is scaled up. Gumps, the paperdoll and the rest of the interface are never touched.
Classic, the default, is exactly ClassicUO’s picture: no effect runs, and the world is drawn the same way it always was. The decision record is ADR-0023.
The chosen look is remembered on this device.
The same things by typed command (a client command, like ClassicUO’s own):
-postfx list the looks and the shaders there are
-postfx load Noir switch to a look
-postfx off back to Classic
-postfx save My Look save the current look under a name
-postfx split 0.5 compare with Classic (again to turn it off)
-postfx cost how long the effects took on the GPU last frame
-postfx open or close the menu
Cel, Ink Outline, Posterize, Black & White, Sepia, Noir, Warm Grade, Teal & Orange, Faded Film, Moonlight, Vignette, Glow, EGA, Handheld, CRT, Colour Help (protanopia, deuteranopia, tritanopia), and two that react to the game:
There is also a Silhouettes look in the built-in set.
On a desktop GPU each look costs well under a millisecond a frame. The measured costs are in ADR-0023.
GUO also has an optional pack, JarJar’s screen effects: 32 more looks by MeesaJarJar (fog, rain, snow, night vision, thermal, depth of field, damage, poison and more). Install it from Options > Video > Store (Asset Store, Content Packs). Installing it does not switch a look on; pick one in Screen effects, and choose Classic to turn every effect off.
Looks are plain text. Put them in the postfx folder of the client’s home
(beside settings.json); they appear in the menu, and a file changed while
you play is picked up within a second.
{
"name": "Stormy",
"description": "Cold and dim when it rains.",
"passes": [
{ "shader": "grade", "params": { "temperature": -0.3, "saturation": 0.8 } },
{ "shader": "vignette", "params": { "radius": 0.85, "strength": 0.3 } }
],
"bindings": [
{ "pass": 0, "param": "exposure", "from": "weather", "scale": -0.15, "offset": 0.0 }
]
}
.gdshader in the same
folder) plus its settings. A setting is a number, true/false, a colour
[r, g, b, a] (0..1), or an image path for a texture (a LUT or palette
strip beside the preset).setting = offset + scale * value, kept within the setting’s range. The
values are:
hp_ratio and hp_missing (health left or lost, 0..1);stamina_ratio and mana_ratio;light_level (0 bright .. 1 dark);war_mode (0 or 1);weather (0 none, 1 rain, 2 storm, 3 snow);time (seconds).--postfx NAME (or --postfx off) picks the look
without saving it.A pass is a Godot canvas_item shader that reads the pass before it and
writes its result:
shader_type canvas_item;
render_mode blend_disabled;
uniform sampler2D source : hint_screen_texture, filter_nearest;
uniform float amount : hint_range(0.0, 1.0, 0.01) = 0.5; // becomes a slider
uniform int style : hint_enum("Soft", "Hard") = 0; // becomes a dropdown
uniform vec4 tint : source_color = vec4(1.0); // becomes a colour picker
void fragment() {
vec4 c = texture(source, SCREEN_UV);
COLOR = vec4(mix(c.rgb, c.rgb * tint.rgb, amount), c.a); // keep the alpha
}
Declare uniform sampler2D light_tex : filter_nearest; to read the client’s
light buffer (where torches, lamps and spells light the world). Keep sampling
nearest: UO’s art is pixel art, and filtering it blurs it.
The Asset Store has a postfx kind: a pack of presets, their shaders and any images they use. Installed packs appear in the menu beside the built-in looks, and your own same-named look wins. See ADR-0019 (Amendment 3).