From 953ac19e77fe3f7757fd3bfaeb9fb3c5684d9e75 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 10 Aug 2026 22:15:10 +0530 Subject: [PATCH] Autoformat slang code --- autoformat | 2 +- docs/custom-shaders.rst | 2 +- kitty/shaders/alpha-blend.slang | 10 +- kitty/shaders/background.slang | 134 ++-- kitty/shaders/bgimage.slang | 38 +- kitty/shaders/blit-common.slang | 11 +- kitty/shaders/blit.slang | 13 +- kitty/shaders/border.slang | 34 +- kitty/shaders/cell.slang | 118 ++-- kitty/shaders/custom/bloom.slang | 52 +- kitty/shaders/custom/crt.slang | 21 +- kitty/shaders/custom/cursor-trail-blaze.slang | 68 +- .../shaders/custom/cursor-trail-default.slang | 27 +- .../custom/cursor-trail-lightning.slang | 108 ++- kitty/shaders/custom/fireworks-rockets.slang | 72 +- kitty/shaders/custom/fireworks.slang | 73 +- kitty/shaders/custom/focus-highlight.slang | 13 +- kitty/shaders/custom/inside-the-matrix.slang | 290 ++++---- kitty/shaders/custom/negative.slang | 7 +- kitty/shaders/custom/northern-lights.slang | 668 +++++++++--------- kitty/shaders/custom/pipeline.slang | 32 +- kitty/shaders/custom/pond-ripple.slang | 42 +- kitty/shaders/custom/sample.slang | 13 +- kitty/shaders/custom/spotlight.slang | 44 +- kitty/shaders/custom/tab-change.slang | 30 +- kitty/shaders/custom/tft.slang | 12 +- kitty/shaders/custom/types.slang | 18 +- kitty/shaders/custom/underwater.slang | 68 +- kitty/shaders/custom/water.slang | 29 +- kitty/shaders/graphics.slang | 21 +- kitty/shaders/hsluv.slang | 163 +++-- kitty/shaders/linear2srgb.slang | 13 +- kitty/shaders/padding.slang | 32 +- kitty/shaders/rounded_rect.slang | 22 +- kitty/shaders/screenshot.slang | 59 +- kitty/shaders/slang.py | 2 +- kitty/shaders/tint.slang | 25 +- kitty/shaders/trail.slang | 16 +- kitty/shaders/utils.slang | 19 +- kitty_tests/slang.py | 52 ++ 40 files changed, 1264 insertions(+), 1209 deletions(-) diff --git a/autoformat b/autoformat index d91ac0382..e8489f087 100755 --- a/autoformat +++ b/autoformat @@ -41,7 +41,7 @@ for x in os.listdir(base): if file.startswith('wayland-') and os.path.basename(root) == 'glfw': continue ext = os.path.splitext(file)[1] - if ext in ('.c', '.h', '.m'): + if ext in ('.c', '.h', '.m', '.slang'): clang_files.append(os.path.join(root, file)) CACHE_DIR = os.path.join(base, '.cache', 'autoformat') diff --git a/docs/custom-shaders.rst b/docs/custom-shaders.rst index d9c9c5492..79bf7ecd0 100644 --- a/docs/custom-shaders.rst +++ b/docs/custom-shaders.rst @@ -124,7 +124,7 @@ This is in a :file:`.slang` file. It must define a function called ``fragment_main()`` whose signature is: .. literalinclude:: ../kitty/shaders/custom/sample.slang - :start-at: public float4 fragment_main( + :start-after: START_FUNCTION_SIGNATURE :end-before: END_FUNCTION_SIGNATURE The two structs passed into this function have the definition shown below: diff --git a/kitty/shaders/alpha-blend.slang b/kitty/shaders/alpha-blend.slang index 10fe70bc6..057f8d769 100644 --- a/kitty/shaders/alpha-blend.slang +++ b/kitty/shaders/alpha-blend.slang @@ -6,14 +6,16 @@ module alpha_blend; // Alpha blend two colors returning the resulting color pre-multiplied by its alpha // and its alpha. See https://en.wikipedia.org/wiki/Alpha_compositing -public float4 alpha_blend(float4 over, float4 under) { +public float4 +alpha_blend(float4 over, float4 under) { float alpha = lerp(under.a, 1.0f, over.a); float3 combined_color = lerp(under.rgb * under.a, over.rgb, over.a); return float4(combined_color, alpha); } // Same as alpha_blend() except that it assumes over and under are both premultiplied. -public float4 alpha_blend_premul(float4 over, float4 under) { +public float4 +alpha_blend_premul(float4 over, float4 under) { float inv_over_alpha = 1.0f - over.a; float alpha = over.a + under.a * inv_over_alpha; return float4(over.rgb + under.rgb * inv_over_alpha, alpha); @@ -21,8 +23,8 @@ public float4 alpha_blend_premul(float4 over, float4 under) { // Same as alpha_blend_premul with under_alpha = 1 outputs a blended color // with alpha 1 which is effectively pre-multiplied since alpha is 1 -public float4 alpha_blend_premul(float4 over, float3 under) { +public float4 +alpha_blend_premul(float4 over, float3 under) { float inv_over_alpha = 1.0f - over.a; return float4(over.rgb + under.rgb * inv_over_alpha, 1.0f); } - diff --git a/kitty/shaders/background.slang b/kitty/shaders/background.slang index 9763cab40..7f7d71472 100644 --- a/kitty/shaders/background.slang +++ b/kitty/shaders/background.slang @@ -12,8 +12,7 @@ import utils; #define NUM_COLORS 256 // Inputs {{{ -public struct CellRenderData -{ +public struct CellRenderData { public float use_cell_bg_for_selection_fg, use_cell_fg_for_selection_fg, use_cell_for_selection_bg; public uint default_fg, highlight_fg, highlight_bg, main_cursor_fg, main_cursor_bg, url_color, url_style, inverted, extra_cursor_fg, extra_cursor_bg; @@ -45,19 +44,20 @@ public ConstantBuffer glt; #define gamma_lut glt.gamma_lut // -static const int fg_index_map[3] = {0, 1, 0}; +static const int fg_index_map[3] = { 0, 1, 0 }; static const uint2 cell_pos_map[4] = { - uint2(1u, 0u), // right, top - uint2(1u, 1u), // right, bottom - uint2(0u, 1u), // left, bottom - uint2(0u, 0u) // left, top + uint2(1u, 0u), // right, top + uint2(1u, 1u), // right, bottom + uint2(0u, 1u), // left, bottom + uint2(0u, 0u) // left, top }; -static const uint cursor_shape_map[] = { // maps cursor shape to foreground sprite index - 0u, // NO_CURSOR - 0u, // BLOCK (this is rendered as background) - 2u, // BEAM - 3u, // UNDERLINE - 4u // UNFOCUSED +static const uint cursor_shape_map[] = { + // maps cursor shape to foreground sprite index + 0u, // NO_CURSOR + 0u, // BLOCK (this is rendered as background) + 2u, // BEAM + 3u, // UNDERLINE + 4u // UNFOCUSED }; // }}} @@ -69,7 +69,8 @@ public static const uint BIT_MASK = 1u; // Linear space luminance values public static const float3 Y = float3(0.2126, 0.7152, 0.0722); -public float3 color_to_vec(uint c) { +public float3 +color_to_vec(uint c) { uint r, g, b; r = (c >> 16) & BYTE_MASK; g = (c >> 8) & BYTE_MASK; @@ -77,21 +78,25 @@ public float3 color_to_vec(uint c) { return float3(gamma_lut[r], gamma_lut[g], gamma_lut[b]); } -public float one_if_equal_zero_otherwise(float a, float b) { +public float +one_if_equal_zero_otherwise(float a, float b) { return (1.0f - zero_or_one(abs(float(a) - float(b)))); } // We need an integer variant to accommodate GPU driver bugs, see // https://github.com/kovidgoyal/kitty/issues/9072 -public uint one_if_equal_zero_otherwise(int a, int b) { +public uint +one_if_equal_zero_otherwise(int a, int b) { return (1u - uint(zero_or_one(abs(float(a) - float(b))))); } -public uint one_if_equal_zero_otherwise(uint a, uint b) { +public uint +one_if_equal_zero_otherwise(uint a, uint b) { return (1u - uint(zero_or_one(abs(float(a) - float(b))))); } -public uint resolve_color(uint c, uint defval) { +public uint +resolve_color(uint c, uint defval) { int t = int(c & BYTE_MASK); uint is_one = one_if_equal_zero_otherwise(t, 1); uint is_two = one_if_equal_zero_otherwise(t, 2); @@ -99,30 +104,32 @@ public uint resolve_color(uint c, uint defval) { return is_one * color_table[(c >> 8) & BYTE_MASK] + is_two * (c >> 8) + is_neither_one_nor_two * defval; } -public float3 to_color(uint c, uint defval) { +public float3 +to_color(uint c, uint defval) { return color_to_vec(resolve_color(c, defval)); } [ForceInline] -float3 q_func(float type_val, uint which, float3 val) { +float3 +q_func(float type_val, uint which, float3 val) { return one_if_equal_zero_otherwise(type_val, float(which)) * val; } -float3 resolve_dynamic_color(uint c, float3 special_val, float3 defval) { +float3 +resolve_dynamic_color(uint c, float3 special_val, float3 defval) { float type_val = float((c >> 24) & BYTE_MASK); return ( - q_func(type_val, COLOR_IS_RGB, color_to_vec(c)) + - q_func(type_val, COLOR_IS_INDEX, color_to_vec(color_table[c & BYTE_MASK])) + - q_func(type_val, COLOR_IS_SPECIAL, special_val) + - q_func(type_val, COLOR_NOT_SET, defval) - ); + q_func(type_val, COLOR_IS_RGB, color_to_vec(c)) + q_func(type_val, COLOR_IS_INDEX, color_to_vec(color_table[c & BYTE_MASK])) + + q_func(type_val, COLOR_IS_SPECIAL, special_val) + q_func(type_val, COLOR_NOT_SET, defval)); } -public float contrast_ratio(float under_luminance, float over_luminance) { +public float +contrast_ratio(float under_luminance, float over_luminance) { return clamp((max(under_luminance, over_luminance) + 0.05f) / (min(under_luminance, over_luminance) + 0.05f), 1.f, 21.f); } -public float contrast_ratio(float3 a, float3 b) { +public float +contrast_ratio(float3 a, float3 b) { return contrast_ratio(dot(a, Y), dot(b, Y)); } @@ -130,25 +137,23 @@ public struct ColorPair { public float3 bg, fg; }; -float contrast_ratio(ColorPair a) { +float +contrast_ratio(ColorPair a) { return contrast_ratio(a.bg, a.fg); } -ColorPair if_less_than_pair(float a, float b, ColorPair thenval, ColorPair elseval) { - return ColorPair( - if_less_than(a, b, thenval.bg, elseval.bg), - if_less_than(a, b, thenval.fg, elseval.fg) - ); +ColorPair +if_less_than_pair(float a, float b, ColorPair thenval, ColorPair elseval) { + return ColorPair(if_less_than(a, b, thenval.bg, elseval.bg), if_less_than(a, b, thenval.fg, elseval.fg)); } -ColorPair if_one_then_pair(float condition, ColorPair thenval, ColorPair elseval) { - return ColorPair( - if_one_then(condition, thenval.bg, elseval.bg), - if_one_then(condition, thenval.fg, elseval.fg) - ); +ColorPair +if_one_then_pair(float condition, ColorPair thenval, ColorPair elseval) { + return ColorPair(if_one_then(condition, thenval.bg, elseval.bg), if_one_then(condition, thenval.fg, elseval.fg)); } -ColorPair resolve_extra_cursor_colors_for_special_cursor(float3 cell_bg, float3 cell_fg) { +ColorPair +resolve_extra_cursor_colors_for_special_cursor(float3 cell_bg, float3 cell_fg) { ColorPair cell = ColorPair(cell_fg, cell_bg); ColorPair base = ColorPair(color_to_vec(crd.default_fg), color_to_vec(crd.bg_colors0)); float cr = contrast_ratio(cell); @@ -157,37 +162,34 @@ ColorPair resolve_extra_cursor_colors_for_special_cursor(float3 cell_bg, float3 return if_less_than_pair(cr, 2.5f, higher_contrast_pair, cell); } -ColorPair resolve_extra_cursor_colors(float3 cell_bg, float3 cell_fg, ColorPair main_cursor) { +ColorPair +resolve_extra_cursor_colors(float3 cell_bg, float3 cell_fg, ColorPair main_cursor) { ColorPair ans = ColorPair( - resolve_dynamic_color(crd.extra_cursor_bg, main_cursor.bg, main_cursor.bg), - resolve_dynamic_color(crd.extra_cursor_fg, cell_bg, main_cursor.fg) - ); + resolve_dynamic_color(crd.extra_cursor_bg, main_cursor.bg, main_cursor.bg), resolve_dynamic_color(crd.extra_cursor_fg, cell_bg, main_cursor.fg)); ColorPair special = resolve_extra_cursor_colors_for_special_cursor(cell_bg, cell_fg); return if_one_then_pair(zero_or_one(abs(float(crd.extra_cursor_bg & BYTE_MASK) - float(COLOR_IS_SPECIAL))), ans, special); } -uint is_cursor(uint x, uint y) { +uint +is_cursor(uint x, uint y) { uint clamped_x = clamp(x, crd.cursor_x1, crd.cursor_x2); uint clamped_y = clamp(y, crd.cursor_y1, crd.cursor_y2); return one_if_equal_zero_otherwise(x, clamped_x) * one_if_equal_zero_otherwise(y, clamped_y); } -float background_opacity_for(uint bg, uint colorval, float opacity_if_matched) { +float +background_opacity_for(uint bg, uint colorval, float opacity_if_matched) { float not_matched = step(1.0, abs(float(colorval) - float(bg))); return not_matched + opacity_if_matched * (1.0 - not_matched); } -float calc_background_opacity(uint bg) { +float +calc_background_opacity(uint bg) { return ( - background_opacity_for(bg, crd.bg_colors0, crd.bg_opacities0) * - background_opacity_for(bg, crd.bg_colors1, crd.bg_opacities1) * - background_opacity_for(bg, crd.bg_colors2, crd.bg_opacities2) * - background_opacity_for(bg, crd.bg_colors3, crd.bg_opacities3) * - background_opacity_for(bg, crd.bg_colors4, crd.bg_opacities4) * - background_opacity_for(bg, crd.bg_colors5, crd.bg_opacities5) * - background_opacity_for(bg, crd.bg_colors6, crd.bg_opacities6) * - background_opacity_for(bg, crd.bg_colors7, crd.bg_opacities7) - ); + background_opacity_for(bg, crd.bg_colors0, crd.bg_opacities0) * background_opacity_for(bg, crd.bg_colors1, crd.bg_opacities1) * + background_opacity_for(bg, crd.bg_colors2, crd.bg_opacities2) * background_opacity_for(bg, crd.bg_colors3, crd.bg_opacities3) * + background_opacity_for(bg, crd.bg_colors4, crd.bg_opacities4) * background_opacity_for(bg, crd.bg_colors5, crd.bg_opacities5) * + background_opacity_for(bg, crd.bg_colors6, crd.bg_opacities6) * background_opacity_for(bg, crd.bg_colors7, crd.bg_opacities7)); } // }}} @@ -205,20 +207,19 @@ public struct CellData { // plus the intermediate data the cell foreground pass needs. public struct BackgroundOutput { public float4 position; - public float3 background_rgb; // final background color (linear, post cursor/selection) - public float bg_alpha; // final alpha, post special-cell opacity (pre draw_bg_bitfield mask) + public float3 background_rgb; // final background color (linear, post cursor/selection) + public float bg_alpha; // final alpha, post special-cell opacity (pre draw_bg_bitfield mask) public float cell_has_default_bg; - public float3 bg; // original background color, pre selection/cursor (used for selection fg) - public float3 foreground; // resolved foreground color, pre selection/override + public float3 bg; // original background color, pre selection/cursor (used for selection fg) + public float3 foreground; // resolved foreground color, pre selection/override public uint fg_as_uint; public uint text_attrs; public uint is_reversed; public CellData cell_data; }; -public BackgroundOutput compute_background( - uint3 colors, uint2 sprite_idx, uint is_selected, uint instance_id, uint vertex_id -) { +public BackgroundOutput +compute_background(uint3 colors, uint2 sprite_idx, uint is_selected, uint instance_id, uint vertex_id) { BackgroundOutput bo; // set cell color indices {{{ @@ -234,7 +235,7 @@ public BackgroundOutput compute_background( bg_as_uint = has_mark * color_table[NUM_COLORS + mark - 1] + (BIT_MASK - has_mark) * bg_as_uint; float cell_has_default_bg = 1.f - step(1.f, abs(float(bg_as_uint - crd.bg_colors0))); // 1 if has default bg else 0 float3 bg = color_to_vec(bg_as_uint); - float3 cell_bg = bg; // preserve original bg for the selection foreground color + float3 cell_bg = bg; // preserve original bg for the selection foreground color uint fg_as_uint = resolve_color(colors[fg_index], default_colors[fg_index]); fg_as_uint = has_mark * color_table[NUM_COLORS + MARK_MASK + mark] + (1u - has_mark) * fg_as_uint; float3 foreground = color_to_vec(fg_as_uint); @@ -290,7 +291,7 @@ public BackgroundOutput compute_background( float effective_cursor_opacity = max(crd.cursor_opacity, bg_alpha); // is_special_cell is either 0 or 1 float is_special_cell = cell_data.has_block_cursor + float(is_selected & BIT_MASK); - is_special_cell += float(is_reversed); // reverse video cells should be opaque as well + is_special_cell += float(is_reversed); // reverse video cells should be opaque as well is_special_cell = zero_or_one(is_special_cell); cell_has_default_bg = if_one_then(is_special_cell, 0., cell_has_default_bg); @@ -319,7 +320,8 @@ public BackgroundOutput compute_background( // same as the cell background pass but without the draw_bg_bitfield masking, so // the padding is always drawn. The premultiplied alpha ensures padding matches // its neighboring cell under a semi-transparent OS window. -public float4 padding_background_premul(uint3 colors, uint2 sprite_idx, uint is_selected, uint instance_id) { +public float4 +padding_background_premul(uint3 colors, uint2 sprite_idx, uint is_selected, uint instance_id) { BackgroundOutput bo = compute_background(colors, sprite_idx, is_selected, instance_id, 0u); return vec4_premul(bo.background_rgb, bo.bg_alpha); } diff --git a/kitty/shaders/bgimage.slang b/kitty/shaders/bgimage.slang index 1c78560bf..c7890c314 100644 --- a/kitty/shaders/bgimage.slang +++ b/kitty/shaders/bgimage.slang @@ -5,10 +5,10 @@ import alpha_blend; // Constants and Macros -#define left 0 -#define top 1 -#define right 2 -#define bottom 3 +#define left 0 +#define top 1 +#define right 2 +#define bottom 3 #define tex_left 0.0 #define tex_top 0.0 #define tex_right 1.0 @@ -17,12 +17,7 @@ import alpha_blend; #define x_axis 0 #define y_axis 1 -static const float2 tex_map[4] = { - float2(tex_left, tex_top), - float2(tex_left, tex_bottom), - float2(tex_right, tex_bottom), - float2(tex_right, tex_top) -}; +static const float2 tex_map[4] = { float2(tex_left, tex_top), float2(tex_left, tex_bottom), float2(tex_right, tex_bottom), float2(tex_right, tex_top) }; struct VertexOutput { @@ -31,11 +26,13 @@ struct VertexOutput { }; // Helper Functions -float scale_factor(float window_size, float image_size) { +float +scale_factor(float window_size, float image_size) { return window_size / image_size; } -float tiling_factor(int i, float4 sizes, float tiled) { +float +tiling_factor(int i, float4 sizes, float tiled) { int window = i; int image = i + 2; return tiled * scale_factor(sizes[window], sizes[image]) + (1.0 - tiled); @@ -43,12 +40,8 @@ float tiling_factor(int i, float4 sizes, float tiled) { // Main Vertex Shader Entry Point [shader("vertex")] -VertexOutput vertex_main( - uint vertex_id : SV_VertexID, - uniform float tiled, - uniform float4 sizes, - uniform float4 positions, -) { +VertexOutput +vertex_main(uint vertex_id: SV_VertexID, uniform float tiled, uniform float4 sizes, uniform float4 positions, ) { const float2 pos_map[4] = { float2(positions[left], positions[top]), float2(positions[left], positions[bottom]), @@ -60,10 +53,7 @@ VertexOutput vertex_main( VertexOutput output; // Calculate outputs float2 tex_coords = tex_map[vertex_id]; - output.texcoord = float2( - tex_coords[x_axis] * tiling_factor(x_axis, sizes, tiled), - tex_coords[y_axis] * tiling_factor(y_axis, sizes, tiled) - ); + output.texcoord = float2(tex_coords[x_axis] * tiling_factor(x_axis, sizes, tiled), tex_coords[y_axis] * tiling_factor(y_axis, sizes, tiled)); output.position = float4(pos_map[vertex_id], 0.0, 1.0); return output; } @@ -73,7 +63,9 @@ uniform Sampler2D image; // Main Fragment Shader Entry Point [shader("fragment")] -float4 fragment_main(float2 texcoord: TEXCOORD, uniform float4 background) : SV_Target { +float4 +fragment_main(float2 texcoord: TEXCOORD, uniform float4 background) + : SV_Target { // Sample the texture using Slang's intrinsic texture object syntax float4 color = image.Sample(texcoord); // Compute final color with alpha blending diff --git a/kitty/shaders/blit-common.slang b/kitty/shaders/blit-common.slang index 58c7b3b5c..6e3d01bf9 100644 --- a/kitty/shaders/blit-common.slang +++ b/kitty/shaders/blit-common.slang @@ -16,18 +16,13 @@ public struct BlitOutput { #define bottom 3 // Static constant array mapping vertex IDs -static const int2 vertex_pos_map[4] = { - {right, top}, - {right, bottom}, - {left, bottom}, - {left, top} -}; +static const int2 vertex_pos_map[4] = { { right, top }, { right, bottom }, { left, bottom }, { left, top } }; -public BlitOutput get_coords_for_blit(uint vertex_id, float4 src_rect, float4 dest_rect) { +public BlitOutput +get_coords_for_blit(uint vertex_id, float4 src_rect, float4 dest_rect) { int2 pos = vertex_pos_map[vertex_id]; BlitOutput output; output.texcoord = float2(src_rect[pos.x], src_rect[pos.y]); output.position = float2(dest_rect[pos.x], dest_rect[pos.y]); return output; } - diff --git a/kitty/shaders/blit.slang b/kitty/shaders/blit.slang index 0c51cd9d2..7155c19d6 100644 --- a/kitty/shaders/blit.slang +++ b/kitty/shaders/blit.slang @@ -14,19 +14,18 @@ struct VSOutput { [shader("vertex")] -VSOutput vertex_main( - uint vertex_id : SV_VertexID, - uniform float4 src_rect, - uniform float4 dest_rect, -) { +VSOutput +vertex_main(uint vertex_id: SV_VertexID, uniform float4 src_rect, uniform float4 dest_rect, ) { BlitOutput ans = get_coords_for_blit(vertex_id, src_rect, dest_rect); - return {ans.texcoord, float4(ans.position[0], ans.position[1], 0.0, 1.0)}; + return { ans.texcoord, float4(ans.position[0], ans.position[1], 0.0, 1.0) }; } uniform Sampler2D image; [shader("fragment")] -float4 fragment_main(float2 texcoord : TEXCOORD) : SV_Target { +float4 +fragment_main(float2 texcoord: TEXCOORD) + : SV_Target { float4 color_premul = image.Sample(texcoord); return vec4_premul(linear2srgb(color_premul.rgb / color_premul.a), color_premul.a); } diff --git a/kitty/shaders/border.slang b/kitty/shaders/border.slang index bfe96a27d..a373608e2 100644 --- a/kitty/shaders/border.slang +++ b/kitty/shaders/border.slang @@ -34,8 +34,7 @@ struct VertexInput { }; // Vertex shader output structure -struct VertexOutput -{ +struct VertexOutput { float4 color_premul : COLOR_PREMUL; float4 position : SV_Position; }; @@ -47,28 +46,26 @@ static const int RIGHT = 2; static const int BOTTOM = 3; static const uint FF = 0xff; -static const uint2 pos_map[4] = { - uint2(RIGHT, TOP), - uint2(RIGHT, BOTTOM), - uint2(LEFT, BOTTOM), - uint2(LEFT, TOP) -}; +static const uint2 pos_map[4] = { uint2(RIGHT, TOP), uint2(RIGHT, BOTTOM), uint2(LEFT, BOTTOM), uint2(LEFT, TOP) }; -float to_color(uint c) { +float +to_color(uint c) { return gamma_lut[c & FF]; } -float is_integer_value(uint c, int x) { +float +is_integer_value(uint c, int x) { return 1. - step(0.5, abs(float(c) - float(x))); } -float3 as_color_vector(uint c, int shift) { +float3 +as_color_vector(uint c, int shift) { return float3(to_color(c >> shift), to_color(c >> (shift - 8)), to_color(c >> (shift - 16))); } [shader("vertex")] -VertexOutput vertex_main(float4 rect, uint rect_color, uniform float background_opacity, uint vertex_id : SV_VertexID) -{ +VertexOutput +vertex_main(float4 rect, uint rect_color, uniform float background_opacity, uint vertex_id: SV_VertexID) { VertexOutput output; uint2 pos = pos_map[vertex_id]; @@ -83,10 +80,9 @@ VertexOutput vertex_main(float4 rect, uint rect_color, uniform float background_ color3 = if_one_then(is_window_bg, window_bg, color3); // Actual border quads and tab bar edge strips must be always drawn opaque - float is_not_a_border = zero_or_one(abs( - (float(rc) - ACTIVE_BORDER_COLOR) * (float(rc) - INACTIVE_BORDER_COLOR) * (float(rc) - BELL_BORDER_COLOR) * - (float(rc) - TAB_BAR_EDGE_LEFT_COLOR) * (float(rc) - TAB_BAR_EDGE_RIGHT_COLOR) - )); + float is_not_a_border = zero_or_one( + abs((float(rc) - ACTIVE_BORDER_COLOR) * (float(rc) - INACTIVE_BORDER_COLOR) * (float(rc) - BELL_BORDER_COLOR) * (float(rc) - TAB_BAR_EDGE_LEFT_COLOR) * + (float(rc) - TAB_BAR_EDGE_RIGHT_COLOR))); float final_opacity = if_one_then(is_not_a_border, background_opacity, 1.); output.color_premul = vec4_premul(color3, final_opacity); @@ -95,6 +91,8 @@ VertexOutput vertex_main(float4 rect, uint rect_color, uniform float background_ } [shader("fragment")] -float4 fragment_main(float4 color_premul : COLOR_PREMUL) : SV_Target { +float4 +fragment_main(float4 color_premul: COLOR_PREMUL) + : SV_Target { return color_premul; } diff --git a/kitty/shaders/cell.slang b/kitty/shaders/cell.slang index 4d36a1736..931e65c4e 100644 --- a/kitty/shaders/cell.slang +++ b/kitty/shaders/cell.slang @@ -33,7 +33,7 @@ struct VertexOutput { float3 background; float4 effective_background_premul; - float effective_text_alpha; + float effective_text_alpha; float3 sprite_pos; float3 underline_pos; float3 cursor_pos; @@ -42,11 +42,12 @@ struct VertexOutput { float3 cell_foreground; float4 cursor_color_premult; float3 decoration_fg; - float colored_sprite; + float colored_sprite; }; // Foreground utility functions {{{ -uint3 to_sprite_coords(uint idx) { +uint3 +to_sprite_coords(uint idx) { uint sprites_per_page = crd.sprites_xnum * crd.sprites_ynum; uint z = idx / sprites_per_page; uint num_on_last_page = idx - sprites_per_page * z; @@ -55,23 +56,26 @@ uint3 to_sprite_coords(uint idx) { return uint3(x, y, z); } -float3 to_sprite_pos(uint2 pos, uint idx) { +float3 +to_sprite_pos(uint2 pos, uint idx) { uint3 c = to_sprite_coords(idx); float2 s_xpos = float2(float(c.x), float(c.x) + 1.0f) * (1.0f / float(crd.sprites_xnum)); float2 s_ypos = float2(float(c.y), float(c.y) + 1.0f) * (1.0f / float(crd.sprites_ynum)); uint texture_height_px = (crd.cell_height + 1u) * crd.sprites_ynum; float row_height = 1.0f / float(texture_height_px); - s_ypos[1] -= row_height; // skip the decorations_exclude row + s_ypos[1] -= row_height; // skip the decorations_exclude row return float3(s_xpos[pos.x], s_ypos[pos.y], float(c.z)); } -uint to_underline_exclusion_pos(uint2 sprite_idx) { +uint +to_underline_exclusion_pos(uint2 sprite_idx) { uint3 c = to_sprite_coords(sprite_idx[0]); uint cell_top_px = c.y * (crd.cell_height + 1u); return cell_top_px + crd.cell_height; } -uint read_sprite_decorations_idx(uint2 sprite_idx) { +uint +read_sprite_decorations_idx(uint2 sprite_idx) { int idx = int(sprite_idx[0] & SPRITE_INDEX_MASK); int width, height; sprite_decorations_map.GetDimensions(width, height); @@ -81,29 +85,32 @@ uint read_sprite_decorations_idx(uint2 sprite_idx) { return sprite_decorations_map[int2(x, y)].r; } -uint2 get_decorations_indices(uint2 sprite_idx, uint in_url /* [0, 1] */, uint text_attrs) { +uint2 +get_decorations_indices(uint2 sprite_idx, uint in_url /* [0, 1] */, uint text_attrs) { uint decorations_idx = read_sprite_decorations_idx(sprite_idx); uint has_decorations = uint(zero_or_one(float(decorations_idx))); uint strike_style = ((text_attrs >> STRIKE_SHIFT) & BIT_MASK); // 0 or 1 uint strike_idx = decorations_idx * strike_style; uint underline_style = ((text_attrs >> DECORATION_SHIFT) & DECORATION_MASK); underline_style = in_url * crd.url_style + (1u - in_url) * underline_style; // [0, 5] - uint has_underline = uint(step(0.5f, float(underline_style))); // [0, 1] + uint has_underline = uint(step(0.5f, float(underline_style))); // [0, 1] return has_decorations * uint2(strike_idx, has_underline * (decorations_idx + underline_style)); } // }}} // Override foreground colors {{{ -float3 fg_override_luminance(float colored_sprite, float under_luminance, float over_lumininace, float3 under, float3 over) { +float3 +fg_override_luminance(float colored_sprite, float under_luminance, float over_lumininace, float3 under, float3 over) { // If the difference in luminance is too small, // force the foreground color to be black or white. float diff_luminance = abs(under_luminance - over_lumininace); - float override_level = (1.f - colored_sprite) * step(diff_luminance, crd.fg_override_threshold); - float original_level = 1.f - override_level; - return original_level * over + override_level * float3(step(under_luminance, 0.5f)); + float override_level = (1.f - colored_sprite) * step(diff_luminance, crd.fg_override_threshold); + float original_level = 1.f - override_level; + return original_level * over + override_level * float3(step(under_luminance, 0.5f)); } -float3 fg_override_contrast(float under_luminance, float over_luminance, float3 under, float3 over) { +float3 +fg_override_contrast(float under_luminance, float over_luminance, float3 under, float3 over) { float ratio = contrast_ratio(under_luminance, over_luminance); float3 diff = abs(under - over); float3 over_hsluv = rgbToHsluv(over); @@ -119,7 +126,8 @@ float3 fg_override_contrast(float under_luminance, float over_luminance, float3 return lerp(result, over, fallback_condition); } -float3 override_foreground_color(float3 over, float3 under, float colored_sprite) { +float3 +override_foreground_color(float3 over, float3 under, float colored_sprite) { float under_luminance = dot(under, Y); float over_lumininace = dot(over.rgb, Y); if (FG_OVERRIDE_ALGO == 1) return fg_override_luminance(colored_sprite, under_luminance, over_lumininace, under, over); @@ -128,14 +136,14 @@ float3 override_foreground_color(float3 over, float3 under, float colored_sprite // }}} [shader("vertex")] -VertexOutput vertex_main( +VertexOutput +vertex_main( [[vk::location(0)]] uint3 colors, [[vk::location(1)]] uint2 sprite_idx, - [[vk::location(2)]] uint is_selected, - uint vertex_id : SV_VertexID, - uint instance_id : SV_InstanceID, - uniform uint draw_bg_bitfield, -) { + [[vk::location(2)]] uint is_selected, + uint vertex_id: SV_VertexID, + uint instance_id: SV_InstanceID, + uniform uint draw_bg_bitfield, ) { VertexOutput vo; BackgroundOutput bo = compute_background(colors, sprite_idx, is_selected, instance_id, vertex_id); @@ -153,8 +161,7 @@ VertexOutput vertex_main( vo.colored_sprite = float((sprite_idx[0] & SPRITE_COLORED_MASK) >> SPRITE_COLORED_SHIFT); float has_dim = float((text_attrs >> DIM_SHIFT) & BIT_MASK), has_blink = float((text_attrs >> BLINK_SHIFT) & BIT_MASK); - vo.effective_text_alpha = crd.inactive_text_alpha * if_one_then(has_dim, crd.dim_opacity, 1.0) * if_one_then( - has_blink, crd.blink_opacity, 1.0); + vo.effective_text_alpha = crd.inactive_text_alpha * if_one_then(has_dim, crd.dim_opacity, 1.0) * if_one_then(has_blink, crd.blink_opacity, 1.0); float in_url = float((is_selected >> 1) & BIT_MASK); vo.decoration_fg = if_one_then(in_url, color_to_vec(crd.url_color), to_color(colors[2], fg_as_uint)); // Selection @@ -203,21 +210,25 @@ uniform Sampler2DArray sprites; // Scaling factor for the extra text-alpha adjustment for luminance-difference. static const float text_gamma_scaling = 0.5; -float clamp_to_unit_float(float x) { +float +clamp_to_unit_float(float x) { // Clamp value to suitable output range return clamp(x, 0.0f, 1.0f); } -float4 foreground_contrast_new(float4 over, float3 under, float text_contrast, float text_gamma_adjustment) { +float4 +foreground_contrast_new(float4 over, float3 under, float text_contrast, float text_gamma_adjustment) { float under_luminance = dot(under, Y); float over_lumininace = dot(over.rgb, Y); // Apply additional gamma-adjustment scaled by the luminance difference, the darker the foreground the more adjustment we apply. // A multiplicative contrast is also available to increase saturation. - over.a = clamp_to_unit_float(lerp(over.a, pow(over.a, text_gamma_adjustment), (1 - over_lumininace + under_luminance) * text_gamma_scaling) * text_contrast); + over.a = + clamp_to_unit_float(lerp(over.a, pow(over.a, text_gamma_adjustment), (1 - over_lumininace + under_luminance) * text_gamma_scaling) * text_contrast); return over; } -float4 foreground_contrast_old(float4 over, float3 under) { +float4 +foreground_contrast_old(float4 over, float3 under) { // Simulation of gamma-incorrect blending float under_luminance = dot(under, Y); float over_lumininace = dot(over.rgb, Y); @@ -225,27 +236,37 @@ float4 foreground_contrast_old(float4 over, float3 under) { // // linear2srgb(over * overA2 + under * (1 - overA2)) = linear2srgb(over) * over.a + linear2srgb(under) * (1 - over.a) // ^ gamma correct blending with new alpha ^ gamma incorrect blending with old alpha - over.a = clamp_to_unit_float((srgb2linear(linear2srgb(over_lumininace) * over.a + linear2srgb(under_luminance) * (1.0f - over.a)) - under_luminance) / (over_lumininace - under_luminance)); + over.a = clamp_to_unit_float( + (srgb2linear(linear2srgb(over_lumininace) * over.a + linear2srgb(under_luminance) * (1.0f - over.a)) - under_luminance) / + (over_lumininace - under_luminance)); return over; } -float4 foreground_contrast(float4 over, float3 under, float text_contrast, float text_gamma_adjustment) { +float4 +foreground_contrast(float4 over, float3 under, float text_contrast, float text_gamma_adjustment) { if (TEXT_NEW_GAMMA) return foreground_contrast_new(over, under, text_contrast, text_gamma_adjustment); return foreground_contrast_old(over, under); } -float4 load_text_foreground_color(float3 sprite_pos, float colored_sprite, float3 cell_foreground) { +float4 +load_text_foreground_color(float3 sprite_pos, float colored_sprite, float3 cell_foreground) { // For colored sprites use the color from the sprite rather than the text foreground // Return non-premultiplied foreground color float4 text_fg = sprites.Sample(sprite_pos); return float4(lerp(cell_foreground, text_fg.xyz, colored_sprite), text_fg.w); } -float4 calculate_premul_foreground_from_sprites( - float3 sprite_pos, float3 underline_pos, float3 cursor_pos, float3 strike_pos, uint underline_exclusion_pos, - float4 text_fg, float3 decoration_fg, float4 cursor_color_premult, - float effective_text_alpha, -) { +float4 +calculate_premul_foreground_from_sprites( + float3 sprite_pos, + float3 underline_pos, + float3 cursor_pos, + float3 strike_pos, + uint underline_exclusion_pos, + float4 text_fg, + float3 decoration_fg, + float4 cursor_color_premult, + float effective_text_alpha, ) { // Return premul foreground color from decorations (cursor, underline, strikethrough) int width, height, layer; sprites.GetDimensions(width, height, layer); @@ -261,24 +282,20 @@ float4 calculate_premul_foreground_from_sprites( float combined_alpha = min(text_fg.w + strike_alpha, 1.0); - float4 ans = alpha_blend( - float4(text_fg.rgb, combined_alpha * effective_text_alpha), - float4(decoration_fg, underline_alpha * effective_text_alpha) - ); + float4 ans = alpha_blend(float4(text_fg.rgb, combined_alpha * effective_text_alpha), float4(decoration_fg, underline_alpha * effective_text_alpha)); return lerp(ans, cursor_color_premult, cursor_alpha * cursor_color_premult.w); } -float4 adjust_foreground_contrast_with_background(float4 text_fg, float3 bg, float text_contrast, float text_gamma_adjustment) { +float4 +adjust_foreground_contrast_with_background(float4 text_fg, float3 bg, float text_contrast, float text_gamma_adjustment) { return foreground_contrast(text_fg, bg, text_contrast, text_gamma_adjustment); } [shader("fragment")] -float4 fragment_main( - VertexOutput vo, - uniform float text_contrast, - uniform float text_gamma_adjustment, -) : SV_Target { +float4 +fragment_main(VertexOutput vo, uniform float text_contrast, uniform float text_gamma_adjustment, ) + : SV_Target { float4 ans_premul = 0; if (!ONLY_FOREGROUND) ans_premul = vo.effective_background_premul; @@ -287,8 +304,15 @@ float4 fragment_main( float4 text_fg = load_text_foreground_color(vo.sprite_pos, vo.colored_sprite, vo.cell_foreground); text_fg = adjust_foreground_contrast_with_background(text_fg, vo.background, text_contrast, text_gamma_adjustment); float4 text_fg_premul = calculate_premul_foreground_from_sprites( - vo.sprite_pos, vo.underline_pos, vo.cursor_pos, vo.strike_pos, vo.underline_exclusion_pos, - text_fg, vo.decoration_fg, vo.cursor_color_premult, vo.effective_text_alpha); + vo.sprite_pos, + vo.underline_pos, + vo.cursor_pos, + vo.strike_pos, + vo.underline_exclusion_pos, + text_fg, + vo.decoration_fg, + vo.cursor_color_premult, + vo.effective_text_alpha); if (ONLY_FOREGROUND) ans_premul = text_fg_premul; else ans_premul = alpha_blend_premul(text_fg_premul, ans_premul); } diff --git a/kitty/shaders/custom/bloom.slang b/kitty/shaders/custom/bloom.slang index 68e5d42f3..2766b024d 100644 --- a/kitty/shaders/custom/bloom.slang +++ b/kitty/shaders/custom/bloom.slang @@ -10,30 +10,30 @@ import kitty_custom_shader_types; // Golden spiral samples [x, y, weight] — weight is inverse of distance static const float3 samples[24] = { - float3(0.1693761725038636, 0.9855514761735895, 1.0), - float3(-1.333070830962943, 0.4721463328627773, 0.7071067811865475), - float3(-0.8464394909806497, -1.51113870578065, 0.5773502691896258), - float3(1.554155680728463, -1.2588090085709776, 0.5), - float3(1.681364377589461, 1.4741145918052656, 0.4472135954999579), - float3(-1.2795157692199817, 2.088741103228784, 0.4082482904638631), + float3(0.1693761725038636, 0.9855514761735895, 1.0), + float3(-1.333070830962943, 0.4721463328627773, 0.7071067811865475), + float3(-0.8464394909806497, -1.51113870578065, 0.5773502691896258), + float3(1.554155680728463, -1.2588090085709776, 0.5), + float3(1.681364377589461, 1.4741145918052656, 0.4472135954999579), + float3(-1.2795157692199817, 2.088741103228784, 0.4082482904638631), float3(-2.4575847530631187, -0.9799373355024756, 0.3779644730092272), - float3(0.5874641440200847, -2.7667464429345077, 0.35355339059327373), - float3(2.997715703369726, 0.11704939884745152, 0.3333333333333333), - float3(0.41360842451688395, 3.1351121305574803, 0.31622776601683794), - float3(-3.167149933769243, 0.9844599011770256, 0.30151134457776363), + float3(0.5874641440200847, -2.7667464429345077, 0.35355339059327373), + float3(2.997715703369726, 0.11704939884745152, 0.3333333333333333), + float3(0.41360842451688395, 3.1351121305574803, 0.31622776601683794), + float3(-3.167149933769243, 0.9844599011770256, 0.30151134457776363), float3(-1.5736713846521535, -3.0860263079123245, 0.2886751345948129), - float3(2.888202648340422, -2.1583061557896213, 0.2773500981126146), - float3(2.7150778983300325, 2.5745586041105715, 0.2672612419124244), - float3(-2.1504069972377464, 3.2211410627650165, 0.2581988897471611), + float3(2.888202648340422, -2.1583061557896213, 0.2773500981126146), + float3(2.7150778983300325, 2.5745586041105715, 0.2672612419124244), + float3(-2.1504069972377464, 3.2211410627650165, 0.2581988897471611), float3(-3.6548858794907493, -1.6253643308191343, 0.25), - float3(1.0130775986052671, -3.9967078676335834, 0.24253562503633297), - float3(4.229723673607257, 0.33081361055181563, 0.23570226039551587), - float3(0.40107790291173834, 4.340407413572593, 0.22941573387056174), - float3(-4.319124570236028, 1.159811599693438, 0.22360679774997896), - float3(-1.9209044802827355, -4.160543952132907, 0.2182178902359924), - float3(3.8639122286635708, -2.6589814382925123, 0.21320071635561041), - float3(3.3486228404946234, 3.4331800232609, 0.20851441405707477), - float3(-2.8769733643574344, 3.9652268864187157, 0.20412414523193154) + float3(1.0130775986052671, -3.9967078676335834, 0.24253562503633297), + float3(4.229723673607257, 0.33081361055181563, 0.23570226039551587), + float3(0.40107790291173834, 4.340407413572593, 0.22941573387056174), + float3(-4.319124570236028, 1.159811599693438, 0.22360679774997896), + float3(-1.9209044802827355, -4.160543952132907, 0.2182178902359924), + float3(3.8639122286635708, -2.6589814382925123, 0.21320071635561041), + float3(3.3486228404946234, 3.4331800232609, 0.20851441405707477), + float3(-2.8769733643574344, 3.9652268864187157, 0.20412414523193154) }; // minimum luminance for a pixel to contribute bloom @@ -41,15 +41,13 @@ static const float BLOOM_THRESHOLD = 0.2f; // strength of the bloom contribution static const float BLOOM_STRENGTH = 0.2f; -float lum(float4 c) { +float +lum(float4 c) { return 0.299f * c.r + 0.587f * c.g + 0.114f * c.b; } -public float4 fragment_main( - float4 color, - KittyTextures t, - KittyCustomShaderData d -) { +public float4 +fragment_main(float4 color, KittyTextures t, KittyCustomShaderData d) { float2 uv = t.pos; // One pixel in backbuffer UV space; scale by sqrt(2) to match original step diff --git a/kitty/shaders/custom/crt.slang b/kitty/shaders/custom/crt.slang index e38ab095d..835df6c30 100644 --- a/kitty/shaders/custom/crt.slang +++ b/kitty/shaders/custom/crt.slang @@ -9,15 +9,12 @@ import kitty_custom_shader_types; -static const float WARP = 0.25f; // simulates curvature of CRT monitor -static const float SCAN = 0.50f; // simulates darkness between scanlines -static const float4 TINT = float4(0, 0.8, 0.6, 0); // tint color set the fourth component to 1 to apply it fully +static const float WARP = 0.25f; // simulates curvature of CRT monitor +static const float SCAN = 0.50f; // simulates darkness between scanlines +static const float4 TINT = float4(0, 0.8, 0.6, 0); // tint color set the fourth component to 1 to apply it fully -public float4 fragment_main( - float4 color, - KittyTextures t, - KittyCustomShaderData d -) { +public float4 +fragment_main(float4 color, KittyTextures t, KittyCustomShaderData d) { // UV within the current viewport (0..1) float2 uv = t.pos; @@ -26,8 +23,12 @@ public float4 fragment_main( dc *= dc; // Warp UV to simulate CRT screen curvature - uv.x -= 0.5f; uv.x *= 1.0f + (dc.y * (0.3f * WARP)); uv.x += 0.5f; - uv.y -= 0.5f; uv.y *= 1.0f + (dc.x * (0.4f * WARP)); uv.y += 0.5f; + uv.x -= 0.5f; + uv.x *= 1.0f + (dc.y * (0.3f * WARP)); + uv.x += 0.5f; + uv.y -= 0.5f; + uv.y *= 1.0f + (dc.x * (0.4f * WARP)); + uv.y += 0.5f; // Original Y pixel coordinate within the viewport (for scanline phase) float fragCoordY = (t.pos.y - t.viewport.y) * float(d.viewport_size_pixels.y); diff --git a/kitty/shaders/custom/cursor-trail-blaze.slang b/kitty/shaders/custom/cursor-trail-blaze.slang index 1dcff5c3c..80ade18a1 100644 --- a/kitty/shaders/custom/cursor-trail-blaze.slang +++ b/kitty/shaders/custom/cursor-trail-blaze.slang @@ -9,49 +9,54 @@ import kitty_custom_shader_types; -static const float4 TRAIL_COLOR = float4(1.0, 0.725, 0.161, 1.0); -static const float4 TRAIL_COLOR_ACCENT = float4(1.0, 0.0, 0.0, 1.0); +static const float4 TRAIL_COLOR = float4(1.0, 0.725, 0.161, 1.0); +static const float4 TRAIL_COLOR_ACCENT = float4(1.0, 0.0, 0.0, 1.0); // Trail lifetime in seconds. -static const float DURATION = 0.5; +static const float DURATION = 0.5; // pow(1 - x, 10) via repeated squaring — branchless ease-out. -static float ease_out10(float x) { - float y = 1.0 - x; +static float +ease_out10(float x) { + float y = 1.0 - x; float y2 = y * y; float y4 = y2 * y2; return y4 * y4 * y2; } // Smooth blend curve for the progress value. -static float blend_smooth(float t) { +static float +blend_smooth(float t) { float s = t * t; return s / (2.0 * (s - t) + 1.0); } // SDF of an axis-aligned box. p = query point, center = box centre, hext = half-extents. -static float sdBox(float2 p, float2 center, float2 hext) { +static float +sdBox(float2 p, float2 center, float2 hext) { float2 d = abs(p - center) - hext; return length(max(d, float2(0.0))) + min(max(d.x, d.y), 0.0); } // Single edge contribution for the quadrilateral SDF (signed winding accumulation). -static float seg(float2 p, float2 a, float2 b, inout float s, float d) { - float2 e = b - a; - float2 w = p - a; +static float +seg(float2 p, float2 a, float2 b, inout float s, float d) { + float2 e = b - a; + float2 w = p - a; float2 proj = a + e * clamp(dot(w, e) / dot(e, e), 0.0, 1.0); d = min(d, dot(p - proj, p - proj)); float c0 = step(0.0, p.y - a.y); float c1 = 1.0 - step(0.0, p.y - b.y); float c2 = 1.0 - step(0.0, e.x * w.y - e.y * w.x); - float allCond = c0 * c1 * c2; + float allCond = c0 * c1 * c2; float noneCond = (1.0 - c0) * (1.0 - c1) * (1.0 - c2); s *= lerp(1.0, -1.0, step(0.5, allCond + noneCond)); return d; } // SDF of an arbitrary quadrilateral (uses winding number via four edge contributions). -static float sdQuad(float2 p, float2 v0, float2 v1, float2 v2, float2 v3) { +static float +sdQuad(float2 p, float2 v0, float2 v1, float2 v2, float2 v3) { float s = 1.0; float d = dot(p - v0, p - v0); d = seg(p, v0, v3, s, d); @@ -63,17 +68,15 @@ static float sdQuad(float2 p, float2 v0, float2 v1, float2 v2, float2 v3) { // Returns 0 for diagonal movement (up-left / down-right), 1 otherwise. // Selects which corner of the parallelogram aligns with the movement direction. -static float movementVertexFactor(float2 a, float2 b) { +static float +movementVertexFactor(float2 a, float2 b) { float c1 = step(b.x, a.x) * step(a.y, b.y); float c2 = step(a.x, b.x) * step(b.y, a.y); return 1.0 - max(c1, c2); } -public float4 fragment_main( - float4 color, - KittyTextures t, - KittyCustomShaderData d -) { +public float4 +fragment_main(float4 color, KittyTextures t, KittyCustomShaderData d) { if (d.cursor_trail_color.a <= 0.0) return color; float age = clamp((d.timestamp - d.cursor_trail_change_time) / DURATION, 0.0, 1.0); @@ -84,39 +87,38 @@ public float4 fragment_main( // Work in pixel space for correct aspect-ratio distance measurements. float2 pixelDims = float2(d.viewport_size_pixels) * t.viewport.zw; - float2 px = t.pos * pixelDims; + float2 px = t.pos * pixelDims; // Current cursor rect in pixel space (edge: .x=left .y=right .z=top .w=bottom, y-up). - float curLeft = d.cursor_trail_edge.x * pixelDims.x; - float curRight = d.cursor_trail_edge.y * pixelDims.x; - float curTop = d.cursor_trail_edge.z * pixelDims.y; + float curLeft = d.cursor_trail_edge.x * pixelDims.x; + float curRight = d.cursor_trail_edge.y * pixelDims.x; + float curTop = d.cursor_trail_edge.z * pixelDims.y; float curBottom = d.cursor_trail_edge.w * pixelDims.y; - float curWidth = curRight - curLeft; - float curHeight = curTop - curBottom; + float curWidth = curRight - curLeft; + float curHeight = curTop - curBottom; float2 curCenter = float2((curLeft + curRight) * 0.5, (curTop + curBottom) * 0.5); - float prevLeft = d.cursor_trail_prev_edge.x * pixelDims.x; - float prevTop = d.cursor_trail_prev_edge.z * pixelDims.y; + float prevLeft = d.cursor_trail_prev_edge.x * pixelDims.x; + float prevTop = d.cursor_trail_prev_edge.z * pixelDims.y; float prevBottom = d.cursor_trail_prev_edge.w * pixelDims.y; float2 prevCenter = float2( (d.cursor_trail_prev_edge.x + d.cursor_trail_prev_edge.y) * 0.5 * pixelDims.x, - (d.cursor_trail_prev_edge.z + d.cursor_trail_prev_edge.w) * 0.5 * pixelDims.y - ); + (d.cursor_trail_prev_edge.z + d.cursor_trail_prev_edge.w) * 0.5 * pixelDims.y); float lineLength = length(curCenter - prevCenter); // Alpha fades to 0 at the previous-cursor end, reaching full effect at the current cursor. float alphaModifier = min(length(px - curCenter) / (lineLength * easedProgress), 1.0); // Build parallelogram vertices in pixel space. - float vf = movementVertexFactor(curCenter, prevCenter); + float vf = movementVertexFactor(curCenter, prevCenter); float ivf = 1.0 - vf; - float2 v0 = float2(lerp(curLeft, curRight, vf), curBottom); + float2 v0 = float2(lerp(curLeft, curRight, vf), curBottom); float2 v1 = float2(lerp(curLeft, curRight, ivf), curTop); - float2 v2 = float2(prevLeft + curWidth * ivf, prevTop); - float2 v3 = float2(prevLeft + curWidth * vf, prevBottom); + float2 v2 = float2(prevLeft + curWidth * ivf, prevTop); + float2 v3 = float2(prevLeft + curWidth * vf, prevBottom); float sdfCursor = sdBox(px, curCenter, float2(curWidth, curHeight) * 0.5); - float sdfTrail = sdQuad(px, v0, v1, v2, v3); + float sdfTrail = sdQuad(px, v0, v1, v2, v3); // ~2-pixel anti-aliasing range in pixel space. float aaRange = 2.0; diff --git a/kitty/shaders/custom/cursor-trail-default.slang b/kitty/shaders/custom/cursor-trail-default.slang index bc07738b6..9a446d3aa 100644 --- a/kitty/shaders/custom/cursor-trail-default.slang +++ b/kitty/shaders/custom/cursor-trail-default.slang @@ -20,8 +20,12 @@ import kitty_custom_shader_types; // Returns true when uv is inside the convex quad c0→c1→c2→c3 (clockwise, y-up). // Corner order from cursor_trail_corners_*: 0=top-right, 1=bottom-right, 2=bottom-left, 3=top-left. -bool inside_trail_quad(float2 uv, float2 c0, float2 c1, float2 c2, float2 c3) { - float2 e0 = c1 - c0; float2 e1 = c2 - c1; float2 e2 = c3 - c2; float2 e3 = c0 - c3; +bool +inside_trail_quad(float2 uv, float2 c0, float2 c1, float2 c2, float2 c3) { + float2 e0 = c1 - c0; + float2 e1 = c2 - c1; + float2 e2 = c3 - c2; + float2 e3 = c0 - c3; float s0 = e0.x * (uv.y - c0.y) - e0.y * (uv.x - c0.x); float s1 = e1.x * (uv.y - c1.y) - e1.y * (uv.x - c1.x); float s2 = e2.x * (uv.y - c2.y) - e2.y * (uv.x - c2.x); @@ -29,27 +33,24 @@ bool inside_trail_quad(float2 uv, float2 c0, float2 c1, float2 c2, float2 c3) { return s0 <= 0.0 && s1 <= 0.0 && s2 <= 0.0 && s3 <= 0.0; } -public float4 fragment_main( - float4 color, - KittyTextures t, - KittyCustomShaderData d -) { +public float4 +fragment_main(float4 color, KittyTextures t, KittyCustomShaderData d) { float opacity = d.cursor_trail_color.a; if (opacity <= 0.0) return color; float2 uv = t.pos; - float2 c0 = float2(d.cursor_trail_corners_x.x, d.cursor_trail_corners_y.x); // top-right - float2 c1 = float2(d.cursor_trail_corners_x.y, d.cursor_trail_corners_y.y); // bottom-right - float2 c2 = float2(d.cursor_trail_corners_x.z, d.cursor_trail_corners_y.z); // bottom-left - float2 c3 = float2(d.cursor_trail_corners_x.w, d.cursor_trail_corners_y.w); // top-left + float2 c0 = float2(d.cursor_trail_corners_x.x, d.cursor_trail_corners_y.x); // top-right + float2 c1 = float2(d.cursor_trail_corners_x.y, d.cursor_trail_corners_y.y); // bottom-right + float2 c2 = float2(d.cursor_trail_corners_x.z, d.cursor_trail_corners_y.z); // bottom-left + float2 c3 = float2(d.cursor_trail_corners_x.w, d.cursor_trail_corners_y.w); // top-left if (!inside_trail_quad(uv, c0, c1, c2, c3)) return color; // Mask out the cursor's current rectangle so the real cursor renders cleanly on top. // cursor_trail_edge: .x=left, .y=right, .z=top, .w=bottom (UV, y-up) - float inside_cursor = step(d.cursor_trail_edge.x, uv.x) * step(uv.x, d.cursor_trail_edge.y) - * step(d.cursor_trail_edge.w, uv.y) * step(uv.y, d.cursor_trail_edge.z); + float inside_cursor = + step(d.cursor_trail_edge.x, uv.x) * step(uv.x, d.cursor_trail_edge.y) * step(d.cursor_trail_edge.w, uv.y) * step(uv.y, d.cursor_trail_edge.z); opacity *= 1.0 - inside_cursor; if (opacity <= 0.0) return color; diff --git a/kitty/shaders/custom/cursor-trail-lightning.slang b/kitty/shaders/custom/cursor-trail-lightning.slang index 29c79f91e..4bfc4924e 100644 --- a/kitty/shaders/custom/cursor-trail-lightning.slang +++ b/kitty/shaders/custom/cursor-trail-lightning.slang @@ -7,27 +7,29 @@ import kitty_custom_shader_types; -static const float3 BOLT_COLOR = float3(1.00, 0.82, 0.72); -static const float3 GLOW_COLOR = float3(0.74, 0.00, 0.00); +static const float3 BOLT_COLOR = float3(1.00, 0.82, 0.72); +static const float3 GLOW_COLOR = float3(0.74, 0.00, 0.00); // Bolt lifetime in seconds. -static const float DURATION = 0.14; +static const float DURATION = 0.14; // Sideways displacement relative to bolt length. -static const float JAGGEDNESS = 0.085; +static const float JAGGEDNESS = 0.085; // Core stroke width in pixels. -static const float CORE_WIDTH = 1.15; +static const float CORE_WIDTH = 1.15; // Glow radius in pixels. -static const float GLOW_WIDTH = 5.5; +static const float GLOW_WIDTH = 5.5; // Number of linear segments that make up the main bolt. -static const int BOLT_SEGMENTS = 12; +static const int BOLT_SEGMENTS = 12; -static float hash11(float p) { +static float +hash11(float p) { p = frac(p * 0.1031); p *= p + 33.33; p *= p + p; return frac(p); } -static float segDist(float2 p, float2 a, float2 b) { +static float +segDist(float2 p, float2 a, float2 b) { float2 ab = b - a; float t = clamp(dot(p - a, ab) / max(dot(ab, ab), 1e-7), 0.0, 1.0); return length(p - (a + ab * t)); @@ -35,23 +37,25 @@ static float segDist(float2 p, float2 a, float2 b) { // A point on the piecewise-linear bolt. Endpoints stay fixed; interior nodes // are displaced perpendicular to the travel direction. -static float2 boltPoint(float2 a, float2 b, float t, float seed) { - float2 delta = b - a; - float boltLength = length(delta); - float2 perp = float2(-delta.y, delta.x) / max(boltLength, 1e-5); - float node = floor(t * float(BOLT_SEGMENTS) + 0.5); - float rnd = hash11(node * 17.17 + seed) * 2.0 - 1.0; - float fade = sin(3.14159265 * t); +static float2 +boltPoint(float2 a, float2 b, float t, float seed) { + float2 delta = b - a; + float boltLength = length(delta); + float2 perp = float2(-delta.y, delta.x) / max(boltLength, 1e-5); + float node = floor(t * float(BOLT_SEGMENTS) + 0.5); + float rnd = hash11(node * 17.17 + seed) * 2.0 - 1.0; + float fade = sin(3.14159265 * t); return lerp(a, b, t) + perp * rnd * boltLength * JAGGEDNESS * fade; } // Minimum distance from p to the main bolt's piecewise-linear path. -static float mainBoltDist(float2 p, float2 a, float2 b, float seed) { - float minD = 1e5; +static float +mainBoltDist(float2 p, float2 a, float2 b, float seed) { + float minD = 1e5; float2 prev = a; [ForceUnroll] for (int i = 1; i <= BOLT_SEGMENTS; i++) { - float t = float(i) / float(BOLT_SEGMENTS); + float t = float(i) / float(BOLT_SEGMENTS); float2 cur = boltPoint(a, b, t, seed); minD = min(minD, segDist(p, prev, cur)); prev = cur; @@ -60,49 +64,41 @@ static float mainBoltDist(float2 p, float2 a, float2 b, float seed) { } // Three small forks growing out of alternating points on the main bolt. -static float forkDist(float2 p, float2 a, float2 b, float seed) { - float minD = 1e5; - float2 delta = b - a; - float boltLength = length(delta); - float2 perp = float2(-delta.y, delta.x) / max(boltLength, 1e-5); - float2 dir = normalize(delta); +static float +forkDist(float2 p, float2 a, float2 b, float seed) { + float minD = 1e5; + float2 delta = b - a; + float boltLength = length(delta); + float2 perp = float2(-delta.y, delta.x) / max(boltLength, 1e-5); + float2 dir = normalize(delta); [ForceUnroll] for (int i = 0; i < 3; i++) { - float fi = float(i); - float t = 0.28 + fi * 0.22; - float2 root = boltPoint(a, b, t, seed); - float side = lerp(-1.0, 1.0, step(0.5, hash11(seed + fi * 41.0))); - float reach = boltLength * (0.055 + 0.035 * hash11(seed + fi * 53.0)); + float fi = float(i); + float t = 0.28 + fi * 0.22; + float2 root = boltPoint(a, b, t, seed); + float side = lerp(-1.0, 1.0, step(0.5, hash11(seed + fi * 41.0))); + float reach = boltLength * (0.055 + 0.035 * hash11(seed + fi * 53.0)); float2 along = dir * reach * (hash11(seed + fi * 67.0) - 0.35); - float2 tip = root + perp * side * reach + along; - float2 elbow = lerp(root, tip, 0.52) - - perp * side * reach * (0.12 + 0.18 * hash11(seed + fi * 79.0)); + float2 tip = root + perp * side * reach + along; + float2 elbow = lerp(root, tip, 0.52) - perp * side * reach * (0.12 + 0.18 * hash11(seed + fi * 79.0)); minD = min(minD, segDist(p, root, elbow)); minD = min(minD, segDist(p, elbow, tip)); } return minD; } -public float4 fragment_main( - float4 color, - KittyTextures t, - KittyCustomShaderData d -) { +public float4 +fragment_main(float4 color, KittyTextures t, KittyCustomShaderData d) { float age = clamp((d.timestamp - d.cursor_trail_change_time) / DURATION, 0.0, 1.0); if (age >= 1.0) return color; float2 pixelDims = float2(d.viewport_size_pixels) * t.viewport.zw; // Cursor centres in pixel space (edge: .x=left .y=right .z=top .w=bottom, y-up). - float2 curCenter = float2( - (d.cursor_trail_edge.x + d.cursor_trail_edge.y) * 0.5, - (d.cursor_trail_edge.z + d.cursor_trail_edge.w) * 0.5 - ) * pixelDims; - float2 prevCenter = float2( - (d.cursor_trail_prev_edge.x + d.cursor_trail_prev_edge.y) * 0.5, - (d.cursor_trail_prev_edge.z + d.cursor_trail_prev_edge.w) * 0.5 - ) * pixelDims; + float2 curCenter = float2((d.cursor_trail_edge.x + d.cursor_trail_edge.y) * 0.5, (d.cursor_trail_edge.z + d.cursor_trail_edge.w) * 0.5) * pixelDims; + float2 prevCenter = + float2((d.cursor_trail_prev_edge.x + d.cursor_trail_prev_edge.y) * 0.5, (d.cursor_trail_prev_edge.z + d.cursor_trail_prev_edge.w) * 0.5) * pixelDims; float cursorWidth = (d.cursor_trail_edge.y - d.cursor_trail_edge.x) * pixelDims.x; @@ -114,25 +110,25 @@ public float4 fragment_main( float mainD = mainBoltDist(px, prevCenter, curCenter, seed); float forkD = forkDist(px, prevCenter, curCenter, seed); - float pixel = max(fwidth(mainD), 0.65); - float core = 1.0 - smoothstep(CORE_WIDTH, CORE_WIDTH + pixel, mainD); + float pixel = max(fwidth(mainD), 0.65); + float core = 1.0 - smoothstep(CORE_WIDTH, CORE_WIDTH + pixel, mainD); float forkCore = 1.0 - smoothstep(CORE_WIDTH * 0.72, CORE_WIDTH * 0.72 + pixel, forkD); - float glow = exp(-mainD * mainD / (GLOW_WIDTH * GLOW_WIDTH)); + float glow = exp(-mainD * mainD / (GLOW_WIDTH * GLOW_WIDTH)); float forkGlow = exp(-forkD * forkD / (GLOW_WIDTH * GLOW_WIDTH * 0.55)); // Flash hard, then rapidly lose the core, leaving only glow. - float flash = 1.0 - smoothstep(0.0, 1.0, age); + float flash = 1.0 - smoothstep(0.0, 1.0, age); flash *= flash; - float flicker = 0.82 + 0.18 * sin(age * 95.0 + seed); - float glowAmt = max(glow, forkGlow * 0.55) * flash * flicker * 0.72; - float coreAmt = max(core, forkCore * 0.82) * flash * flicker; + float flicker = 0.82 + 0.18 * sin(age * 95.0 + seed); + float glowAmt = max(glow, forkGlow * 0.55) * flash * flicker * 0.72; + float coreAmt = max(core, forkCore * 0.82) * flash * flicker; float3 rgb = lerp(color.rgb, GLOW_COLOR, clamp(glowAmt, 0.0, 1.0)); - rgb = lerp(rgb, BOLT_COLOR, clamp(coreAmt, 0.0, 1.0)); + rgb = lerp(rgb, BOLT_COLOR, clamp(coreAmt, 0.0, 1.0)); // Keep the cursor crisp above the effect. - float insideCursor = step(d.cursor_trail_edge.x, t.pos.x) * step(t.pos.x, d.cursor_trail_edge.y) - * step(d.cursor_trail_edge.w, t.pos.y) * step(t.pos.y, d.cursor_trail_edge.z); + float insideCursor = step(d.cursor_trail_edge.x, t.pos.x) * step(t.pos.x, d.cursor_trail_edge.y) * step(d.cursor_trail_edge.w, t.pos.y) * + step(t.pos.y, d.cursor_trail_edge.z); rgb = lerp(rgb, color.rgb, insideCursor); return float4(rgb, color.a); diff --git a/kitty/shaders/custom/fireworks-rockets.slang b/kitty/shaders/custom/fireworks-rockets.slang index 6ce46cddb..1a2bfd31e 100644 --- a/kitty/shaders/custom/fireworks-rockets.slang +++ b/kitty/shaders/custom/fireworks-rockets.slang @@ -7,32 +7,36 @@ import kitty_custom_shader_types; -static const int NUM_PARTICLES = 128; +static const int NUM_PARTICLES = 128; static const float BLACK_BLEND_THRESHOLD = 0.4f; -static const float CYCLE_TIME = 6.0f; -static const float PARTICLE_PHASE = 2.0f; +static const float CYCLE_TIME = 6.0f; +static const float PARTICLE_PHASE = 2.0f; // Pseudo-random in [-1, 1] -float rand_val(float val, float seed) { +float +rand_val(float val, float seed) { return cos(val * sin(val * seed) * seed); } -float distance2(float2 a, float2 b) { +float +distance2(float2 a, float2 b) { float2 d = a - b; return dot(d, d); } // Diagonal red-and-white barber-pole pattern — branchless -float3 barberpole(float2 pos, float2 rocketpos) { +float3 +barberpole(float2 pos, float2 rocketpos) { float d = fmod(((pos.x - rocketpos.x) + (pos.y - rocketpos.y)) * 20.0f, 2.0f); return lerp(float3(1.0f), float3(1.0f, 0.0f, 0.0f), step(1.0f, d)); } // Draw a single rocket at rocketpos in the supplied coordinate space — branchless -float3 rocket(float2 pos, float2 rocketpos) { - float3 col = float3(0.0f); - float absx = abs(rocketpos.x - pos.x); - float absy = abs(rocketpos.y - pos.y); +float3 +rocket(float2 pos, float2 rocketpos) { + float3 col = float3(0.0f); + float absx = abs(rocketpos.x - pos.x); + float absy = abs(rocketpos.y - pos.y); // Wooden stick float stick = step(absx, 0.01f) * step(absy, 0.22f); @@ -43,9 +47,9 @@ float3 rocket(float2 pos, float2 rocketpos) { col = lerp(col, barberpole(pos, rocketpos), pole); // Pointed nose cone - float pointw = (rocketpos.y - pos.y - 0.25f) * -0.7f; + float pointw = (rocketpos.y - pos.y - 0.25f) * -0.7f; float has_point = step(0.1f, rocketpos.y - pos.y); - float f_point = smoothstep(pointw - 0.001f, pointw + 0.001f, absx); + float f_point = smoothstep(pointw - 0.001f, pointw + 0.001f, absx); col = lerp(col, lerp(float3(1.0f, 0.0f, 0.0f), col, f_point), has_point); // Ambient shadow gradient @@ -59,36 +63,35 @@ float3 rocket(float2 pos, float2 rocketpos) { // Particle direction for iteration i is the unit vector at angle i radians, // precomputed as (cos(i), sin(i)) — equivalent to iteratively applying the // original 1-radian rotation matrix but eliminates inter-iteration data dependency. -float3 drawParticles(float2 pos, float3 particolor, float time, - float2 cpos, float gravity, float seed, float timelength) { +float3 +drawParticles(float2 pos, float3 particolor, float time, float2 cpos, float gravity, float seed, float timelength) { float3 col = float3(0.0f); [ForceUnroll] for (int i = 0; i < NUM_PARTICLES; i++) { - float fi = float(i + 1); // 1-indexed to match original - float d = rand_val(fi, seed); - float fade = (fi / float(NUM_PARTICLES)) * time; - float2 pp = float2(cos(float(i)), sin(float(i))); // unit direction at angle i + float fi = float(i + 1); // 1-indexed to match original + float d = rand_val(fi, seed); + float fade = (fi / float(NUM_PARTICLES)) * time; + float2 pp = float2(cos(float(i)), sin(float(i))); // unit direction at angle i float2 particpos = cpos + time * pp * d; - col = lerp(particolor / fade, col, - smoothstep(0.0f, 0.0001f, distance2(particpos, pos))); + col = lerp(particolor / fade, col, smoothstep(0.0f, 0.0001f, distance2(particpos, pos))); } col *= smoothstep(0.0f, 1.0f, (timelength - time) / timelength); return col; } // Returns rocket or particles depending on which phase of the cycle we are in -float3 drawFireworks(float time, float2 uv, float3 particolor, float seed) { +float3 +drawFireworks(float time, float2 uv, float3 particolor, float seed) { float3 col = float3(0.0f); if (time <= 0.0f) return col; - float tmod = fmod(time, CYCLE_TIME); + float tmod = fmod(time, CYCLE_TIME); float cycle_id = ceil(time / CYCLE_TIME); if (tmod > PARTICLE_PHASE) { float2 cpos = float2(rand_val(cycle_id, seed), -0.5f); - col = drawParticles(uv, particolor, tmod - PARTICLE_PHASE, - cpos, 0.5f, cycle_id, seed); + col = drawParticles(uv, particolor, tmod - PARTICLE_PHASE, cpos, 0.5f, cycle_id, seed); } else { float rx = 3.0f * rand_val(cycle_id, seed); float ry = 3.0f * (-0.5f + (PARTICLE_PHASE - tmod)); @@ -97,30 +100,27 @@ float3 drawFireworks(float time, float2 uv, float3 particolor, float seed) { return col; } -public float4 fragment_main( - float4 color, - KittyTextures t, - KittyCustomShaderData d -) { +public float4 +fragment_main(float4 color, KittyTextures t, KittyCustomShaderData d) { float2 pixelDims = float2(d.viewport_size_pixels) * t.viewport.zw; - float aspect = pixelDims.x / pixelDims.y; + float aspect = pixelDims.x / pixelDims.y; // Map to [-aspect, aspect] × [-1, 1] with y=1 at top (matching the original) float2 uv = 1.0f - 2.0f * t.pos; - uv.x *= aspect; - uv.y = -uv.y; + uv.x *= aspect; + uv.y = -uv.y; float3 col = float3(0.1f, 0.1f, 0.2f); - col += 0.1f * uv.y; // subtle sky gradient + col += 0.1f * uv.y; // subtle sky gradient float time = d.timestamp; - col += drawFireworks(time, uv, float3(1.0f, 0.1f, 0.1f), 1.0f); + col += drawFireworks(time, uv, float3(1.0f, 0.1f, 0.1f), 1.0f); col += drawFireworks(time - 2.0f, uv, float3(0.0f, 1.0f, 0.5f), 2.0f); col += drawFireworks(time - 4.0f, uv, float3(1.0f, 1.0f, 0.1f), 3.0f); float4 terminalColor = t.backbuffer.Sample(t.pos); - float alpha = step(length(terminalColor.rgb), BLACK_BLEND_THRESHOLD); - float3 blended = lerp(terminalColor.rgb, col * 0.3f, alpha); + float alpha = step(length(terminalColor.rgb), BLACK_BLEND_THRESHOLD); + float3 blended = lerp(terminalColor.rgb, col * 0.3f, alpha); return float4(blended, terminalColor.a); } diff --git a/kitty/shaders/custom/fireworks.slang b/kitty/shaders/custom/fireworks.slang index d750b99d7..1b47d7dc0 100644 --- a/kitty/shaders/custom/fireworks.slang +++ b/kitty/shaders/custom/fireworks.slang @@ -8,87 +8,82 @@ import kitty_custom_shader_types; -static const int NUM_EXPLOSIONS = 3; -static const int NUM_PARTICLES = 42; +static const int NUM_EXPLOSIONS = 3; +static const int NUM_PARTICLES = 42; static const float BLACK_BLEND_THRESHOLD = 0.4f; -static const float TIME_SCALE = 0.5f; -static const float PI = 3.14159265359f; +static const float TIME_SCALE = 0.5f; +static const float PI = 3.14159265359f; // Noise constant from Dave Hoskins — not user-tunable so kept as plain static const static const float3 MOD3 = float3(0.1031f, 0.11369f, 0.13787f); -float3 hash31(float p) { +float3 +hash31(float p) { float3 p3 = frac(float3(p) * MOD3); p3 += dot(p3, p3.yzx + 19.19f); - return frac(float3((p3.x + p3.y) * p3.z, - (p3.x + p3.z) * p3.y, - (p3.y + p3.z) * p3.x)); + return frac(float3((p3.x + p3.y) * p3.z, (p3.x + p3.z) * p3.y, (p3.y + p3.z) * p3.x)); } -float hash12(float2 p) { +float +hash12(float2 p) { float3 p3 = frac(float3(p.xyx) * MOD3); p3 += dot(p3, p3.yzx + 19.19f); return frac((p3.x + p3.y) * p3.z); } -float light(float2 uv, float2 pos, float size) { - uv -= pos; +float +light(float2 uv, float2 pos, float size) { + uv -= pos; size *= size; return size / dot(uv, uv); } -float3 explosion(float2 uv, float2 p, float seed, float t) { - float3 col = float3(0.0f); - float3 en = hash31(seed); +float3 +explosion(float2 uv, float2 p, float seed, float t) { + float3 col = float3(0.0f); + float3 en = hash31(seed); float3 baseCol = en; // startP, pt, and b are loop-invariant; computed once outside float2 startP = p - float2(0.0f, t * t * 0.1f); - float pt = 1.0f - pow(t - 1.0f, 2.0f); + float pt = 1.0f - pow(t - 1.0f, 2.0f); // B(en.x, en.y, en.z, t) macro expansion - float b = smoothstep(en.x - en.z, en.x + en.z, t) - * smoothstep(en.y + en.z, en.y - en.z, t); - float sizeBase = lerp(0.01f, 0.005f, smoothstep(0.0f, 0.1f, pt)) - * smoothstep(1.0f, 0.1f, pt); + float b = smoothstep(en.x - en.z, en.x + en.z, t) * smoothstep(en.y + en.z, en.y - en.z, t); + float sizeBase = lerp(0.01f, 0.005f, smoothstep(0.0f, 0.1f, pt)) * smoothstep(1.0f, 0.1f, pt); [ForceUnroll] for (int i = 0; i < NUM_PARTICLES; i++) { - float3 n = hash31(float(i)) - 0.5f; + float3 n = hash31(float(i)) - 0.5f; float2 endP = startP + normalize(n.xy) * n.z - float2(0.0f, t * 0.2f); - float2 pos = lerp(p, endP, pt); + float2 pos = lerp(p, endP, pt); float sparkle = sin((pt + n.z) * 21.0f) * 0.5f + 0.5f; - sparkle = pow(sparkle, pow(en.x, 3.0f) * 50.0f) - * lerp(0.01f, 0.01f, en.y * n.y); + sparkle = pow(sparkle, pow(en.x, 3.0f) * 50.0f) * lerp(0.01f, 0.01f, en.y * n.y); col += baseCol * light(uv, pos, sizeBase + sparkle * b); } return col; } -float3 rainbow(float3 c, float time) { +float3 +rainbow(float3 c, float time) { float avg = (c.r + c.g + c.b) / 3.0f; - c = avg + (c - avg) * sin(float3(0.0f, 0.333f, 0.666f) + time); - c += sin(float3(0.4f, 0.3f, 0.3f) * time - + float3(1.1244f, 3.43215f, 6.435f)) - * float3(0.4f, 0.1f, 0.5f); + c = avg + (c - avg) * sin(float3(0.0f, 0.333f, 0.666f) + time); + c += sin(float3(0.4f, 0.3f, 0.3f) * time + float3(1.1244f, 3.43215f, 6.435f)) * float3(0.4f, 0.1f, 0.5f); return c; } -public float4 fragment_main( - float4 color, - KittyTextures t, - KittyCustomShaderData d -) { +public float4 +fragment_main(float4 color, KittyTextures t, KittyCustomShaderData d) { float2 pixelDims = float2(d.viewport_size_pixels) * t.viewport.zw; - float aspect = pixelDims.x / pixelDims.y; - float time = fmod(d.timestamp, 3600.0f) * TIME_SCALE; + float aspect = pixelDims.x / pixelDims.y; + float time = fmod(d.timestamp, 3600.0f) * TIME_SCALE; // Aspect-corrected UV with y=0 at top so gravity points downward float2 uv = t.pos; uv.x -= 0.5f; uv.x *= aspect; - uv.y = -uv.y + 1.0f; + uv.y = -uv.y + 1.0f; float3 c = float3(0.0f); @@ -96,7 +91,7 @@ public float4 fragment_main( for (int i = 0; i < NUM_EXPLOSIONS; i++) { float et = time + float(i) * 1234.45235f; float id = floor(et); - et -= id; + et -= id; float2 p = hash31(id).xy; p.x -= 0.5f; @@ -106,8 +101,8 @@ public float4 fragment_main( c = rainbow(c, d.timestamp); float4 terminalColor = t.backbuffer.Sample(t.pos); - float alpha = step(length(terminalColor.rgb), BLACK_BLEND_THRESHOLD); - float3 blended = lerp(terminalColor.rgb, c * 0.3f, alpha); + float alpha = step(length(terminalColor.rgb), BLACK_BLEND_THRESHOLD); + float3 blended = lerp(terminalColor.rgb, c * 0.3f, alpha); return float4(blended, terminalColor.a); } diff --git a/kitty/shaders/custom/focus-highlight.slang b/kitty/shaders/custom/focus-highlight.slang index 031519f45..73be828f1 100644 --- a/kitty/shaders/custom/focus-highlight.slang +++ b/kitty/shaders/custom/focus-highlight.slang @@ -19,16 +19,13 @@ static const float BORDER_GLOW = 0.55; // Dim factor applied to pixels outside the active window (1.0 = unchanged, 0.0 = black). static const float INACTIVE_DIM = 0.3; -public float4 fragment_main( - float4 color, - KittyTextures t, - KittyCustomShaderData d -) { +public float4 +fragment_main(float4 color, KittyTextures t, KittyCustomShaderData d) { float4 g = d.active_window_geometry; // Pass through unchanged if no active window geometry is known. if (g.z <= 0.0 || g.w <= 0.0) return color; - float2 uv = t.pos; + float2 uv = t.pos; float2 winMin = g.xy; float2 winMax = g.xy + g.zw; @@ -39,8 +36,8 @@ public float4 fragment_main( if (inWindow) { // Minimum distance from this pixel to any edge of the active window. - float2 edgeDists = min(uv - winMin, winMax - uv); - float nearestEdge = min(edgeDists.x, edgeDists.y); + float2 edgeDists = min(uv - winMin, winMax - uv); + float nearestEdge = min(edgeDists.x, edgeDists.y); // Additive border glow: strongest at the edge, falls off over BORDER_WIDTH. float borderGlow = smoothstep(BORDER_WIDTH, 0.0, nearestEdge) * BORDER_GLOW; diff --git a/kitty/shaders/custom/inside-the-matrix.slang b/kitty/shaders/custom/inside-the-matrix.slang index 02ee7b74e..c82fce834 100644 --- a/kitty/shaders/custom/inside-the-matrix.slang +++ b/kitty/shaders/custom/inside-the-matrix.slang @@ -8,65 +8,68 @@ import kitty_custom_shader_types; -static const int ITERATIONS = 40; -static const float SPEED = 0.5f; -static const float STRIP_CHARS_MIN = 7.0f; -static const float STRIP_CHARS_MAX = 40.0f; +static const int ITERATIONS = 40; +static const float SPEED = 0.5f; +static const float STRIP_CHARS_MIN = 7.0f; +static const float STRIP_CHARS_MAX = 40.0f; static const float STRIP_CHAR_HEIGHT = 0.15f; -static const float STRIP_CHAR_WIDTH = 0.10f; -static const float ZCELL_SIZE = STRIP_CHAR_HEIGHT * STRIP_CHARS_MAX; -static const float XYCELL_SIZE = 12.0f * STRIP_CHAR_WIDTH; -static const int BLOCK_SIZE = 10; -static const int BLOCK_GAP = 2; -static const float WALK_SPEED = 0.5f * XYCELL_SIZE; +static const float STRIP_CHAR_WIDTH = 0.10f; +static const float ZCELL_SIZE = STRIP_CHAR_HEIGHT * STRIP_CHARS_MAX; +static const float XYCELL_SIZE = 12.0f * STRIP_CHAR_WIDTH; +static const int BLOCK_SIZE = 10; +static const int BLOCK_GAP = 2; +static const float WALK_SPEED = 0.5f * XYCELL_SIZE; static const float BLOCKS_BEFORE_TURN = 3.0f; -static const float PI = 3.14159265359f; +static const float PI = 3.14159265359f; // ---- random ---- -float hash(float v) { +float +hash(float v) { return frac(sin(v) * 43758.5453123f); } -float hash(float2 v) { +float +hash(float2 v) { return hash(dot(v, float2(5.3983f, 5.4427f))); } -float2 hash2(float2 v) { +float2 +hash2(float2 v) { // GLSL: v * mat2(127.1, 311.7, 269.5, 183.3) [column-major] // Slang float2x2 is row-major; transpose the GLSL column-major layout. - v = mul(v, float2x2(127.1f, 269.5f, - 311.7f, 183.3f)); + v = mul(v, float2x2(127.1f, 269.5f, 311.7f, 183.3f)); return frac(sin(v) * 43758.5453123f); } -float4 hash4(float2 v) { +float4 +hash4(float2 v) { // GLSL: v * mat4x2(127.1,311.7, 269.5,183.3, 113.5,271.9, 246.1,124.6) // mat4x2 = 4 cols × 2 rows; transpose into float2x4 (2 rows × 4 cols). - float4 p = mul(v, float2x4(127.1f, 269.5f, 113.5f, 246.1f, - 311.7f, 183.3f, 271.9f, 124.6f)); + float4 p = mul(v, float2x4(127.1f, 269.5f, 113.5f, 246.1f, 311.7f, 183.3f, 271.9f, 124.6f)); return frac(sin(p) * 43758.5453123f); } -float4 hash4(float3 v) { +float4 +hash4(float3 v) { // GLSL: v * mat4x3(127.1,311.7,74.7, 269.5,183.3,246.1, 113.5,271.9,124.6, 271.9,269.5,311.7) // mat4x3 = 4 cols × 3 rows; transpose into float3x4 (3 rows × 4 cols). - float4 p = mul(v, float3x4(127.1f, 269.5f, 113.5f, 271.9f, - 311.7f, 183.3f, 271.9f, 269.5f, - 74.7f, 246.1f, 124.6f, 311.7f)); + float4 p = mul(v, float3x4(127.1f, 269.5f, 113.5f, 271.9f, 311.7f, 183.3f, 271.9f, 269.5f, 74.7f, 246.1f, 124.6f, 311.7f)); return frac(sin(p) * 43758.5453123f); } // ---- symbols ---- -float rune_line(float2 p, float2 a, float2 b) { +float +rune_line(float2 p, float2 a, float2 b) { p -= a; b -= a; float h = clamp(dot(p, b) / dot(b, b), 0.0f, 1.0f); return length(p - b * h); } -float rune(float2 U, float2 seed, float highlight) { +float +rune(float2 U, float2 seed, float highlight) { float d = 1e5f; [ForceUnroll] for (int i = 0; i < 4; i++) { @@ -75,29 +78,29 @@ float rune(float2 U, float2 seed, float highlight) { // each rune touches the edge of its box on all 4 sides // with [ForceUnroll] these become compile-time constant selects - pos.y = (i == 0) ? 0.0f : pos.y; + pos.y = (i == 0) ? 0.0f : pos.y; pos.x = (i == 1) ? 0.999f : pos.x; - pos.x = (i == 2) ? 0.0f : pos.x; + pos.x = (i == 2) ? 0.0f : pos.x; pos.y = (i == 3) ? 0.999f : pos.y; float4 snaps = float4(2.0f, 3.0f, 2.0f, 3.0f); pos = (floor(pos * snaps) + 0.5f) / snaps; - if (any(pos.xy != pos.zw)) - d = min(d, rune_line(U, pos.xy, pos.zw + 0.001f)); + if (any(pos.xy != pos.zw)) d = min(d, rune_line(U, pos.xy, pos.zw + 0.001f)); } return smoothstep(0.1f, 0.0f, d) + highlight * smoothstep(0.4f, 0.0f, d); } -float random_char(float2 outer, float2 inner, float highlight) { - float2 seed = float2(dot(outer, float2(269.5f, 183.3f)), - dot(outer, float2(113.5f, 271.9f))); +float +random_char(float2 outer, float2 inner, float highlight) { + float2 seed = float2(dot(outer, float2(269.5f, 183.3f)), dot(outer, float2(113.5f, 271.9f))); return rune(inner, seed, highlight); } // ---- digital rain ---- -float3 rain(float3 ro3, float3 rd3, float time) { +float3 +rain(float3 ro3, float3 rd3, float time) { float4 result = float4(0.0f, 0.0f, 0.0f, 0.0f); float2 ro2 = ro3.xy; @@ -106,7 +109,7 @@ float3 rain(float3 ro3, float3 rd3, float time) { bool prefer_dx = abs(rd2.x) > abs(rd2.y); float t3_to_t2 = prefer_dx ? rd3.x / rd2.x : rd3.y / rd2.y; - int3 cell_side = int3(step(0.0f, rd3)); + int3 cell_side = int3(step(0.0f, rd3)); int3 cell_shift = int3(sign(rd3)); float t2 = 0.0f; @@ -114,48 +117,44 @@ float3 rain(float3 ro3, float3 rd3, float time) { [ForceUnroll] for (int iter = 0; iter < ITERATIONS; iter++) { - int2 cell = next_cell; - float t2s = t2; + int2 cell = next_cell; + float t2s = t2; - float2 side = float2(next_cell + cell_side.xy) * XYCELL_SIZE; + float2 side = float2(next_cell + cell_side.xy) * XYCELL_SIZE; float2 t2_side = (side - ro2) / rd2; // branchless cell-side selection - bool pick_x = t2_side.x < t2_side.y; - t2 = pick_x ? t2_side.x : t2_side.y; + bool pick_x = t2_side.x < t2_side.y; + t2 = pick_x ? t2_side.x : t2_side.y; next_cell.x += pick_x ? cell_shift.x : 0; next_cell.y += pick_x ? 0 : cell_shift.y; // skip gap cells float2 cell_in_block = frac(float2(cell) / float(BLOCK_SIZE)); - float gap = float(BLOCK_GAP) / float(BLOCK_SIZE); - if (cell_in_block.x < gap || cell_in_block.y < gap || - (cell_in_block.x < (gap + 0.1f) && cell_in_block.y < (gap + 0.1f))) - continue; + float gap = float(BLOCK_GAP) / float(BLOCK_SIZE); + if (cell_in_block.x < gap || cell_in_block.y < gap || (cell_in_block.x < (gap + 0.1f) && cell_in_block.y < (gap + 0.1f))) continue; - float t3s = t2s / t3_to_t2; - float pos_z = ro3.z + rd3.z * t3s; - float xh = hash(float2(cell)); + float t3s = t2s / t3_to_t2; + float pos_z = ro3.z + rd3.z * t3s; + float xh = hash(float2(cell)); float z_shift = xh * 11.0f - time * (0.5f + xh + xh * xh + pow(xh, 16.0f) * 3.0f); - float czs = floor(z_shift / STRIP_CHAR_HEIGHT); - z_shift = czs * STRIP_CHAR_HEIGHT; - int zcell = int(floor((pos_z - z_shift) / ZCELL_SIZE)); + float czs = floor(z_shift / STRIP_CHAR_HEIGHT); + z_shift = czs * STRIP_CHAR_HEIGHT; + int zcell = int(floor((pos_z - z_shift) / ZCELL_SIZE)); [ForceUnroll] for (int j = 0; j < 2; j++) { - float4 ch = hash4(float3(int3(cell, zcell))); + float4 ch = hash4(float3(int3(cell, zcell))); float4 ch2 = frac(ch * float4(127.1f, 311.7f, 271.9f, 124.6f)); - float chars_count = ch.w * (STRIP_CHARS_MAX - STRIP_CHARS_MIN) + STRIP_CHARS_MIN; + float chars_count = ch.w * (STRIP_CHARS_MAX - STRIP_CHARS_MIN) + STRIP_CHARS_MIN; float target_length = chars_count * STRIP_CHAR_HEIGHT; - float target_rad = STRIP_CHAR_WIDTH * 0.5f; - float target_z = float(zcell) * ZCELL_SIZE + z_shift - + ch.z * (ZCELL_SIZE - target_length); - float2 target = float2(cell) * XYCELL_SIZE + target_rad - + ch.xy * (XYCELL_SIZE - target_rad * 2.0f); + float target_rad = STRIP_CHAR_WIDTH * 0.5f; + float target_z = float(zcell) * ZCELL_SIZE + z_shift + ch.z * (ZCELL_SIZE - target_length); + float2 target = float2(cell) * XYCELL_SIZE + target_rad + ch.xy * (XYCELL_SIZE - target_rad * 2.0f); - float2 sv = target - ro2; - float tmin = dot(sv, rd2); + float2 sv = target - ro2; + float tmin = dot(sv, rd2); if (tmin >= t2s && tmin <= t2) { float u = sv.x * rd2.y - sv.y * rd2.x; if (abs(u) < target_rad) { @@ -163,28 +162,20 @@ float3 rain(float3 ro3, float3 rd3, float time) { float z = ro3.z + rd3.z * tmin / t3_to_t2; float v = (z - target_z) / target_length; if (v >= 0.0f && v < 1.0f) { - float c = floor(v * chars_count); - float q = frac(v * chars_count); + float c = floor(v * chars_count); + float q = frac(v * chars_count); float2 char_hash = hash2(float2(c + czs, ch2.x)); if (char_hash.x >= 0.1f || c == 0.0f) { - float time_factor = floor( - c == 0.0f ? time - : time * (ch2.z + ch2.w * ch2.w * 4.0f - * pow(char_hash.y, 4.0f))); - float a = random_char(float2(char_hash.x, time_factor), - float2(u, q), - max(1.0f, 3.0f - c * 0.5f) * 0.2f); + float time_factor = floor(c == 0.0f ? time : time * (ch2.z + ch2.w * ch2.w * 4.0f * pow(char_hash.y, 4.0f))); + float a = random_char(float2(char_hash.x, time_factor), float2(u, q), max(1.0f, 3.0f - c * 0.5f) * 0.2f); a *= clamp((chars_count - 0.5f - c) * 0.5f, 0.0f, 1.0f); if (a > 0.0f) { float attenuation = 1.0f + pow(0.06f * tmin / t3_to_t2, 2.0f); - float3 col = (c == 0.0f - ? float3(0.67f, 1.0f, 0.82f) - : float3(0.25f, 0.80f, 0.40f)) / attenuation; - float a1 = result.a; - result.a = a1 + (1.0f - a1) * a; + float3 col = (c == 0.0f ? float3(0.67f, 1.0f, 0.82f) : float3(0.25f, 0.80f, 0.40f)) / attenuation; + float a1 = result.a; + result.a = a1 + (1.0f - a1) * a; result.xyz = (result.xyz * a1 + col * (1.0f - a1) * a) / result.a; - if (result.a > 0.98f) - return result.xyz; + if (result.a > 0.98f) return result.xyz; } } } @@ -200,54 +191,52 @@ float3 rain(float3 ro3, float3 rd3, float time) { // For M*v in GLSL with column-major constructor, Slang needs the same values // re-read as row-major (i.e. transposed GLSL column-major == correct Slang row-major). -float2 rotate2D(float2 v, float angle) { +float2 +rotate2D(float2 v, float angle) { float s = sin(angle), c = cos(angle); // GLSL: mat2(c,-s, s,c)*v → rows: (c,s),(-s,c) return mul(float2x2(c, s, -s, c), v); } -float3 rotateX(float3 v, float angle) { +float3 +rotateX(float3 v, float angle) { float s = sin(angle), c = cos(angle); // GLSL: mat3(1,0,0, 0,c,-s, 0,s,c)*v → rows: (1,0,0),(0,c,s),(0,-s,c) - return mul(float3x3(1.0f, 0.0f, 0.0f, - 0.0f, c, s, - 0.0f, -s, c), v); + return mul(float3x3(1.0f, 0.0f, 0.0f, 0.0f, c, s, 0.0f, -s, c), v); } -float3 rotateY(float3 v, float angle) { +float3 +rotateY(float3 v, float angle) { float s = sin(angle), c = cos(angle); // GLSL: mat3(c,0,-s, 0,1,0, s,0,c)*v → rows: (c,0,s),(0,1,0),(-s,0,c) - return mul(float3x3( c, 0.0f, s, - 0.0f, 1.0f, 0.0f, - -s, 0.0f, c), v); + return mul(float3x3(c, 0.0f, s, 0.0f, 1.0f, 0.0f, -s, 0.0f, c), v); } -float3 rotateZ(float3 v, float angle) { +float3 +rotateZ(float3 v, float angle) { float s = sin(angle), c = cos(angle); // GLSL: mat3(c,-s,0, s,c,0, 0,0,1)*v → rows: (c,s,0),(-s,c,0),(0,0,1) - return mul(float3x3( c, s, 0.0f, - -s, c, 0.0f, - 0.0f, 0.0f, 1.0f), v); + return mul(float3x3(c, s, 0.0f, -s, c, 0.0f, 0.0f, 0.0f, 1.0f), v); } -float smoothstep1(float x) { return smoothstep(0.0f, 1.0f, x); } +float +smoothstep1(float x) { + return smoothstep(0.0f, 1.0f, x); +} // ---- entry point ---- -public float4 fragment_main( - float4 color, - KittyTextures t, - KittyCustomShaderData d -) { - float2 uv = t.pos; - float time = fmod(d.timestamp, 300.0f) * SPEED; +public float4 +fragment_main(float4 color, KittyTextures t, KittyCustomShaderData d) { + float2 uv = t.pos; + float time = fmod(d.timestamp, 300.0f) * SPEED; - const float turn_rad = 0.25f / BLOCKS_BEFORE_TURN; + const float turn_rad = 0.25f / BLOCKS_BEFORE_TURN; const float turn_abs_time = (PI * 0.5f * turn_rad) * 1.5f; - const float turn_time = turn_abs_time / (1.0f - 2.0f * turn_rad + turn_abs_time); + const float turn_time = turn_abs_time / (1.0f - 2.0f * turn_rad + turn_abs_time); float level1_size = float(BLOCK_SIZE) * BLOCKS_BEFORE_TURN * XYCELL_SIZE; - float gap_size = float(BLOCK_GAP) * XYCELL_SIZE; + float gap_size = float(BLOCK_GAP) * XYCELL_SIZE; float3 ro = float3(gap_size * 0.5f, gap_size * 0.5f, 0.0f); float3 rd = float3(uv.x, 2.0f, uv.y); @@ -257,65 +246,90 @@ public float4 fragment_main( float t1 = frac(t8 * 8.0f); float2 prev, dir; - if (tq < 0.25f) { prev = float2(0.0f, 0.0f); dir = float2(0.0f, 1.0f); } - else if (tq < 0.50f) { prev = float2(0.0f, 1.0f); dir = float2(1.0f, 0.0f); } - else if (tq < 0.75f) { prev = float2(1.0f, 1.0f); dir = float2(0.0f, -1.0f); } - else { prev = float2(1.0f, 0.0f); dir = float2(-1.0f, 0.0f); } + if (tq < 0.25f) { + prev = float2(0.0f, 0.0f); + dir = float2(0.0f, 1.0f); + } else if (tq < 0.50f) { + prev = float2(0.0f, 1.0f); + dir = float2(1.0f, 0.0f); + } else if (tq < 0.75f) { + prev = float2(1.0f, 1.0f); + dir = float2(0.0f, -1.0f); + } else { + prev = float2(1.0f, 0.0f); + dir = float2(-1.0f, 0.0f); + } float angle = floor(tq * 4.0f); prev *= 4.0f; - const float first_turn_look_angle = 0.4f; + const float first_turn_look_angle = 0.4f; const float second_turn_drift_angle = 0.5f; - const float fifth_turn_drift_angle = 0.25f; + const float fifth_turn_drift_angle = 0.25f; float2 dirL = rotate2D(dir, -PI * 0.5f); float2 dirR = -dirL; float2 turn; - float turn_sign = 0.0f; - float up_down = 0.0f; - float rotate_on_turns = 1.0f; - float roll_on_turns = 1.0f; - float add_angel = 0.0f; + float turn_sign = 0.0f; + float up_down = 0.0f; + float rotate_on_turns = 1.0f; + float roll_on_turns = 1.0f; + float add_angel = 0.0f; if (t8 < 0.125f) { - turn = dirL; turn_sign = -1.0f; - angle -= first_turn_look_angle * ( - max(0.0f, t1 - (1.0f - turn_time * 2.0f)) / turn_time - - max(0.0f, t1 - (1.0f - turn_time)) / turn_time * 2.5f); + turn = dirL; + turn_sign = -1.0f; + angle -= first_turn_look_angle * (max(0.0f, t1 - (1.0f - turn_time * 2.0f)) / turn_time - max(0.0f, t1 - (1.0f - turn_time)) / turn_time * 2.5f); roll_on_turns = 0.0f; } else if (t8 < 0.250f) { - prev += dir; turn = dir; dir = dirL; - angle -= 1.0f; turn_sign = 1.0f; - add_angel += first_turn_look_angle * 0.5f - + (-first_turn_look_angle * 0.5f + 1.0f + second_turn_drift_angle) * t1; - rotate_on_turns = 0.0f; roll_on_turns = 0.0f; + prev += dir; + turn = dir; + dir = dirL; + angle -= 1.0f; + turn_sign = 1.0f; + add_angel += first_turn_look_angle * 0.5f + (-first_turn_look_angle * 0.5f + 1.0f + second_turn_drift_angle) * t1; + rotate_on_turns = 0.0f; + roll_on_turns = 0.0f; } else if (t8 < 0.375f) { - prev += dir + dirL; turn = dirR; turn_sign = 1.0f; + prev += dir + dirL; + turn = dirR; + turn_sign = 1.0f; add_angel += second_turn_drift_angle * sqrt(1.0f - t1); } else if (t8 < 0.500f) { - prev += dir + dir + dirL; turn = dirR; dir = dirR; - angle += 1.0f; turn_sign = 0.0f; + prev += dir + dir + dirL; + turn = dirR; + dir = dirR; + angle += 1.0f; + turn_sign = 0.0f; up_down = sin(t1 * PI) * 0.37f; } else if (t8 < 0.625f) { - prev += dir + dir; turn = dir; dir = dirR; - angle += 1.0f; turn_sign = -1.0f; + prev += dir + dir; + turn = dir; + dir = dirR; + angle += 1.0f; + turn_sign = -1.0f; up_down = sin(-min(1.0f, t1 / (1.0f - turn_time)) * PI) * 0.37f; } else if (t8 < 0.750f) { - prev += dir + dir + dirR; turn = dirL; turn_sign = -1.0f; + prev += dir + dir + dirR; + turn = dirL; + turn_sign = -1.0f; add_angel -= (fifth_turn_drift_angle + 1.0f) * smoothstep1(t1); - rotate_on_turns = 0.0f; roll_on_turns = 0.0f; + rotate_on_turns = 0.0f; + roll_on_turns = 0.0f; } else if (t8 < 0.875f) { - prev += dir + dir + dir + dirR; turn = dir; dir = dirL; - angle -= 1.0f; turn_sign = 1.0f; - add_angel -= fifth_turn_drift_angle - - smoothstep1(t1) * (fifth_turn_drift_angle * 2.0f + 1.0f); - rotate_on_turns = 0.0f; roll_on_turns = 0.0f; + prev += dir + dir + dir + dirR; + turn = dir; + dir = dirL; + angle -= 1.0f; + turn_sign = 1.0f; + add_angel -= fifth_turn_drift_angle - smoothstep1(t1) * (fifth_turn_drift_angle * 2.0f + 1.0f); + rotate_on_turns = 0.0f; + roll_on_turns = 0.0f; } else { - prev += dir + dir + dir; turn = dirR; turn_sign = 1.0f; - angle += fifth_turn_drift_angle * ( - 1.5f * min(1.0f, (1.0f - t1) / turn_time) - - 0.5f * smoothstep1(1.0f - min(1.0f, t1 / (1.0f - turn_time)))); + prev += dir + dir + dir; + turn = dirR; + turn_sign = 1.0f; + angle += fifth_turn_drift_angle * (1.5f * min(1.0f, (1.0f - t1) / turn_time) - 0.5f * smoothstep1(1.0f - min(1.0f, t1 / (1.0f - turn_time)))); } if (d.mouse_button_pressed.x != 0) { @@ -334,9 +348,9 @@ public float4 fragment_main( if (turn_sign == 0.0f) { p = prev + dir * (turn_rad + t1); } else if (t1 > (1.0f - turn_time)) { - float tr = (t1 - (1.0f - turn_time)) / turn_time; + float tr = (t1 - (1.0f - turn_time)) / turn_time; float2 ctr = prev + dir * (1.0f - turn_rad) + turn * turn_rad; - p = ctr + turn_rad * rotate2D(dir, (tr - 1.0f) * turn_sign * PI * 0.5f); + p = ctr + turn_rad * rotate2D(dir, (tr - 1.0f) * turn_sign * PI * 0.5f); angle += tr * turn_sign * rotate_on_turns; rd = rotateY(rd, sin(tr * turn_sign * PI) * 0.2f * roll_on_turns); } else { @@ -352,8 +366,8 @@ public float4 fragment_main( float3 col = rain(ro, rd, time) * 0.25f; float4 terminalColor = t.backbuffer.Sample(t.pos); - float mask = 1.2f - step(0.5f, dot(terminalColor.rgb, float3(1.0f, 1.0f, 1.0f))); - float3 blended = lerp(terminalColor.rgb * 1.2f, col, mask); + float mask = 1.2f - step(0.5f, dot(terminalColor.rgb, float3(1.0f, 1.0f, 1.0f))); + float3 blended = lerp(terminalColor.rgb * 1.2f, col, mask); return float4(blended, terminalColor.a); } diff --git a/kitty/shaders/custom/negative.slang b/kitty/shaders/custom/negative.slang index 4f0224a5c..b29fb99ac 100644 --- a/kitty/shaders/custom/negative.slang +++ b/kitty/shaders/custom/negative.slang @@ -6,10 +6,7 @@ import kitty_custom_shader_types; -public float4 fragment_main( - float4 color, - KittyTextures t, - KittyCustomShaderData d -) { +public float4 +fragment_main(float4 color, KittyTextures t, KittyCustomShaderData d) { return float4(1.0 - color.r, 1.0 - color.g, 1.0 - color.b, color.a); } diff --git a/kitty/shaders/custom/northern-lights.slang b/kitty/shaders/custom/northern-lights.slang index 90c0581fc..6487a81ad 100644 --- a/kitty/shaders/custom/northern-lights.slang +++ b/kitty/shaders/custom/northern-lights.slang @@ -39,12 +39,11 @@ static const float VERTICAL_FADE = 25.0f; static const float CAMERA_DISTANCE = 9.7f; // === PERSISTENCE & DECAY CONTROLS === -static const float ACCUMULATION_DECAY = 0.03f; // Decay per frame: 0.0 = infinite, 0.005 = slow fade +static const float ACCUMULATION_DECAY = 0.03f; // Decay per frame: 0.0 = infinite, 0.005 = slow fade static const float MOUSE_PERSIST_INTENSITY = 1.1f; // Brightness multiplier for persistent rays // === STAR FIELD CONTROLS === -static const float STAR_FIELD_ROTATIONS_SPEED = - (2 * PI) / (86400 * TIME_SCALE); +static const float STAR_FIELD_ROTATIONS_SPEED = (2 * PI) / (86400 * TIME_SCALE); static const float STAR_BRIGHTNESS = 0.7f; static const float STAR_SIZE = 1.6f; static const float STAR_TWINKLE_SPEED = 0.05f; @@ -77,9 +76,9 @@ static const float DIP_LEVEL = 0.30f; // === MOUSE TRAIL & RADIAL RAY SHAPE CONTROLS === static const float MOUSE_AURORA_INTENSITY = 0.15f; static const float MOUSE_TRAIL_WIDTH = 0.06f; -static const float MOUSE_RAY_WIDTH = 0.015f; // Tangential ray width -static const float MOUSE_RAY_LENGTH = 0.11f; // Radial ray length along center alignment -static const float MOUSE_RAY_FREQUENCY = 160.0f; // Fine curtain thread streaking frequency +static const float MOUSE_RAY_WIDTH = 0.015f; // Tangential ray width +static const float MOUSE_RAY_LENGTH = 0.11f; // Radial ray length along center alignment +static const float MOUSE_RAY_FREQUENCY = 160.0f; // Fine curtain thread streaking frequency static const float MOUSE_TRAIL_NOISE_SCALE = 3.0f; static const float MOUSE_PRESS_BOOST = 0.3f; static const float MOUSE_IGNORE_WHEN_HIDDEN = 0.0f; @@ -88,8 +87,7 @@ static const float DEBUG_MOUSE_OVERLAY = 0.0f; static const float3 SKY_COLOR_TOP = float3(0.05f, 0.1f, 0.2f); static const float3 SKY_COLOR_BOTTOM = float3(0.1f, 0.05f, 0.2f); -static const float2x2 TRI_ROT_MATRIX = - float2x2(0.95534f, 0.29552f, -0.29552f, 0.95534f); +static const float2x2 TRI_ROT_MATRIX = float2x2(0.95534f, 0.29552f, -0.29552f, 0.95534f); static const float LATITUDE_DEGREES = 61.2f; static const float LAT_RAD = LATITUDE_DEGREES * (3.14159265359f / 180.0f); @@ -97,471 +95,479 @@ static const float3 CELESTIAL_POLE = float3(0.0f, sin(LAT_RAD), cos(LAT_RAD)); static const float PERSPECTIVE_TILT = 0.12f; static const int STAR_LAYERS = 4; -static const float STAR_LAYER_DENSITY[STAR_LAYERS] = {0.0005f, 0.0015f, 0.0045f, 0.0125f}; +static const float STAR_LAYER_DENSITY[STAR_LAYERS] = { 0.0005f, 0.0015f, 0.0045f, 0.0125f }; static const float TWINKLE_FREQ_A = 1.618034f; static const float TWINKLE_FREQ_B = 2.414214f; -static const int STAGE = 0; // internal use only +static const int STAGE = 0; // internal use only // === TRIANGLE WAVE NOISE === -float triWave(float x) { return clamp(abs(frac(x) - 0.5f), 0.01f, 0.49f); } - -float2 triWave2(float2 p) { - return float2(triWave(p.x) + triWave(p.y), triWave(p.y + triWave(p.x))); +float +triWave(float x) { + return clamp(abs(frac(x) - 0.5f), 0.01f, 0.49f); } -float triNoise2d(float2 p, float spd, float timestamp, float sharpness) { - float z = 1.8f; - float z2 = 2.5f; - float rz = 0.0f; +float2 +triWave2(float2 p) { + return float2(triWave(p.x) + triWave(p.y), triWave(p.y + triWave(p.x))); +} - float c = cos(p.x * 0.06f); - float s = sin(p.x * 0.06f); - p = mul(float2x2(c, s, -s, c), p); +float +triNoise2d(float2 p, float spd, float timestamp, float sharpness) { + float z = 1.8f; + float z2 = 2.5f; + float rz = 0.0f; - float2 bp = p; + float c = cos(p.x * 0.06f); + float s = sin(p.x * 0.06f); + p = mul(float2x2(c, s, -s, c), p); - [ForceUnroll] for (int i = 0; i < 5; i++) { - float2 dg = triWave2(bp * 1.85f) * 0.75f; - float tc = cos(timestamp * spd); - float ts = sin(timestamp * spd); - dg = mul(float2x2(tc, ts, -ts, tc), dg); - p -= dg / z2; + float2 bp = p; - bp *= 1.3f; - z2 *= 0.45f; - z *= 0.42f; - p *= 1.21f + (rz - 1.0f) * 0.02f; + [ForceUnroll] + for (int i = 0; i < 5; i++) { + float2 dg = triWave2(bp * 1.85f) * 0.75f; + float tc = cos(timestamp * spd); + float ts = sin(timestamp * spd); + dg = mul(float2x2(tc, ts, -ts, tc), dg); + p -= dg / z2; - rz += triWave(p.x + triWave(p.y)) * z; - p = mul(TRI_ROT_MATRIX, p) * -1.0f; - } + bp *= 1.3f; + z2 *= 0.45f; + z *= 0.42f; + p *= 1.21f + (rz - 1.0f) * 0.02f; - return clamp(1.0f / pow(rz * sharpness, CURTAIN_SOFTNESS), 0.0f, 0.55f); + rz += triWave(p.x + triWave(p.y)) * z; + p = mul(TRI_ROT_MATRIX, p) * -1.0f; + } + + return clamp(1.0f / pow(rz * sharpness, CURTAIN_SOFTNESS), 0.0f, 0.55f); } // === HASH FUNCTIONS === -float hash21(float2 n) { - return frac(sin(dot(n, float2(12.9898f, 4.1414f))) * 43758.5453f); +float +hash21(float2 n) { + return frac(sin(dot(n, float2(12.9898f, 4.1414f))) * 43758.5453f); } -uint pcgHash(uint v) { - v = v * 747796405u + 2891336453u; - uint w = ((v >> ((v >> 28u) + 4u)) ^ v) * 277803737u; - return (w >> 22u) ^ w; +uint +pcgHash(uint v) { + v = v * 747796405u + 2891336453u; + uint w = ((v >> ((v >> 28u) + 4u)) ^ v) * 277803737u; + return (w >> 22u) ^ w; } -float epochRand(uint epoch, uint channel) { - uint seed = epoch * 0x9E3779B9u ^ (channel * 0x85EBCA6Bu + 0x27D4EB2Fu); - return float(pcgHash(seed)) * (1.0f / 4294967295.0f); +float +epochRand(uint epoch, uint channel) { + uint seed = epoch * 0x9E3779B9u ^ (channel * 0x85EBCA6Bu + 0x27D4EB2Fu); + return float(pcgHash(seed)) * (1.0f / 4294967295.0f); } -float3 pcgHash33(float3 q) { - const uint STREAM_X = 0x9E3779B9u; - const uint STREAM_Y = 0x85EBCA6Bu; - const uint STREAM_Z = 0xC2B2AE35u; +float3 +pcgHash33(float3 q) { + const uint STREAM_X = 0x9E3779B9u; + const uint STREAM_Y = 0x85EBCA6Bu; + const uint STREAM_Z = 0xC2B2AE35u; - uint3 p = uint3(int3(floor(q))); + uint3 p = uint3(int3(floor(q))); - const uint MULT = 0x5851F42Du; + const uint MULT = 0x5851F42Du; - uint h = p.x * MULT + p.y * 0x27D4EB2Fu + p.z * 0x165667B1u + STREAM_X; - h = ((h >> ((h >> 28u) + 4u)) ^ h) * 277803737u; - uint outX = (h >> 22u) ^ h; + uint h = p.x * MULT + p.y * 0x27D4EB2Fu + p.z * 0x165667B1u + STREAM_X; + h = ((h >> ((h >> 28u) + 4u)) ^ h) * 277803737u; + uint outX = (h >> 22u) ^ h; - h = p.y * MULT + p.z * 0x27D4EB2Fu + p.x * 0x165667B1u + STREAM_Y; - h = ((h >> ((h >> 28u) + 4u)) ^ h) * 277803737u; - uint outY = (h >> 22u) ^ h; + h = p.y * MULT + p.z * 0x27D4EB2Fu + p.x * 0x165667B1u + STREAM_Y; + h = ((h >> ((h >> 28u) + 4u)) ^ h) * 277803737u; + uint outY = (h >> 22u) ^ h; - h = p.z * MULT + p.x * 0x27D4EB2Fu + p.y * 0x165667B1u + STREAM_Z; - h = ((h >> ((h >> 28u) + 4u)) ^ h) * 277803737u; - uint outZ = (h >> 22u) ^ h; + h = p.z * MULT + p.x * 0x27D4EB2Fu + p.y * 0x165667B1u + STREAM_Z; + h = ((h >> ((h >> 28u) + 4u)) ^ h) * 277803737u; + uint outZ = (h >> 22u) ^ h; - return float3(outX, outY, outZ) * (1.0f / 4294967295.0f); + return float3(outX, outY, outZ) * (1.0f / 4294967295.0f); } // === PER-EPOCH RANDOM PARAMETERS === struct AuroraRandParams { - float timeOffset; - float2 startOffset; - float waveSpeedMul; - float scaleMul; - float sharpMul; - float colorJitter; - float intensityMul; + float timeOffset; + float2 startOffset; + float waveSpeedMul; + float scaleMul; + float sharpMul; + float colorJitter; + float intensityMul; }; -AuroraRandParams identityParams() { - AuroraRandParams rp; - rp.timeOffset = 0.0f; - rp.startOffset = float2(0.0f, 0.0f); - rp.waveSpeedMul = 1.0f; - rp.scaleMul = 1.0f; - rp.sharpMul = 1.0f; - rp.colorJitter = 0.0f; - rp.intensityMul = 1.0f; - return rp; +AuroraRandParams +identityParams() { + AuroraRandParams rp; + rp.timeOffset = 0.0f; + rp.startOffset = float2(0.0f, 0.0f); + rp.waveSpeedMul = 1.0f; + rp.scaleMul = 1.0f; + rp.sharpMul = 1.0f; + rp.colorJitter = 0.0f; + rp.intensityMul = 1.0f; + return rp; } -AuroraRandParams epochParams(uint epoch) { - AuroraRandParams rp; - rp.timeOffset = epochRand(epoch, 1u) * 4096.0f; - rp.startOffset = float2(0.0f, 0.0f); - rp.waveSpeedMul = lerp(0.75f, 1.25f, epochRand(epoch, 6u)); - rp.scaleMul = lerp(0.85f, 1.15f, epochRand(epoch, 7u)); - rp.sharpMul = lerp(0.80f, 1.20f, epochRand(epoch, 8u)); - rp.colorJitter = lerp(-0.6f, 0.6f, epochRand(epoch, 9u)); - rp.intensityMul = lerp(0.85f, 1.15f, epochRand(epoch, 10u)); - return rp; +AuroraRandParams +epochParams(uint epoch) { + AuroraRandParams rp; + rp.timeOffset = epochRand(epoch, 1u) * 4096.0f; + rp.startOffset = float2(0.0f, 0.0f); + rp.waveSpeedMul = lerp(0.75f, 1.25f, epochRand(epoch, 6u)); + rp.scaleMul = lerp(0.85f, 1.15f, epochRand(epoch, 7u)); + rp.sharpMul = lerp(0.80f, 1.20f, epochRand(epoch, 8u)); + rp.colorJitter = lerp(-0.6f, 0.6f, epochRand(epoch, 9u)); + rp.intensityMul = lerp(0.85f, 1.15f, epochRand(epoch, 10u)); + return rp; } -AuroraRandParams lerpParams(AuroraRandParams a, AuroraRandParams b, float t) { - AuroraRandParams rp; - rp.timeOffset = lerp(a.timeOffset, b.timeOffset, t); - rp.startOffset = lerp(a.startOffset, b.startOffset, t); - rp.waveSpeedMul = lerp(a.waveSpeedMul, b.waveSpeedMul, t); - rp.scaleMul = lerp(a.scaleMul, b.scaleMul, t); - rp.sharpMul = lerp(a.sharpMul, b.sharpMul, t); - rp.colorJitter = lerp(a.colorJitter, b.colorJitter, t); - rp.intensityMul = lerp(a.intensityMul, b.intensityMul, t); - return rp; +AuroraRandParams +lerpParams(AuroraRandParams a, AuroraRandParams b, float t) { + AuroraRandParams rp; + rp.timeOffset = lerp(a.timeOffset, b.timeOffset, t); + rp.startOffset = lerp(a.startOffset, b.startOffset, t); + rp.waveSpeedMul = lerp(a.waveSpeedMul, b.waveSpeedMul, t); + rp.scaleMul = lerp(a.scaleMul, b.scaleMul, t); + rp.sharpMul = lerp(a.sharpMul, b.sharpMul, t); + rp.colorJitter = lerp(a.colorJitter, b.colorJitter, t); + rp.intensityMul = lerp(a.intensityMul, b.intensityMul, t); + return rp; } -AuroraRandParams getAuroraParams(float rawTimestamp, float randomFloat) { - if (RANDOMIZE <= 0.001f) - return identityParams(); - uint launchOffset = uint(randomFloat * 100000.0f); - uint epoch = uint(floor(rawTimestamp / RESEED_INTERVAL)) + launchOffset; - return lerpParams(identityParams(), epochParams(epoch), RANDOMIZE); +AuroraRandParams +getAuroraParams(float rawTimestamp, float randomFloat) { + if (RANDOMIZE <= 0.001f) return identityParams(); + uint launchOffset = uint(randomFloat * 100000.0f); + uint epoch = uint(floor(rawTimestamp / RESEED_INTERVAL)) + launchOffset; + return lerpParams(identityParams(), epochParams(epoch), RANDOMIZE); } -float transitionBrightness(float rawTimestamp) { - if (RANDOMIZE <= 0.001f) - return 1.0f; - float ph = frac(rawTimestamp / RESEED_INTERVAL); - float dEpoch = min(ph, 1.0f - ph) * RESEED_INTERVAL; - float halfDip = TRANSITION_DIP * 0.5f; - if (dEpoch >= halfDip) - return 1.0f; - float u = dEpoch / max(halfDip, 1e-4f); - return lerp(DIP_LEVEL, 1.0f, smoothstep(0.0f, 1.0f, u)); +float +transitionBrightness(float rawTimestamp) { + if (RANDOMIZE <= 0.001f) return 1.0f; + float ph = frac(rawTimestamp / RESEED_INTERVAL); + float dEpoch = min(ph, 1.0f - ph) * RESEED_INTERVAL; + float halfDip = TRANSITION_DIP * 0.5f; + if (dEpoch >= halfDip) return 1.0f; + float u = dEpoch / max(halfDip, 1e-4f); + return lerp(DIP_LEVEL, 1.0f, smoothstep(0.0f, 1.0f, u)); } // === PROCEDURAL STARS === -float magnitudeToBrightness(float hashVal) { - float mag = hashVal * STAR_BRIGHTNESS_VARIATION; - return pow(10.0f, -0.4f * mag); +float +magnitudeToBrightness(float hashVal) { + float mag = hashVal * STAR_BRIGHTNESS_VARIATION; + return pow(10.0f, -0.4f * mag); } -float evalTwinkleWave(float3 pos, float seed, float horizonFactor) { - float variance = 1.0f - STAR_TWINKLE_CORRELATION; +float +evalTwinkleWave(float3 pos, float seed, float horizonFactor) { + float variance = 1.0f - STAR_TWINKLE_CORRELATION; - float seedPhaseA = seed * 6.2831853f * variance; - float seedPhaseB = seed * 3.14159265f * variance; - float seedPhaseC = seed * 4.71238898f * variance; - float freqMod = 1.0f + (frac(seed * 17.13f) - 0.5f) * 0.6f * variance; + float seedPhaseA = seed * 6.2831853f * variance; + float seedPhaseB = seed * 3.14159265f * variance; + float seedPhaseC = seed * 4.71238898f * variance; + float freqMod = 1.0f + (frac(seed * 17.13f) - 0.5f) * 0.6f * variance; - float spd = STAR_TWINKLE_SPEED * freqMod * horizonFactor; + float spd = STAR_TWINKLE_SPEED * freqMod * horizonFactor; - float waveA = sin(dot(pos, TWINKLE_WAVE_DIR_A) * spd + seedPhaseA); - float waveB = cos(dot(pos, TWINKLE_WAVE_DIR_B) * spd * STAR_TWINKLE_WAVE_B_MULT + - seedPhaseB + waveA * 0.7f); - float waveC = sin(dot(pos, TWINKLE_WAVE_DIR_C) * spd * STAR_TWINKLE_WAVE_C_MULT + - seedPhaseC); - float microFlash = pow(clamp(waveC * 0.5f + 0.5f, 0.0f, 1.0f), 3.0f); + float waveA = sin(dot(pos, TWINKLE_WAVE_DIR_A) * spd + seedPhaseA); + float waveB = cos(dot(pos, TWINKLE_WAVE_DIR_B) * spd * STAR_TWINKLE_WAVE_B_MULT + seedPhaseB + waveA * 0.7f); + float waveC = sin(dot(pos, TWINKLE_WAVE_DIR_C) * spd * STAR_TWINKLE_WAVE_C_MULT + seedPhaseC); + float microFlash = pow(clamp(waveC * 0.5f + 0.5f, 0.0f, 1.0f), 3.0f); - float combined = waveA * 0.45f + waveB * 0.35f + (microFlash - 0.2f) * 0.4f; + float combined = waveA * 0.45f + waveB * 0.35f + (microFlash - 0.2f) * 0.4f; - return max(0.0f, STAR_TWINKLE_BASE + - STAR_TWINKLE_AMPLITUDE * combined * horizonFactor); + return max(0.0f, STAR_TWINKLE_BASE + STAR_TWINKLE_AMPLITUDE * combined * horizonFactor); } -float3 continuousChromaticTwinkle(float3 rd, float time, float seed) { - float3 turbulencePos = rd * STAR_TURBULENCE_SCALE; - float3 windDrift = time * STAR_WIND_DRIFT_SPEED; - float3 p = turbulencePos + windDrift; +float3 +continuousChromaticTwinkle(float3 rd, float time, float seed) { + float3 turbulencePos = rd * STAR_TURBULENCE_SCALE; + float3 windDrift = time * STAR_WIND_DRIFT_SPEED; + float3 p = turbulencePos + windDrift; - float horizonFactor = 1.0f + 0.8f * saturate(STAR_DISPERSION_HORIZON_FACTOR - rd.y); - float dispersionScale = STAR_DISPERSION_SCALE * saturate(STAR_DISPERSION_HORIZON_FACTOR - rd.y); + float horizonFactor = 1.0f + 0.8f * saturate(STAR_DISPERSION_HORIZON_FACTOR - rd.y); + float dispersionScale = STAR_DISPERSION_SCALE * saturate(STAR_DISPERSION_HORIZON_FACTOR - rd.y); - float3 pR = p - STAR_DISPERSION_DIR * dispersionScale; - float3 pG = p; - float3 pB = p + STAR_DISPERSION_DIR * dispersionScale; + float3 pR = p - STAR_DISPERSION_DIR * dispersionScale; + float3 pG = p; + float3 pB = p + STAR_DISPERSION_DIR * dispersionScale; - float twR = evalTwinkleWave(pR, seed, horizonFactor); - float twG = evalTwinkleWave(pG, seed, horizonFactor); - float twB = evalTwinkleWave(pB, seed, horizonFactor); + float twR = evalTwinkleWave(pR, seed, horizonFactor); + float twG = evalTwinkleWave(pG, seed, horizonFactor); + float twB = evalTwinkleWave(pB, seed, horizonFactor); - return float3(twR, twG, twB); + return float3(twR, twG, twB); } -float3 sampleStars(float3 rd, float2 res, float time) { - if (STAR_BRIGHTNESS <= 0.0f) - return float3(0.0f, 0.0f, 0.0f); +float3 +sampleStars(float3 rd, float2 res, float time) { + if (STAR_BRIGHTNESS <= 0.0f) return float3(0.0f, 0.0f, 0.0f); - float rotSpeed = STAR_FIELD_ROTATIONS_SPEED; - float angle = time * rotSpeed; - float ca = cos(angle); - float sa = sin(angle); + float rotSpeed = STAR_FIELD_ROTATIONS_SPEED; + float angle = time * rotSpeed; + float ca = cos(angle); + float sa = sin(angle); - float3 rotRd = rd * ca + cross(CELESTIAL_POLE, rd) * sa + - CELESTIAL_POLE * dot(CELESTIAL_POLE, rd) * (1.0f - ca); + float3 rotRd = rd * ca + cross(CELESTIAL_POLE, rd) * sa + CELESTIAL_POLE * dot(CELESTIAL_POLE, rd) * (1.0f - ca); - float3 c = float3(0.0f, 0.0f, 0.0f); + float3 c = float3(0.0f, 0.0f, 0.0f); - [ForceUnroll] for (int i = 0; i < STAR_LAYERS; i++) { - float scale = (0.15f + float(i) * 0.02f) * res.x; + [ForceUnroll] + for (int i = 0; i < STAR_LAYERS; i++) { + float scale = (0.15f + float(i) * 0.02f) * res.x; - float3 layerOffset = float3(float(i) * 0.371f, float(i) * 0.519f, float(i) * 0.163f); - float3 scaledRd = rotRd * scale + layerOffset; + float3 layerOffset = float3(float(i) * 0.371f, float(i) * 0.519f, float(i) * 0.163f); + float3 scaledRd = rotRd * scale + layerOffset; - float3 q = frac(scaledRd) - 0.5f; - float3 id = floor(scaledRd); - float3 rn = pcgHash33(id); + float3 q = frac(scaledRd) - 0.5f; + float3 id = floor(scaledRd); + float3 rn = pcgHash33(id); - float threshold = STAR_LAYER_DENSITY[i] * STAR_DENSITY; - if (rn.x >= threshold) - continue; + float threshold = STAR_LAYER_DENSITY[i] * STAR_DENSITY; + if (rn.x >= threshold) continue; - float c2 = 1.0f - smoothstep(0.0f, STAR_SIZE, length(q)); - float3 twinkleRGB = continuousChromaticTwinkle(rotRd, time, rn.y); - float starFlux = magnitudeToBrightness(rn.y); - float3 starCol = lerp(float3(1.0f, 0.49f, 0.1f), float3(0.75f, 0.9f, 1.0f), rn.z); + float c2 = 1.0f - smoothstep(0.0f, STAR_SIZE, length(q)); + float3 twinkleRGB = continuousChromaticTwinkle(rotRd, time, rn.y); + float starFlux = magnitudeToBrightness(rn.y); + float3 starCol = lerp(float3(1.0f, 0.49f, 0.1f), float3(0.75f, 0.9f, 1.0f), rn.z); - c += c2 * starCol * starFlux * twinkleRGB; - } - return c * c * STAR_BRIGHTNESS; + c += c2 * starCol * starFlux * twinkleRGB; + } + return c * c * STAR_BRIGHTNESS; } // === MOUSE TRAIL FIELD === -float mouseTrailField(float3 worldPos, float2 mouseWorld, float mouseValid, - float strength) { - if (strength <= 0.0f || mouseValid <= 0.5f) - return 0.0f; +float +mouseTrailField(float3 worldPos, float2 mouseWorld, float mouseValid, float strength) { + if (strength <= 0.0f || mouseValid <= 0.5f) return 0.0f; - float dist = length(worldPos.xz - mouseWorld); + float dist = length(worldPos.xz - mouseWorld); - float falloff = saturate(1.0f - dist / MOUSE_TRAIL_WIDTH); - falloff = falloff * falloff * (3.0f - 2.0f * falloff); + float falloff = saturate(1.0f - dist / MOUSE_TRAIL_WIDTH); + falloff = falloff * falloff * (3.0f - 2.0f * falloff); - return falloff * strength; + return falloff * strength; } // === ZENITH PROJECTION HELPER === -float3 zenithRayDir(float2 centeredUV, float aspect, float horizonRadius, - float spinCos, float spinSin) { - float2 scaledUV = centeredUV * float2(aspect, 1.0f) * horizonRadius; - float r2 = dot(scaledUV, scaledUV); - float rd_y = 1.0f / sqrt(1.0f + r2); - float3 rd = normalize(float3(scaledUV.x, rd_y, scaledUV.y)); +float3 +zenithRayDir(float2 centeredUV, float aspect, float horizonRadius, float spinCos, float spinSin) { + float2 scaledUV = centeredUV * float2(aspect, 1.0f) * horizonRadius; + float r2 = dot(scaledUV, scaledUV); + float rd_y = 1.0f / sqrt(1.0f + r2); + float3 rd = normalize(float3(scaledUV.x, rd_y, scaledUV.y)); - float ct = cos(PERSPECTIVE_TILT); - float st = sin(PERSPECTIVE_TILT); - rd.yz = float2(rd.y * ct - rd.z * st, rd.y * st + rd.z * ct); + float ct = cos(PERSPECTIVE_TILT); + float st = sin(PERSPECTIVE_TILT); + rd.yz = float2(rd.y * ct - rd.z * st, rd.y * st + rd.z * ct); - rd.xz = mul(float2x2(spinCos, spinSin, -spinSin, spinCos), rd.xz); - return rd; + rd.xz = mul(float2x2(spinCos, spinSin, -spinSin, spinCos), rd.xz); + return rd; } // === VOLUMETRIC AURORA RAY MARCH === -float4 rayMarchAurora(float3 ro, float3 rd, float2 fragCoord, float noiseTime, - AuroraRandParams rp, float2 mouseWorld, float mouseValid, - float genStrength, float persistentTrail) { - float stepCount = MARCH_STEPS; +float4 +rayMarchAurora( + float3 ro, + float3 rd, + float2 fragCoord, + float noiseTime, + AuroraRandParams rp, + float2 mouseWorld, + float mouseValid, + float genStrength, + float persistentTrail) { + float stepCount = MARCH_STEPS; - float effSpeed = WAVE_SPEED * rp.waveSpeedMul; - float effSharpness = CURTAIN_SHARPNESS * rp.sharpMul; - float effScale = AURORA_SCALE * rp.scaleMul; + float effSpeed = WAVE_SPEED * rp.waveSpeedMul; + float effSharpness = CURTAIN_SHARPNESS * rp.sharpMul; + float effScale = AURORA_SCALE * rp.scaleMul; - float3 accColor = float3(0.0f); - float accAlpha = 0.0f; + float3 accColor = float3(0.0f); + float accAlpha = 0.0f; - [ForceUnroll] for (int i = 0; i < 60; i++) { - if (float(i) >= stepCount) - break; + [ForceUnroll] + for (int i = 0; i < 60; i++) { + if (float(i) >= stepCount) break; - float of = 0.006f * hash21(fragCoord) * smoothstep(0.0f, 15.0f, float(i)); - float pt = ((AURORA_ALTITUDE + pow(float(i), 1.4f) * 0.002f) - ro.y) / - (rd.y * 2.0f + 0.4f); - pt -= of; + float of = 0.006f * hash21(fragCoord) * smoothstep(0.0f, 15.0f, float(i)); + float pt = ((AURORA_ALTITUDE + pow(float(i), 1.4f) * 0.002f) - ro.y) / (rd.y * 2.0f + 0.4f); + pt -= of; - if (pt < 0.0f) - continue; + if (pt < 0.0f) continue; - float3 bpos = ro + pt * rd; - float2 p = bpos.zx * effScale + rp.startOffset; - float density = triNoise2d(p, effSpeed, noiseTime, effSharpness); + float3 bpos = ro + pt * rd; + float2 p = bpos.zx * effScale + rp.startOffset; + float density = triNoise2d(p, effSpeed, noiseTime, effSharpness); - float trailField = mouseTrailField(bpos, mouseWorld, mouseValid, genStrength) + persistentTrail; - float trailNoise = triNoise2d(p * MOUSE_TRAIL_NOISE_SCALE + mouseWorld * 0.5f, - effSpeed * 1.5f, noiseTime, effSharpness * 0.8f); - density = saturate(density + trailField * (trailNoise + 0.6f)); + float trailField = mouseTrailField(bpos, mouseWorld, mouseValid, genStrength) + persistentTrail; + float trailNoise = triNoise2d(p * MOUSE_TRAIL_NOISE_SCALE + mouseWorld * 0.5f, effSpeed * 1.5f, noiseTime, effSharpness * 0.8f); + density = saturate(density + trailField * (trailNoise + 0.6f)); - float h = float(i) / MARCH_STEPS; - float3 colLow = float3(0.1f, 0.95f, 0.4f); - float3 colMid = float3(0.1f, 0.85f, 0.85f); - float3 colHigh = float3(0.65f, 0.25f, 0.95f); - float t1 = smoothstep(0.0f, 0.45f, h); - float t2 = smoothstep(0.35f, 1.0f, h); - float3 baseCol = lerp(lerp(colLow, colMid, t1), colHigh, t2); - baseCol += sin(float3(0.0f, 1.0f, 2.0f) + h * 6.0f + rp.colorJitter) * 0.06f; - baseCol = max(baseCol, float3(0.0f)); + float h = float(i) / MARCH_STEPS; + float3 colLow = float3(0.1f, 0.95f, 0.4f); + float3 colMid = float3(0.1f, 0.85f, 0.85f); + float3 colHigh = float3(0.65f, 0.25f, 0.95f); + float t1 = smoothstep(0.0f, 0.45f, h); + float t2 = smoothstep(0.35f, 1.0f, h); + float3 baseCol = lerp(lerp(colLow, colMid, t1), colHigh, t2); + baseCol += sin(float3(0.0f, 1.0f, 2.0f) + h * 6.0f + rp.colorJitter) * 0.06f; + baseCol = max(baseCol, float3(0.0f)); - float3 mouseCol = float3(0.15f, 0.75f, 1.0f); - float3 stepColor = lerp(baseCol, mouseCol, saturate(trailField * 1.5f)); + float3 mouseCol = float3(0.15f, 0.75f, 1.0f); + float3 stepColor = lerp(baseCol, mouseCol, saturate(trailField * 1.5f)); - float absorption = exp(-accAlpha * 2.0f); - accColor += stepColor * density * absorption; - accAlpha += density * 0.05f; - } + float absorption = exp(-accAlpha * 2.0f); + accColor += stepColor * density * absorption; + accAlpha += density * 0.05f; + } - accColor *= AURORA_INTENSITY * rp.intensityMul; + accColor *= AURORA_INTENSITY * rp.intensityMul; - return float4(accColor, saturate(accAlpha)); + return float4(accColor, saturate(accAlpha)); } // === SMOOTH CUTOFF === -float smoothCutoff(float value, float floor_, float ceiling) { - float t = saturate((value - floor_) / max(ceiling - floor_, 0.001f)); - return t * t * (3.0f - 2.0f * t); +float +smoothCutoff(float value, float floor_, float ceiling) { + float t = saturate((value - floor_) / max(ceiling - floor_, 0.001f)); + return t * t * (3.0f - 2.0f * t); } // === ENTRY POINT === -public -float4 fragment_main(float4 color, KittyTextures t, KittyCustomShaderData d) { - float2 pixelDims = float2(d.viewport_size_pixels) * t.viewport.zw; - float timestamp = fmod(d.timestamp, 86400.0f) * TIME_SCALE; +public float4 +fragment_main(float4 color, KittyTextures t, KittyCustomShaderData d) { + float2 pixelDims = float2(d.viewport_size_pixels) * t.viewport.zw; + float timestamp = fmod(d.timestamp, 86400.0f) * TIME_SCALE; - float2 uv = t.pos; - float aspect = pixelDims.x / max(pixelDims.y, 1.0f); + float2 uv = t.pos; + float aspect = pixelDims.x / max(pixelDims.y, 1.0f); - float2 mouseUV = (d.mouse_pos.xy - t.viewport.xy) / max(t.viewport.zw, float2(1e-5f, 1e-5f)); - bool inWindow = all(mouseUV >= float2(0.0f)) && all(mouseUV <= float2(1.0f)); - float mouseValid = float(inWindow); - if (MOUSE_IGNORE_WHEN_HIDDEN > 0.5f && d.mouse_pointer_hidden > 0.5f) - mouseValid = 0.0f; + float2 mouseUV = (d.mouse_pos.xy - t.viewport.xy) / max(t.viewport.zw, float2(1e-5f, 1e-5f)); + bool inWindow = all(mouseUV >= float2(0.0f)) && all(mouseUV <= float2(1.0f)); + float mouseValid = float(inWindow); + if (MOUSE_IGNORE_WHEN_HIDDEN > 0.5f && d.mouse_pointer_hidden > 0.5f) mouseValid = 0.0f; - // First group: Accumulate radial aurora rays cast from sky center into scratch texture 'a' - if (STAGE == 0) { - float prev = t.persist.Sample(uv).r; - float decayed = prev * saturate(1.0f - ACCUMULATION_DECAY); + // First group: Accumulate radial aurora rays cast from sky center into scratch texture 'a' + if (STAGE == 0) { + float prev = t.persist.Sample(uv).r; + float decayed = prev * saturate(1.0f - ACCUMULATION_DECAY); - float deposit = 0.0f; - if (mouseValid > 0.5f && d.mouse_button_pressed.x > 0.5f) { - // Aspect-corrected UVs relative to screen center (0.5, 0.5) - float2 cUV = (uv - 0.5f) * float2(aspect, 1.0f); - float2 mUV = (mouseUV - 0.5f) * float2(aspect, 1.0f); + float deposit = 0.0f; + if (mouseValid > 0.5f && d.mouse_button_pressed.x > 0.5f) { + // Aspect-corrected UVs relative to screen center (0.5, 0.5) + float2 cUV = (uv - 0.5f) * float2(aspect, 1.0f); + float2 mUV = (mouseUV - 0.5f) * float2(aspect, 1.0f); - // Radial direction vector from sky center through the mouse click - float mLen = length(mUV); - float2 mDir = (mLen > 0.0001f) ? (mUV / mLen) : float2(0.0f, 1.0f); - float2 mTangent = float2(-mDir.y, mDir.x); // Perpendicular to radial ray + // Radial direction vector from sky center through the mouse click + float mLen = length(mUV); + float2 mDir = (mLen > 0.0001f) ? (mUV / mLen) : float2(0.0f, 1.0f); + float2 mTangent = float2(-mDir.y, mDir.x); // Perpendicular to radial ray - // Difference vector from click point to current pixel - float2 diff = cUV - mUV; + // Difference vector from click point to current pixel + float2 diff = cUV - mUV; - // Project onto radial (along) and tangential (across) axes - float distAlongRay = dot(diff, mDir); - float distAcrossRay = dot(diff, mTangent); + // Project onto radial (along) and tangential (across) axes + float distAlongRay = dot(diff, mDir); + float distAcrossRay = dot(diff, mTangent); - // Asymmetric ray length extending outward from the click point - float yMax = (distAlongRay >= 0.0f) ? MOUSE_RAY_LENGTH : (MOUSE_RAY_LENGTH * 0.25f); + // Asymmetric ray length extending outward from the click point + float yMax = (distAlongRay >= 0.0f) ? MOUSE_RAY_LENGTH : (MOUSE_RAY_LENGTH * 0.25f); - float hDist = abs(distAcrossRay) / max(MOUSE_RAY_WIDTH, 0.001f); - float vDist = abs(distAlongRay) / max(yMax, 0.001f); + float hDist = abs(distAcrossRay) / max(MOUSE_RAY_WIDTH, 0.001f); + float vDist = abs(distAlongRay) / max(yMax, 0.001f); - // High-frequency curtain thread streaking along the ray width - float streaks1 = sin(distAcrossRay * MOUSE_RAY_FREQUENCY) * 0.35f + 0.65f; - float streaks2 = cos(distAcrossRay * MOUSE_RAY_FREQUENCY * 2.3f) * 0.25f + 0.75f; + // High-frequency curtain thread streaking along the ray width + float streaks1 = sin(distAcrossRay * MOUSE_RAY_FREQUENCY) * 0.35f + 0.65f; + float streaks2 = cos(distAcrossRay * MOUSE_RAY_FREQUENCY * 2.3f) * 0.25f + 0.75f; - float hFalloff = smoothstep(1.0f, 0.0f, hDist); - float vFalloff = smoothstep(1.0f, 0.0f, vDist); + float hFalloff = smoothstep(1.0f, 0.0f, hDist); + float vFalloff = smoothstep(1.0f, 0.0f, vDist); - deposit = hFalloff * vFalloff * streaks1 * streaks2 * MOUSE_PRESS_BOOST; + deposit = hFalloff * vFalloff * streaks1 * streaks2 * MOUSE_PRESS_BOOST; + } + + return float4(saturate(decayed + deposit), 0.0f, 0.0f, 1.0f); } - return float4(saturate(decayed + deposit), 0.0f, 0.0f, 1.0f); - } + // Second group: Copy scratch texture 'a' to 'persist' + if (STAGE == 1) { return t.a.Sample(uv); } - // Second group: Copy scratch texture 'a' to 'persist' - if (STAGE == 1) { - return t.a.Sample(uv); - } + // Third group: Main composite pass + AuroraRandParams rp = getAuroraParams(d.timestamp, INITIAL_RANDOM_SEED); + float epochBrightness = transitionBrightness(d.timestamp); + float noiseTime = timestamp + rp.timeOffset; - // Third group: Main composite pass - AuroraRandParams rp = getAuroraParams(d.timestamp, INITIAL_RANDOM_SEED); - float epochBrightness = transitionBrightness(d.timestamp); - float noiseTime = timestamp + rp.timeOffset; + float2 fragCoord = uv * pixelDims; - float2 fragCoord = uv * pixelDims; + float persistentTrail = t.persist.Sample(uv).r * MOUSE_PERSIST_INTENSITY; - float persistentTrail = t.persist.Sample(uv).r * MOUSE_PERSIST_INTENSITY; + float3 ro = float3(0.0f, 0.0f, -CAMERA_DISTANCE); + float2 centeredUV = uv - 0.5f; - float3 ro = float3(0.0f, 0.0f, -CAMERA_DISTANCE); - float2 centeredUV = uv - 0.5f; + float horizonRadius = 1.5f; - float horizonRadius = 1.5f; + float spinAngle = timestamp * 0.02f; + float cs = cos(spinAngle), ss = sin(spinAngle); - float spinAngle = timestamp * 0.02f; - float cs = cos(spinAngle), ss = sin(spinAngle); + float3 rd = zenithRayDir(centeredUV, aspect, horizonRadius, cs, ss); - float3 rd = zenithRayDir(centeredUV, aspect, horizonRadius, cs, ss); + float3 bgCol = lerp(SKY_COLOR_TOP, SKY_COLOR_BOTTOM, 1.0f - rd.y) * 0.63f; + float3 col = bgCol; - float3 bgCol = lerp(SKY_COLOR_TOP, SKY_COLOR_BOTTOM, 1.0f - rd.y) * 0.63f; - float3 col = bgCol; + float2 mouseWorld = float2(0.0f, 0.0f); - float2 mouseWorld = float2(0.0f, 0.0f); + if (mouseValid > 0.5f) { + float2 mCenteredUV = mouseUV - 0.5f; + float3 mrd = zenithRayDir(mCenteredUV, aspect, horizonRadius, cs, ss); - if (mouseValid > 0.5f) { - float2 mCenteredUV = mouseUV - 0.5f; - float3 mrd = zenithRayDir(mCenteredUV, aspect, horizonRadius, cs, ss); - - float denom = mrd.y * 2.0f + 0.4f; - if (denom > 0.001f) { - float dist = (AURORA_ALTITUDE - ro.y) / denom; - mouseWorld = (ro + mrd * dist).xz; + float denom = mrd.y * 2.0f + 0.4f; + if (denom > 0.001f) { + float dist = (AURORA_ALTITUDE - ro.y) / denom; + mouseWorld = (ro + mrd * dist).xz; + } } - } - float genStrength = MOUSE_AURORA_INTENSITY * - (1.0f + d.mouse_button_pressed.x * MOUSE_PRESS_BOOST); + float genStrength = MOUSE_AURORA_INTENSITY * (1.0f + d.mouse_button_pressed.x * MOUSE_PRESS_BOOST); - float4 aur = smoothstep(0.0f, 1.5f, - rayMarchAurora(ro, rd, fragCoord, noiseTime, rp, - mouseWorld, mouseValid, genStrength, persistentTrail)); - col += sampleStars(rd, pixelDims, timestamp); - col = col * (1.0f - aur.a) + aur.rgb; + float4 aur = smoothstep(0.0f, 1.5f, rayMarchAurora(ro, rd, fragCoord, noiseTime, rp, mouseWorld, mouseValid, genStrength, persistentTrail)); + col += sampleStars(rd, pixelDims, timestamp); + col = col * (1.0f - aur.a) + aur.rgb; - col *= EFFECT_INTENSITY * epochBrightness; + col *= EFFECT_INTENSITY * epochBrightness; - float effectLum = dot(col, float3(0.299f, 0.587f, 0.114f)); - float gate = smoothCutoff(effectLum, EFFECT_FLOOR, EFFECT_CEILING); - col *= gate; + float effectLum = dot(col, float3(0.299f, 0.587f, 0.114f)); + float gate = smoothCutoff(effectLum, EFFECT_FLOOR, EFFECT_CEILING); + col *= gate; - float4 terminalColor = t.backbuffer.Sample(t.pos); - float termLum = dot(terminalColor.rgb, float3(0.299f, 0.587f, 0.114f)); - float isBlack = step(termLum, 0.001f); - float textPreserve = 1.0f - smoothstep(BLACK_BLEND_THRESHOLD, - BLACK_BLEND_THRESHOLD + 0.2f, termLum); - float blendFactor = lerp(textPreserve * EFFECT_INTENSITY, 1.0f, isBlack); - float3 blendedColor = terminalColor.rgb + col * blendFactor; + float4 terminalColor = t.backbuffer.Sample(t.pos); + float termLum = dot(terminalColor.rgb, float3(0.299f, 0.587f, 0.114f)); + float isBlack = step(termLum, 0.001f); + float textPreserve = 1.0f - smoothstep(BLACK_BLEND_THRESHOLD, BLACK_BLEND_THRESHOLD + 0.2f, termLum); + float blendFactor = lerp(textPreserve * EFFECT_INTENSITY, 1.0f, isBlack); + float3 blendedColor = terminalColor.rgb + col * blendFactor; - if (DEBUG_MOUSE_OVERLAY > 0.5f && mouseValid > 0.5f) { - float ptProxy = (AURORA_ALTITUDE - ro.y) / (rd.y * 2.0f + 0.4f); - float g = mouseTrailField(ro + rd * ptProxy, mouseWorld, 1.0f, genStrength); - blendedColor = - lerp(blendedColor, float3(1.0f, 0.25f, 0.05f), saturate(g * 2.0f)); - } + if (DEBUG_MOUSE_OVERLAY > 0.5f && mouseValid > 0.5f) { + float ptProxy = (AURORA_ALTITUDE - ro.y) / (rd.y * 2.0f + 0.4f); + float g = mouseTrailField(ro + rd * ptProxy, mouseWorld, 1.0f, genStrength); + blendedColor = lerp(blendedColor, float3(1.0f, 0.25f, 0.05f), saturate(g * 2.0f)); + } - return float4(blendedColor, terminalColor.a); + return float4(blendedColor, terminalColor.a); } diff --git a/kitty/shaders/custom/pipeline.slang b/kitty/shaders/custom/pipeline.slang index 1d9251e03..e1fef0bb0 100644 --- a/kitty/shaders/custom/pipeline.slang +++ b/kitty/shaders/custom/pipeline.slang @@ -17,14 +17,10 @@ public struct BlitOutput { #define bottom 3 // Static constant array mapping vertex IDs -static const int2 vertex_pos_map[4] = { - {right, top}, - {right, bottom}, - {left, bottom}, - {left, top} -}; +static const int2 vertex_pos_map[4] = { { right, top }, { right, bottom }, { left, bottom }, { left, top } }; -BlitOutput get_coords_for_blit(uint vertex_id, float4 src_rect, float4 dest_rect) { +BlitOutput +get_coords_for_blit(uint vertex_id, float4 src_rect, float4 dest_rect) { int2 pos = vertex_pos_map[vertex_id]; BlitOutput output; output.texcoord = float2(src_rect[pos.x], src_rect[pos.y]); @@ -36,23 +32,27 @@ BlitOutput get_coords_for_blit(uint vertex_id, float4 src_rect, float4 dest_rect public ConstantBuffer csd; -public float4 pipeline_vertex_main(uint vertex_id) { +public float4 +pipeline_vertex_main(uint vertex_id) { BlitOutput ans = get_coords_for_blit(vertex_id, csd.src_rect, csd.dest_rect); return float4(ans.texcoord[0], ans.texcoord[1], ans.position[0], ans.position[1]); } -float4 vec4_premul(float3 rgb, float a) { +float4 +vec4_premul(float3 rgb, float a) { return float4(rgb * a, a); } -float linear2srgb(float x) { // scalar +float +linear2srgb(float x) { // scalar float lower = 12.92 * x; float upper = 1.055 * pow(x, 1.0f / 2.4f) - 0.055f; return lerp(lower, upper, step(0.0031308f, x)); } -float3 linear2srgb(float3 x) { // vector +float3 +linear2srgb(float3 x) { // vector float3 lower = 12.92 * x; float3 upper = 1.055 * pow(x, float3(1.0f / 2.4f)) - 0.055f; @@ -61,11 +61,11 @@ float3 linear2srgb(float3 x) { // vector uniform Sampler2D backbuffer, a, b, persist; -public float4 pipeline_fragment_main(float2 backbuffer_pos, int group, float4 viewport, float animation_progress, bool convert_to_srgb) { - float4 color = backbuffer.Sample(backbuffer_pos); // pre multiplied linear RGB - color = float4(color.rgb / color.a, color.a); // un pre multiplied color passed through pipeline - KittyTextures t = {backbuffer, a, b, persist, backbuffer_pos, viewport, animation_progress, group}; +public float4 +pipeline_fragment_main(float2 backbuffer_pos, int group, float4 viewport, float animation_progress, bool convert_to_srgb) { + float4 color = backbuffer.Sample(backbuffer_pos); // pre multiplied linear RGB + color = float4(color.rgb / color.a, color.a); // un pre multiplied color passed through pipeline + KittyTextures t = { backbuffer, a, b, persist, backbuffer_pos, viewport, animation_progress, group }; // PIPELINE return vec4_premul(color.rgb, color.a); } - diff --git a/kitty/shaders/custom/pond-ripple.slang b/kitty/shaders/custom/pond-ripple.slang index cef48c99e..429452c1d 100644 --- a/kitty/shaders/custom/pond-ripple.slang +++ b/kitty/shaders/custom/pond-ripple.slang @@ -9,37 +9,34 @@ import kitty_custom_shader_types; // Maximum wavefront radius at animation_progress=1 (aspect-corrected UV units). -static const float WAVE_SPEED = 0.45; +static const float WAVE_SPEED = 0.45; // Spatial frequency of the rings (cycles per aspect-corrected UV unit). -static const float WAVE_FREQUENCY = 14.0; +static const float WAVE_FREQUENCY = 14.0; // Starting half-width of the Gaussian envelope (expands as animation_progress increases). -static const float WAVE_WIDTH = 0.055; +static const float WAVE_WIDTH = 0.055; // Peak radial UV displacement at the wavecrest. -static const float AMPLITUDE = 0.010; +static const float AMPLITUDE = 0.010; // Scale applied to the wave gradient when constructing the surface normal. -static const float NORMAL_SCALE = 8.0; +static const float NORMAL_SCALE = 8.0; // Blinn-Phong shininess exponent — higher values give a sharper, more mirror-like glint. -static const float SHININESS = 64.0; +static const float SHININESS = 64.0; // Peak specular contribution at the wave crest facing the light. static const float SPECULAR_STRENGTH = 0.40; // Subtle diffuse shading that rounds each ring into a 3D ridge. -static const float DIFFUSE_STRENGTH = 0.10; +static const float DIFFUSE_STRENGTH = 0.10; -public float4 fragment_main( - float4 color, - KittyTextures t, - KittyCustomShaderData d -) { +public float4 +fragment_main(float4 color, KittyTextures t, KittyCustomShaderData d) { float2 pixelDims = float2(d.viewport_size_pixels) * t.viewport.zw; - float aspect = pixelDims.x / pixelDims.y; + float aspect = pixelDims.x / pixelDims.y; // d.mouse_pos.zw is the UV position of the last left-button press (y=0 at bottom). float2 center = d.mouse_pos.zw; // Aspect-corrected vector from the click center to this pixel. float2 delta = (t.pos - center) * float2(aspect, 1.0); - float dist = length(delta); - float2 dir = delta / max(dist, 1e-5); + float dist = length(delta); + float2 dir = delta / max(dist, 1e-5); // Wavefront radius advances linearly with animation_progress. float front = WAVE_SPEED * t.animation_progress; @@ -49,7 +46,7 @@ public float4 fragment_main( // Gaussian spatial envelope centered at the wavefront; grows in width over time. float sigma = WAVE_WIDTH * (1.0 + t.animation_progress); - float env = exp(-behind * behind / (2.0 * sigma * sigma)); + float env = exp(-behind * behind / (2.0 * sigma * sigma)); // Causality: suppress any energy ahead of the wavefront. env *= 1.0 - smoothstep(-0.01, 0.02, behind); @@ -57,7 +54,7 @@ public float4 fragment_main( // Temporal amplitude decay as the wave expands outward. float decay = pow(1.0 - t.animation_progress, 0.7); - float k = WAVE_FREQUENCY * 6.28318530718; + float k = WAVE_FREQUENCY * 6.28318530718; float wave = sin(behind * k); // Analytic derivative of wave height h = wave * env * decay w.r.t. dist. @@ -77,22 +74,17 @@ public float4 fragment_main( // Blinn-Phong half-vector. float3 H = normalize(L + V); - float diffuse = max(dot(N, L), 0.0); + float diffuse = max(dot(N, L), 0.0); float specular = pow(max(dot(N, H), 0.0), SHININESS); // Radial UV displacement: push pixels away from center at crests, pull inward at troughs. - float2 warpedUV = clamp( - t.pos + dir * (wave * env * decay * AMPLITUDE), - float2(0.0), float2(1.0) - ); + float2 warpedUV = clamp(t.pos + dir * (wave * env * decay * AMPLITUDE), float2(0.0), float2(1.0)); // Sample the terminal backbuffer at the displaced position. float4 result = t.backbuffer.Sample(warpedUV); // 3D lighting: diffuse rounds each ring into a ridge, specular gives directional glints. - float lighting = 1.0 - + diffuse * DIFFUSE_STRENGTH * env * decay - + specular * SPECULAR_STRENGTH * env * decay; + float lighting = 1.0 + diffuse * DIFFUSE_STRENGTH * env * decay + specular * SPECULAR_STRENGTH * env * decay; result.rgb *= lighting; return result; diff --git a/kitty/shaders/custom/sample.slang b/kitty/shaders/custom/sample.slang index b6446c8af..be52d9859 100644 --- a/kitty/shaders/custom/sample.slang +++ b/kitty/shaders/custom/sample.slang @@ -4,7 +4,11 @@ import kitty_custom_shader_types; -public float4 fragment_main( +static const float FACTOR = 2; + +// START_FUNCTION_SIGNATURE +public float4 +fragment_main( // The color from the previous custom shader in this group or from the // backbuffer if this is the first shader in the group float4 color, @@ -13,8 +17,7 @@ public float4 fragment_main( KittyTextures t, // See types.slang for details on the data in this structure - KittyCustomShaderData d -) -{ // END_FUNCTION_SIGNATURE - return float4(color.r, min(max(0.1, color.g) * 2, 1), color.b, color.a); + KittyCustomShaderData d) { + // END_FUNCTION_SIGNATURE + return float4(color.r, min(max(0.1, color.g) * FACTOR, 1), color.b, color.a); } diff --git a/kitty/shaders/custom/spotlight.slang b/kitty/shaders/custom/spotlight.slang index d283f8a0a..eaa6c6d9d 100644 --- a/kitty/shaders/custom/spotlight.slang +++ b/kitty/shaders/custom/spotlight.slang @@ -7,42 +7,36 @@ import kitty_custom_shader_types; -static const float SPOTLIGHT_RADIUS = 0.25; // spotlight radius in UV units -static const float SPOTLIGHT_SOFTNESS = 20.0; // edge sharpness; higher = sharper -static const float AMBIENT_LIGHT = 0.5; // minimum brightness outside the spotlight -static const float SPOTLIGHT_SPEED = 1.0; // oscillation speed in random-motion mode +static const float SPOTLIGHT_RADIUS = 0.25; // spotlight radius in UV units +static const float SPOTLIGHT_SOFTNESS = 20.0; // edge sharpness; higher = sharper +static const float AMBIENT_LIGHT = 0.5; // minimum brightness outside the spotlight +static const float SPOTLIGHT_SPEED = 1.0; // oscillation speed in random-motion mode // Set to 1 to follow the mouse, 0 to use random oscillation -static const int USE_MOUSE = 1; +static const int USE_MOUSE = 1; // When USE_MOUSE=1 and the mouse leaves the window: // 0 = fall back to random oscillation // 1 = clamp spotlight to the nearest window edge // 2 = disable spotlight (pass through unchanged pixels) -static const int MOUSE_LEAVE_BEHAVIOR = 2; +static const int MOUSE_LEAVE_BEHAVIOR = 2; // Set to 1 to pass through unchanged pixels when the mouse pointer is hidden -static const int HIDE_WHEN_POINTER_HIDDEN = 1; +static const int HIDE_WHEN_POINTER_HIDDEN = 1; -public float4 fragment_main( - float4 color, - KittyTextures t, - KittyCustomShaderData d -) { +public float4 +fragment_main(float4 color, KittyTextures t, KittyCustomShaderData d) { if (HIDE_WHEN_POINTER_HIDDEN == 1 && d.mouse_pointer_hidden != 0.0) return color; float2 uv = t.pos; // Viewport pixel dimensions for aspect-ratio correction - float2 pixelDims = float2(d.viewport_size_pixels) * t.viewport.zw; - float aspectRatio = pixelDims.x / pixelDims.y; + float2 pixelDims = float2(d.viewport_size_pixels) * t.viewport.zw; + float aspectRatio = pixelDims.x / pixelDims.y; // Random oscillating center (different frequencies and phases on each axis) - float time = d.timestamp * SPOTLIGHT_SPEED; - float2 randomCenter = float2( - 0.5 + 0.4 * sin(time), - 0.5 + 0.4 * sin(time * 1.3 + 3.14159) - ); + float time = d.timestamp * SPOTLIGHT_SPEED; + float2 randomCenter = float2(0.5 + 0.4 * sin(time), 0.5 + 0.4 * sin(time * 1.3 + 3.14159)); // Mouse is valid only when both coords are inside the OS window [0, 1] - float2 mp = d.mouse_pos.xy; - float mouseInWindow = float(all(mp >= float2(0.0)) && all(mp <= float2(1.0))); + float2 mp = d.mouse_pos.xy; + float mouseInWindow = float(all(mp >= float2(0.0)) && all(mp <= float2(1.0))); // Disable spotlight entirely when mouse leaves the window if (USE_MOUSE == 1 && MOUSE_LEAVE_BEHAVIOR == 2 && mouseInWindow == 0.0) return color; @@ -51,18 +45,14 @@ public float4 fragment_main( float2 outsideFallback = lerp(randomCenter, clamp(mp, float2(0.0), float2(1.0)), float(MOUSE_LEAVE_BEHAVIOR == 1 ? 1 : 0)); // Branchless center selection - float2 mouseCenter = lerp(outsideFallback, mp, mouseInWindow); + float2 mouseCenter = lerp(outsideFallback, mp, mouseInWindow); float2 spotlightCenter = lerp(randomCenter, mouseCenter, float(USE_MOUSE)); // Euclidean distance in aspect-ratio-corrected space (produces a circular spotlight) float distToCenter = length((uv - spotlightCenter) * float2(aspectRatio, 1.0)); // Soft spotlight edge - float spotlightIntensity = smoothstep( - SPOTLIGHT_RADIUS, - SPOTLIGHT_RADIUS - (1.0 / SPOTLIGHT_SOFTNESS), - distToCenter - ); + float spotlightIntensity = smoothstep(SPOTLIGHT_RADIUS, SPOTLIGHT_RADIUS - (1.0 / SPOTLIGHT_SOFTNESS), distToCenter); // Blend between ambient and full illumination float3 result = color.rgb * lerp(float3(AMBIENT_LIGHT), float3(1.0), spotlightIntensity); diff --git a/kitty/shaders/custom/tab-change.slang b/kitty/shaders/custom/tab-change.slang index a5532bba0..1b25ec745 100644 --- a/kitty/shaders/custom/tab-change.slang +++ b/kitty/shaders/custom/tab-change.slang @@ -27,12 +27,13 @@ static const float EDGE_SOFTNESS = 0.04; // Returns a value in [0, 1] for this pixel: 0 at the tab-bar edge of the // content area, 1 at the opposite edge. The wipe sweeps from 0 toward 1. -float wipe_pixel_pos(float2 uv, float4 ca) { - float gap_left = ca.x; - float gap_right = 1.0 - (ca.x + ca.z); +float +wipe_pixel_pos(float2 uv, float4 ca) { + float gap_left = ca.x; + float gap_right = 1.0 - (ca.x + ca.z); float gap_bottom = ca.y; - float gap_top = 1.0 - (ca.y + ca.w); - float max_gap = max(max(gap_left, gap_right), max(gap_bottom, gap_top)); + float gap_top = 1.0 - (ca.y + ca.w); + float max_gap = max(max(gap_left, gap_right), max(gap_bottom, gap_top)); float pos; if (max_gap == gap_top) { @@ -51,11 +52,8 @@ float wipe_pixel_pos(float2 uv, float4 ca) { return clamp(pos, 0.0, 1.0); } -public float4 fragment_main( - float4 color, - KittyTextures t, - KittyCustomShaderData d -) { +public float4 +fragment_main(float4 color, KittyTextures t, KittyCustomShaderData d) { float4 ca = d.central_area; if (ca.z <= 0.0 || ca.w <= 0.0) return color; @@ -73,20 +71,20 @@ public float4 fragment_main( } else if (ANIMATION_TYPE == 2) { // BLINDS: strips opening in sequence from the tab-bar side float pixel_pos = wipe_pixel_pos(t.pos, ca); - float inv_n = 1.0 / float(NUM_BLINDS); + float inv_n = 1.0 / float(NUM_BLINDS); float strip_idx = floor(pixel_pos * float(NUM_BLINDS)); float strip_start = strip_idx * inv_n; // Progress local to this strip: 0 when it starts opening, 1 when fully open - float strip_p = clamp((p - strip_start) / inv_n, 0.0, 1.0); + float strip_p = clamp((p - strip_start) / inv_n, 0.0, 1.0); // Position within the strip (0 at tab-bar side, 1 at far side) - float local = fract(pixel_pos * float(NUM_BLINDS)); - float soft = 0.1; + float local = fract(pixel_pos * float(NUM_BLINDS)); + float soft = 0.1; brightness = 1.0 - smoothstep(strip_p - soft, strip_p + soft, local); } else { // IRIS: circular reveal expanding from the centre of the content area float2 ca_center = ca.xy + ca.zw * 0.5; - float half_diag = length(ca.zw * 0.5); - float dist = length(t.pos - ca_center) / max(half_diag, 0.001); + float half_diag = length(ca.zw * 0.5); + float dist = length(t.pos - ca_center) / max(half_diag, 0.001); brightness = 1.0 - smoothstep(p - EDGE_SOFTNESS, p + EDGE_SOFTNESS, dist); } diff --git a/kitty/shaders/custom/tft.slang b/kitty/shaders/custom/tft.slang index eae38713f..2169992a8 100644 --- a/kitty/shaders/custom/tft.slang +++ b/kitty/shaders/custom/tft.slang @@ -12,17 +12,15 @@ static const float RESOLUTION = 4.0; // strength of the effect static const float STRENGTH = 0.5; -void scanline(inout float3 color, float2 pixel_pos) { +void +scanline(inout float3 color, float2 pixel_pos) { float scanline = step(1.2, fmod(pixel_pos.y, RESOLUTION)); - float grille = step(1.2, fmod(pixel_pos.x, RESOLUTION)); + float grille = step(1.2, fmod(pixel_pos.x, RESOLUTION)); color *= max(1.0 - STRENGTH, scanline * grille); } -public float4 fragment_main( - float4 color, - KittyTextures t, - KittyCustomShaderData d -) { +public float4 +fragment_main(float4 color, KittyTextures t, KittyCustomShaderData d) { float2 pixel_pos = t.pos * float2(d.viewport_size_pixels); float3 c = color.rgb; scanline(c, pixel_pos); diff --git a/kitty/shaders/custom/types.slang b/kitty/shaders/custom/types.slang index 2ab0f61ca..db4fa0ab8 100644 --- a/kitty/shaders/custom/types.slang +++ b/kitty/shaders/custom/types.slang @@ -4,7 +4,7 @@ public struct KittyCustomShaderData { - public float4 src_rect, dest_rect; // used by the vertex shader internally + public float4 src_rect, dest_rect; // used by the vertex shader internally // global background/foreground colors from kitty options (linear RGB, alpha=1) public float4 background, foreground; @@ -39,8 +39,8 @@ public struct KittyCustomShaderData { // Corner order: 0=top-right, 1=bottom-right, 2=bottom-left, 3=top-left. // Use animation_start cursor-trail-move / animation_stop cursor-trail-stop to activate. // animation_progress is NOT tied to trail duration; use cursor_trail_state.x for opacity instead. - public float4 cursor_trail_corners_x; // UV x-coordinates of the 4 animated trail quad corners - public float4 cursor_trail_corners_y; // UV y-coordinates of the 4 animated trail quad corners + public float4 cursor_trail_corners_x; // UV x-coordinates of the 4 animated trail quad corners + public float4 cursor_trail_corners_y; // UV y-coordinates of the 4 animated trail quad corners // .x=left, .y=right, .z=top, .w=bottom UV edges of the current cursor rectangle public float4 cursor_trail_edge; // .x=left, .y=right, .z=top, .w=bottom UV edges of the cursor rectangle before the most recent move. @@ -61,12 +61,12 @@ public struct KittyCustomShaderData { // viewport currently being rendered too. public uint2 viewport_size_pixels; - public float mouse_pointer_hidden; // 1.0 if the mouse pointer is currently hidden, 0.0 if visible - public float cursor_trail_state; // 1.0 if the cursor trail is actively animating, 0.0 otherwise + public float mouse_pointer_hidden; // 1.0 if the mouse pointer is currently hidden, 0.0 if visible + public float cursor_trail_state; // 1.0 if the cursor trail is actively animating, 0.0 otherwise - public float timestamp; // time in seconds since kitty was started (millisecond precision) - public float last_rendered_at; // time of previous render in seconds since kitty was started (millisecond precision) - public uint frame_counter; // cumulative frame counter increments by one every time a frame is fully rendered, first frame is zero + public float timestamp; // time in seconds since kitty was started (millisecond precision) + public float last_rendered_at; // time of previous render in seconds since kitty was started (millisecond precision) + public uint frame_counter; // cumulative frame counter increments by one every time a frame is fully rendered, first frame is zero // Time (same epoch as `timestamp`) of the most recent cursor position change that triggered a trail. // Zero until the cursor has moved at least twice. Pair with cursor_trail_prev_edge. public float cursor_trail_change_time; @@ -84,7 +84,7 @@ public struct KittyTextures { public Sampler2D b; public Sampler2D persist; - public float2 pos; // the position of this pixel in the textures (UV coordinates) + public float2 pos; // the position of this pixel in the textures (UV coordinates) // Some group related uniforms diff --git a/kitty/shaders/custom/underwater.slang b/kitty/shaders/custom/underwater.slang index d1f8fe7de..0634fa554 100644 --- a/kitty/shaders/custom/underwater.slang +++ b/kitty/shaders/custom/underwater.slang @@ -11,87 +11,75 @@ import kitty_custom_shader_types; static const float BLACK_BLEND_THRESHOLD = 0.4; // Ray 1 source position (as fractions of viewport dimensions) -static const float RAY1_SRC_X = 0.7; -static const float RAY1_SRC_Y = 1.1; +static const float RAY1_SRC_X = 0.7; +static const float RAY1_SRC_Y = 1.1; // Y component of ray 1 reference direction (X component is always 1.0) -static const float RAY1_DIR_Y = 0.116; +static const float RAY1_DIR_Y = 0.116; static const float RAY1_SEED_A = 36.2214; static const float RAY1_SEED_B = 21.11349; -static const float RAY1_SPEED = 1.1; -static const float RAY1_WEIGHT = 0.5; // contribution weight in final blend +static const float RAY1_SPEED = 1.1; +static const float RAY1_WEIGHT = 0.5; // contribution weight in final blend // Ray 2 source position (as fractions of viewport dimensions) -static const float RAY2_SRC_X = 0.8; -static const float RAY2_SRC_Y = 1.2; -static const float RAY2_DIR_Y = -0.241; +static const float RAY2_SRC_X = 0.8; +static const float RAY2_SRC_Y = 1.2; +static const float RAY2_DIR_Y = -0.241; static const float RAY2_SEED_A = 22.39910; static const float RAY2_SEED_B = 18.0234; -static const float RAY2_SPEED = 0.9; +static const float RAY2_SPEED = 0.9; static const float RAY2_WEIGHT = 0.4; // Per-channel depth-attenuation: channel *= BASE + brightness * SCALE -static const float DEPTH_R_BASE = 0.05; +static const float DEPTH_R_BASE = 0.05; static const float DEPTH_R_SCALE = 0.8; -static const float DEPTH_G_BASE = 0.15; +static const float DEPTH_G_BASE = 0.15; static const float DEPTH_G_SCALE = 0.6; -static const float DEPTH_B_BASE = 0.3; +static const float DEPTH_B_BASE = 0.3; static const float DEPTH_B_SCALE = 0.5; // Overall strength of the ray overlay on dark terminal content static const float OVERLAY_STRENGTH = 0.3; -float hash21(float2 p) { +float +hash21(float2 p) { p = frac(p * float2(233.34, 851.73)); p += dot(p, p + 23.45); return frac(p.x * p.y); } -float rayStrength( - float2 raySource, float2 rayRefDir, float2 coord, - float seedA, float seedB, float speed, - float iTime, float resX -) { +float +rayStrength(float2 raySource, float2 rayRefDir, float2 coord, float seedA, float seedB, float speed, float iTime, float resX) { float2 sourceToCoord = coord - raySource; - float cosAngle = dot(normalize(sourceToCoord), rayRefDir); - float dither = hash21(coord) * 0.015 - 0.0075; - float ray = clamp( - (0.45 + 0.15 * sin( cosAngle * seedA + iTime * speed)) + - (0.30 + 0.20 * cos(-cosAngle * seedB + iTime * speed)) + dither, - 0.0, 1.0); + float cosAngle = dot(normalize(sourceToCoord), rayRefDir); + float dither = hash21(coord) * 0.015 - 0.0075; + float ray = clamp((0.45 + 0.15 * sin(cosAngle * seedA + iTime * speed)) + (0.30 + 0.20 * cos(-cosAngle * seedB + iTime * speed)) + dither, 0.0, 1.0); float distFade = smoothstep(0.0, resX, resX - length(sourceToCoord)); return ray * lerp(0.5, 1.0, distFade); } -public float4 fragment_main( - float4 color, - KittyTextures t, - KittyCustomShaderData d -) { +public float4 +fragment_main(float4 color, KittyTextures t, KittyCustomShaderData d) { float2 iResolution = float2(d.viewport_size_pixels) * t.viewport.zw; - float iTime = d.timestamp; + float iTime = d.timestamp; // Convert kitty UV (y=0 at bottom) to a top-left-origin pixel coordinate, // matching the original shader's internal coordinate system. float2 coord = float2(t.pos.x, 1.0 - t.pos.y) * iResolution; - float2 rayPos1 = float2(iResolution.x * RAY1_SRC_X, iResolution.y * RAY1_SRC_Y); + float2 rayPos1 = float2(iResolution.x * RAY1_SRC_X, iResolution.y * RAY1_SRC_Y); float2 rayRefDir1 = normalize(float2(1.0, RAY1_DIR_Y)); - float2 rayPos2 = float2(iResolution.x * RAY2_SRC_X, iResolution.y * RAY2_SRC_Y); + float2 rayPos2 = float2(iResolution.x * RAY2_SRC_X, iResolution.y * RAY2_SRC_Y); float2 rayRefDir2 = normalize(float2(1.0, RAY2_DIR_Y)); - float4 rays1 = float4(1.0, 1.0, 1.0, 0.0) * - rayStrength(rayPos1, rayRefDir1, coord, - RAY1_SEED_A, RAY1_SEED_B, RAY1_SPEED, iTime, iResolution.x); + float4 rays1 = float4(1.0, 1.0, 1.0, 0.0) * rayStrength(rayPos1, rayRefDir1, coord, RAY1_SEED_A, RAY1_SEED_B, RAY1_SPEED, iTime, iResolution.x); - float4 rays2 = float4(1.0, 1.0, 1.0, 0.0) * - rayStrength(rayPos2, rayRefDir2, coord, - RAY2_SEED_A, RAY2_SEED_B, RAY2_SPEED, iTime, iResolution.x); + float4 rays2 = float4(1.0, 1.0, 1.0, 0.0) * rayStrength(rayPos2, rayRefDir2, coord, RAY2_SEED_A, RAY2_SEED_B, RAY2_SPEED, iTime, iResolution.x); float4 col = rays1 * RAY1_WEIGHT + rays2 * RAY2_WEIGHT; // Attenuate toward the bottom (depth) with a blue-green tint - float brightness = t.pos.y; // 0 at screen bottom (deep), 1 at top (surface) + float brightness = t.pos.y; // 0 at screen bottom (deep), 1 at top (surface) col.r *= DEPTH_R_BASE + brightness * DEPTH_R_SCALE; col.g *= DEPTH_G_BASE + brightness * DEPTH_G_SCALE; col.b *= DEPTH_B_BASE + brightness * DEPTH_B_SCALE; @@ -99,7 +87,7 @@ public float4 fragment_main( float4 terminalColor = t.backbuffer.Sample(t.pos); // Overlay rays onto dark (near-black) terminal pixels; preserve bright content - float alpha = step(length(terminalColor.rgb), BLACK_BLEND_THRESHOLD); + float alpha = step(length(terminalColor.rgb), BLACK_BLEND_THRESHOLD); float3 blendedColor = lerp(terminalColor.rgb, col.rgb * OVERLAY_STRENGTH, alpha); return float4(blendedColor, terminalColor.a); diff --git a/kitty/shaders/custom/water.slang b/kitty/shaders/custom/water.slang index 5a0ba4dd1..5a589d43a 100644 --- a/kitty/shaders/custom/water.slang +++ b/kitty/shaders/custom/water.slang @@ -6,31 +6,26 @@ import kitty_custom_shader_types; -static const float TAU = 6.28318530718; -static const int MAX_ITER = 6; -static const float3 WATER_COLOR = float3(0.5, 0.5, 0.5); // water overlay tint -static const float CAUSTIC_INTEN = 0.005; // caustic ripple intensity -static const float UV_DISPLACEMENT = 0.04; // UV warp magnitude from caustics +static const float TAU = 6.28318530718; +static const int MAX_ITER = 6; +static const float3 WATER_COLOR = float3(0.5, 0.5, 0.5); // water overlay tint +static const float CAUSTIC_INTEN = 0.005; // caustic ripple intensity +static const float UV_DISPLACEMENT = 0.04; // UV warp magnitude from caustics -public float4 fragment_main( - float4 color, - KittyTextures t, - KittyCustomShaderData d -) { - float wtime = d.timestamp * 0.5 + 23.0; - float2 uv = t.pos; +public float4 +fragment_main(float4 color, KittyTextures t, KittyCustomShaderData d) { + float wtime = d.timestamp * 0.5 + 23.0; + float2 uv = t.pos; float2 p = fmod(uv * TAU, TAU) - 250.0; float2 i = p; - float c = 1.0; + float c = 1.0; [ForceUnroll] for (int n = 0; n < MAX_ITER; n++) { float tval = wtime * (1.0 - (3.5 / float(n + 1))); - i = p + float2(cos(tval - i.x) + sin(tval + i.y), - sin(tval - i.y) + cos(tval + i.x)); - c += 1.0 / length(float2(p.x / (sin(i.x + tval) / CAUSTIC_INTEN), - p.y / (cos(i.y + tval) / CAUSTIC_INTEN))); + i = p + float2(cos(tval - i.x) + sin(tval + i.y), sin(tval - i.y) + cos(tval + i.x)); + c += 1.0 / length(float2(p.x / (sin(i.x + tval) / CAUSTIC_INTEN), p.y / (cos(i.y + tval) / CAUSTIC_INTEN))); } c /= float(MAX_ITER); c = 1.17 - pow(c, 1.4); diff --git a/kitty/shaders/graphics.slang b/kitty/shaders/graphics.slang index 309cfff3e..eed8719b9 100644 --- a/kitty/shaders/graphics.slang +++ b/kitty/shaders/graphics.slang @@ -10,32 +10,25 @@ extern static const bool is_alpha_mask = false; extern static const bool texture_is_not_premultiplied = true; -struct VSOutput -{ +struct VSOutput { float2 texcoord : TEXCOORD; float4 position : SV_Position; }; [shader("vertex")] -VSOutput vertex_main( - uint vertex_id : SV_VertexID, - uniform float4 src_rect, - uniform float4 dest_rect, -) { +VSOutput +vertex_main(uint vertex_id: SV_VertexID, uniform float4 src_rect, uniform float4 dest_rect, ) { BlitOutput ans = get_coords_for_blit(vertex_id, src_rect, dest_rect); - return {ans.texcoord, float4(ans.position[0], ans.position[1], 0.0, 1.0)}; + return { ans.texcoord, float4(ans.position[0], ans.position[1], 0.0, 1.0) }; } uniform Sampler2D image; [shader("fragment")] -float4 fragment_main( - float2 texcoord : TEXCOORD, - uniform float3 amask_fg, - uniform float4 amask_bg_premult, - uniform float extra_alpha -) : SV_Target { +float4 +fragment_main(float2 texcoord: TEXCOORD, uniform float3 amask_fg, uniform float4 amask_bg_premult, uniform float extra_alpha) + : SV_Target { float4 color = image.Sample(texcoord); if (is_alpha_mask) { color = float4(amask_fg, color.r); diff --git a/kitty/shaders/hsluv.slang b/kitty/shaders/hsluv.slang index 9c69fadb9..2e3d66d46 100644 --- a/kitty/shaders/hsluv.slang +++ b/kitty/shaders/hsluv.slang @@ -5,48 +5,59 @@ module hsluv; // Helper Functions -float divide(float num, float denom) { +float +divide(float num, float denom) { return num / (abs(denom) + 1e-15) * sign(denom); } -float3 divide(float3 num, float3 denom) { +float3 +divide(float3 num, float3 denom) { return num / (abs(denom) + 1e-15) * sign(denom); } -float3 hsluv_intersectLineLine(float3 line1x, float3 line1y, float3 line2x, float3 line2y) { +float3 +hsluv_intersectLineLine(float3 line1x, float3 line1y, float3 line2x, float3 line2y) { return (line1y - line2y) / (line2x - line1x); } -float3 hsluv_distanceFromPole(float3 pointx, float3 pointy) { +float3 +hsluv_distanceFromPole(float3 pointx, float3 pointy) { return sqrt(pointx * pointx + pointy * pointy); } -float3 hsluv_lengthOfRayUntilIntersect(float theta, float3 x, float3 y) { +float3 +hsluv_lengthOfRayUntilIntersect(float theta, float3 x, float3 y) { float3 len = divide(y, sin(theta) - x * cos(theta)); len = lerp(len, (float3)1000.0, step(len, (float3)0.0)); return len; } -float hsluv_maxSafeChromaForL(float L) { +float +hsluv_maxSafeChromaForL(float L) { // Transposed from GLSL column-major constructor to Slang row-major layout float3x3 m2 = float3x3( - 3.2409699419045214, -1.5373831775700935, -0.49861076029300328, - -0.96924363628087983, 1.8759675015077207, 0.041555057407175613, - 0.055630079696993609,-0.20397695888897657, 1.0569715142428786 - ); + 3.2409699419045214, + -1.5373831775700935, + -0.49861076029300328, + -0.96924363628087983, + 1.8759675015077207, + 0.041555057407175613, + 0.055630079696993609, + -0.20397695888897657, + 1.0569715142428786); float sub0 = L + 16.0; float sub1 = sub0 * sub0 * sub0 * 0.000000641; float sub2 = lerp(L / 903.2962962962963, sub1, step(0.0088564516790356308, sub1)); - float3 top1 = (284517.0 * m2[0] - 94839.0 * m2[2]) * sub2; + float3 top1 = (284517.0 * m2[0] - 94839.0 * m2[2]) * sub2; float3 bottom = (632260.0 * m2[2] - 126452.0 * m2[1]) * sub2; - float3 top2 = (838422.0 * m2[2] + 769860.0 * m2[1] + 731718.0 * m2[0]) * L * sub2; + float3 top2 = (838422.0 * m2[2] + 769860.0 * m2[1] + 731718.0 * m2[0]) * L * sub2; float3 bounds0x = top1 / bottom; float3 bounds0y = top2 / bottom; - float3 bounds1x = top1 / (bottom + 126452.0); + float3 bounds1x = top1 / (bottom + 126452.0); float3 bounds1y = (top2 - 769860.0 * L) / (bottom + 126452.0); float3 xs0 = hsluv_intersectLineLine(bounds0x, bounds0y, -1.0 / bounds0x, (float3)0.0); @@ -55,87 +66,100 @@ float hsluv_maxSafeChromaForL(float L) { float3 lengths0 = hsluv_distanceFromPole(xs0, bounds0y + xs0 * bounds0x); float3 lengths1 = hsluv_distanceFromPole(xs1, bounds1y + xs1 * bounds1x); - return min(lengths0.x, - min(lengths1.x, - min(lengths0.y, - min(lengths1.y, - min(lengths0.z, - lengths1.z))))); + return min(lengths0.x, min(lengths1.x, min(lengths0.y, min(lengths1.y, min(lengths0.z, lengths1.z))))); } -float hsluv_maxChromaForLH(float L, float H) { +float +hsluv_maxChromaForLH(float L, float H) { float hrad = radians(H); // Transposed from GLSL column-major constructor to Slang row-major layout float3x3 m2 = float3x3( - 3.2409699419045214, -1.5373831775700935, -0.49861076029300328, - -0.96924363628087983, 1.8759675015077207, 0.041555057407175613, - 0.055630079696993609,-0.20397695888897657, 1.0569715142428786 - ); + 3.2409699419045214, + -1.5373831775700935, + -0.49861076029300328, + -0.96924363628087983, + 1.8759675015077207, + 0.041555057407175613, + 0.055630079696993609, + -0.20397695888897657, + 1.0569715142428786); float sub1 = pow(L + 16.0, 3.0) / 1560896.0; float sub2 = lerp(L / 903.2962962962963, sub1, step(0.0088564516790356308, sub1)); - float3 top1 = (284517.0 * m2[0] - 94839.0 * m2[2]) * sub2; + float3 top1 = (284517.0 * m2[0] - 94839.0 * m2[2]) * sub2; float3 bottom = (632260.0 * m2[2] - 126452.0 * m2[1]) * sub2; - float3 top2 = (838422.0 * m2[2] + 769860.0 * m2[1] + 731718.0 * m2[0]) * L * sub2; + float3 top2 = (838422.0 * m2[2] + 769860.0 * m2[1] + 731718.0 * m2[0]) * L * sub2; float3 bound0x = top1 / bottom; float3 bound0y = top2 / bottom; - float3 bound1x = top1 / (bottom + 126452.0); + float3 bound1x = top1 / (bottom + 126452.0); float3 bound1y = (top2 - 769860.0 * L) / (bottom + 126452.0); float3 lengths0 = hsluv_lengthOfRayUntilIntersect(hrad, bound0x, bound0y); float3 lengths1 = hsluv_lengthOfRayUntilIntersect(hrad, bound1x, bound1y); - return min(lengths0.x, - min(lengths1.x, - min(lengths0.y, - min(lengths1.y, - min(lengths0.z, - lengths1.z))))); + return min(lengths0.x, min(lengths1.x, min(lengths0.y, min(lengths1.y, min(lengths0.z, lengths1.z))))); } -float3 hsluv_fromLinear(float3 c) { +float3 +hsluv_fromLinear(float3 c) { return lerp(c * 12.92, 1.055 * pow(max(c, (float3)0), (float3)(1.0 / 2.4)) - 0.055, step(0.0031308, c)); } -float3 hsluv_toLinear(float3 c) { +float3 +hsluv_toLinear(float3 c) { return lerp(c / 12.92, pow(max((c + 0.055) / (1.0 + 0.055), (float3)0), (float3)2.4), step(0.04045, c)); } -float hsluv_yToL(float Y) { +float +hsluv_yToL(float Y) { return lerp(Y * 903.2962962962963, 116.0 * pow(max(Y, 0), 1.0 / 3.0) - 16.0, step(0.0088564516790356308, Y)); } -float hsluv_lToY(float L) { +float +hsluv_lToY(float L) { return lerp(L / 903.2962962962963, pow((max(L, 0) + 16.0) / 116.0, 3.0), step(8.0, L)); } -float3 xyzToRgb(float3 tuple) { +float3 +xyzToRgb(float3 tuple) { // Transposed layout from GLSL column-major matrix construction const float3x3 m = float3x3( - 3.2409699419045214, -0.96924363628087983, 0.055630079696993609, - -1.5373831775700935, 1.8759675015077207, -0.20397695888897657, - -0.49861076029300328, 0.041555057407175613, 1.0569715142428786 - ); + 3.2409699419045214, + -0.96924363628087983, + 0.055630079696993609, + -1.5373831775700935, + 1.8759675015077207, + -0.20397695888897657, + -0.49861076029300328, + 0.041555057407175613, + 1.0569715142428786); // GLSL `tuple * m` is transformed to Slang/HLSL `mul(m, tuple)` return hsluv_fromLinear(mul(m, tuple)); } -float3 rgbToXyz(float3 tuple) { +float3 +rgbToXyz(float3 tuple) { // Transposed layout from GLSL column-major matrix construction const float3x3 m = float3x3( - 0.41239079926595948, 0.21263900587151036, 0.019330818715591851, - 0.35758433938387796, 0.71516867876775593, 0.11919477979462599, - 0.18048078840183429, 0.072192315360733715, 0.95053215224966058 - ); + 0.41239079926595948, + 0.21263900587151036, + 0.019330818715591851, + 0.35758433938387796, + 0.71516867876775593, + 0.11919477979462599, + 0.18048078840183429, + 0.072192315360733715, + 0.95053215224966058); // GLSL `tuple * m` is transformed to Slang/HLSL `mul(m, tuple)` return mul(m, hsluv_toLinear(tuple)); } -float3 xyzToLuv(float3 tuple) { +float3 +xyzToLuv(float3 tuple) { float X = tuple.x; float Y = tuple.y; float Z = tuple.z; @@ -143,14 +167,11 @@ float3 xyzToLuv(float3 tuple) { float L = hsluv_yToL(Y); float div = 1.0 / max(dot(tuple, float3(1, 15, 3)), 1e-15); - return float3( - 1.0, - (52.0 * (X * div) - 2.57179), - (117.0 * (Y * div) - 6.08816) - ) * L; + return float3(1.0, (52.0 * (X * div) - 2.57179), (117.0 * (Y * div) - 6.08816)) * L; } -float3 luvToXyz(float3 tuple) { +float3 +luvToXyz(float3 tuple) { float L = tuple.x; float U = divide(tuple.y, 13.0 * L) + 0.19783000664283681; @@ -163,7 +184,8 @@ float3 luvToXyz(float3 tuple) { return float3(X, Y, Z); } -float3 luvToLch(float3 tuple) { +float3 +luvToLch(float3 tuple) { float L = tuple.x; float U = tuple.y; float V = tuple.z; @@ -175,42 +197,45 @@ float3 luvToLch(float3 tuple) { return float3(L, C, H); } -float3 lchToLuv(float3 tuple) { +float3 +lchToLuv(float3 tuple) { float hrad = radians(tuple.z); - return float3( - tuple.x, - cos(hrad) * tuple.y, - sin(hrad) * tuple.y - ); + return float3(tuple.x, cos(hrad) * tuple.y, sin(hrad) * tuple.y); } -float3 hsluvToLch(float3 tuple) { +float3 +hsluvToLch(float3 tuple) { tuple.y *= hsluv_maxChromaForLH(tuple.z, tuple.x) * 0.01; return tuple.zyx; } -float3 lchToHsluv(float3 tuple) { +float3 +lchToHsluv(float3 tuple) { tuple.y = divide(tuple.y, hsluv_maxChromaForLH(tuple.x, tuple.z) * 0.01); return tuple.zyx; } -float3 lchToRgb(float3 tuple) { +float3 +lchToRgb(float3 tuple) { return xyzToRgb(luvToXyz(lchToLuv(tuple))); } -float3 rgbToLch(float3 tuple) { +float3 +rgbToLch(float3 tuple) { return luvToLch(xyzToLuv(rgbToXyz(tuple))); } -public float3 hsluvToRgb(float3 tuple) { +public float3 +hsluvToRgb(float3 tuple) { return lchToRgb(hsluvToLch(tuple)); } -public float3 rgbToHsluv(float3 tuple) { +public float3 +rgbToHsluv(float3 tuple) { return lchToHsluv(rgbToLch(tuple)); } -float3 luvToRgb(float3 tuple) { +float3 +luvToRgb(float3 tuple) { return xyzToRgb(luvToXyz(tuple)); } - diff --git a/kitty/shaders/linear2srgb.slang b/kitty/shaders/linear2srgb.slang index b178f3fe6..8c9b94f54 100644 --- a/kitty/shaders/linear2srgb.slang +++ b/kitty/shaders/linear2srgb.slang @@ -5,7 +5,8 @@ module linear2srgb; // Scalar sRGB to Linear conversion -public float srgb2linear(float x) { +public float +srgb2linear(float x) { float lower = x / 12.92; float upper = pow((x + 0.055f) / 1.055f, 2.4f); @@ -14,7 +15,8 @@ public float srgb2linear(float x) { } // Scalar Linear to sRGB conversion -public float linear2srgb(float x) { +public float +linear2srgb(float x) { float lower = 12.92 * x; float upper = 1.055 * pow(x, 1.0f / 2.4f) - 0.055f; @@ -22,7 +24,8 @@ public float linear2srgb(float x) { } // Vector Linear to sRGB conversion -public float3 linear2srgb(float3 x) { +public float3 +linear2srgb(float3 x) { float3 lower = 12.92 * x; float3 upper = 1.055 * pow(x, float3(1.0f / 2.4f)) - 0.055f; @@ -30,9 +33,9 @@ public float3 linear2srgb(float3 x) { } // Vector sRGB to Linear conversion -public float3 srgb2linear(float3 c) { +public float3 +srgb2linear(float3 c) { // You can call the scalar version per-component, // or pass the whole vector if you overload it for float3. return float3(srgb2linear(c.r), srgb2linear(c.g), srgb2linear(c.b)); } - diff --git a/kitty/shaders/padding.slang b/kitty/shaders/padding.slang index 3a9670b8d..e273c6409 100644 --- a/kitty/shaders/padding.slang +++ b/kitty/shaders/padding.slang @@ -23,10 +23,10 @@ import background; // Corner indicator per quad vertex, matching cell_pos_map in background.slang. // .x: 0 -> left, 1 -> right ; .y: 0 -> top, 1 -> bottom static const uint2 pad_pos_map[4] = { - uint2(1u, 0u), // right, top - uint2(1u, 1u), // right, bottom - uint2(0u, 1u), // left, bottom - uint2(0u, 0u) // left, top + uint2(1u, 0u), // right, top + uint2(1u, 1u), // right, bottom + uint2(0u, 1u), // left, bottom + uint2(0u, 0u) // left, top }; struct VertexOutput { @@ -35,12 +35,13 @@ struct VertexOutput { }; [shader("vertex")] -VertexOutput vertex_main( +VertexOutput +vertex_main( [[vk::location(0)]] uint3 colors, [[vk::location(1)]] uint2 sprite_idx, - [[vk::location(2)]] uint is_selected, - uint vertex_id : SV_VertexID, - uint instance_id : SV_InstanceID, + [[vk::location(2)]] uint is_selected, + uint vertex_id: SV_VertexID, + uint instance_id: SV_InstanceID, // The true cell index (into the cell grid) for strip 0's first instance. uniform uint base_instance, uniform uint instance_step, @@ -62,18 +63,17 @@ VertexOutput vertex_main( // Strip-1 parameters. Equal to strip-0 values for single-strip draws so // the lerp below is always correct regardless of strip count. uniform uint base_instance2, - uniform float2 across2, -) { + uniform float2 across2, ) { VertexOutput vo; // Branchless strip selector: 0.0 for strip 0, 1.0 for strip 1. float strip_f = float(instance_id / along_count); - uint strip_i = instance_id % along_count; + uint strip_i = instance_id % along_count; // Select the base cell and across extents for whichever strip this // instance belongs to, using lerp so no divergent branch is emitted. - float fbase = lerp(float(base_instance), float(base_instance2), strip_f); - uint real_id = uint(fbase) + strip_i * instance_step; + float fbase = lerp(float(base_instance), float(base_instance2), strip_f); + uint real_id = uint(fbase) + strip_i * instance_step; float2 chosen_across = lerp(across, across2, strip_f); vo.color_premul = padding_background_premul(colors, sprite_idx, is_selected, real_id); @@ -89,7 +89,7 @@ VertexOutput vertex_main( float a = lerp(float(p.x), float(p.y), h); // 1 when this is the first/last instance within its strip, else 0. float is_first = 1.0 - min(float(strip_i), 1.0); - float is_last = 1.0 - min(float(along_count - 1u - strip_i), 1.0); + float is_last = 1.0 - min(float(along_count - 1u - strip_i), 1.0); float along = lerp(along_lo, along_hi, s); // Extend the first cell's near corner and the last cell's far corner out to @@ -104,6 +104,8 @@ VertexOutput vertex_main( } [shader("fragment")] -float4 fragment_main(float4 color_premul : COLOR_PREMUL) : SV_Target { +float4 +fragment_main(float4 color_premul: COLOR_PREMUL) + : SV_Target { return color_premul; } diff --git a/kitty/shaders/rounded_rect.slang b/kitty/shaders/rounded_rect.slang index cb881ddba..84478c689 100644 --- a/kitty/shaders/rounded_rect.slang +++ b/kitty/shaders/rounded_rect.slang @@ -9,23 +9,21 @@ import alpha_blend; #define right 2 #define bottom 3 -static const int2 vertex_pos_map[4] = { - int2(right, top), - int2(right, bottom), - int2(left, bottom), - int2(left, top) -}; +static const int2 vertex_pos_map[4] = { int2(right, top), int2(right, bottom), int2(left, bottom), int2(left, top) }; static const float4 dest_rect = float4(-1, 1, 1, -1); [shader("vertex")] -float4 vertex_main(uint vertex_id : SV_VertexID) : SV_Position { +float4 +vertex_main(uint vertex_id: SV_VertexID) + : SV_Position { int2 pos = vertex_pos_map[vertex_id]; return float4(dest_rect[pos.x], dest_rect[pos.y], 0, 1); } // Signed distance function for a rounded rectangle -float rounded_rectangle_sdf(float2 p, float2 b, float r) { +float +rounded_rectangle_sdf(float2 p, float2 b, float r) { // signed distance field // first term is used for points outside the rectangle float2 q = abs(p) - b; @@ -33,13 +31,15 @@ float rounded_rectangle_sdf(float2 p, float2 b, float r) { } [shader("fragment")] -float4 fragment_main( +float4 +fragment_main( uniform float4 rect, uniform float2 params, uniform float4 color, uniform float4 background_color, - float4 frag_coord : SV_Position // SV_Position provides gl_FragCoord equivalent in Slang -) : SV_Target { + float4 frag_coord: SV_Position // SV_Position provides gl_FragCoord equivalent in Slang + ) + : SV_Target { float2 size = rect.zw; // GLSL .ba maps to .zw or .ba in Slang (using .zw is typical) float2 origin = rect.xy; float thickness = params[0]; diff --git a/kitty/shaders/screenshot.slang b/kitty/shaders/screenshot.slang index 340b24c1e..289c6b8e7 100644 --- a/kitty/shaders/screenshot.slang +++ b/kitty/shaders/screenshot.slang @@ -12,18 +12,16 @@ struct VSOutput { [shader("vertex")] -VSOutput vertex_main( - uint vertex_id : SV_VertexID, - uniform float4 src_rect, - uniform float4 dest_rect, -) { +VSOutput +vertex_main(uint vertex_id: SV_VertexID, uniform float4 src_rect, uniform float4 dest_rect, ) { BlitOutput ans = get_coords_for_blit(vertex_id, src_rect, dest_rect); - return {ans.texcoord, float4(ans.position[0], ans.position[1], 0.0, 1.0)}; + return { ans.texcoord, float4(ans.position[0], ans.position[1], 0.0, 1.0) }; } uniform Sampler2D image; -float3 safe_unpremult_to_linear(float4 s) { +float3 +safe_unpremult_to_linear(float4 s) { // Avoid division by zero by replacing 0.0 alpha with 1.0. // If alpha is 0.0, the division is safe, and lerp masks the result to 0.0 anyway. float safe_alpha = lerp(1.0f, s.a, step(0.00001f, s.a)); @@ -33,34 +31,37 @@ float3 safe_unpremult_to_linear(float4 s) { } [shader("fragment")] -float4 fragment_main(float2 texcoord : TEXCOORD, uniform float2 src_size) : SV_Target { +float4 +fragment_main(float2 texcoord: TEXCOORD, uniform float2 src_size) + : SV_Target { float4 s00, s10, s01, s11; float2 texel_size = 1.0 / src_size; // Use Slang target switches to dynamically compile for specific backend features - __target_switch - { - // Modern backends with full texture gathering capabilities - case spirv: case hlsl: case metal: { - float2 gather_coord = texcoord - (0.5 * texel_size); + __target_switch { + // Modern backends with full texture gathering capabilities + case spirv: + case hlsl: + case metal: { + float2 gather_coord = texcoord - (0.5 * texel_size); - float4 r_gather = image.GatherRed(gather_coord); - float4 g_gather = image.GatherGreen(gather_coord); - float4 b_gather = image.GatherBlue(gather_coord); - float4 a_gather = image.GatherAlpha(gather_coord); + float4 r_gather = image.GatherRed(gather_coord); + float4 g_gather = image.GatherGreen(gather_coord); + float4 b_gather = image.GatherBlue(gather_coord); + float4 a_gather = image.GatherAlpha(gather_coord); - s00 = float4(r_gather.w, g_gather.w, b_gather.w, a_gather.w); // Bottom-Left - s10 = float4(r_gather.z, g_gather.z, b_gather.z, a_gather.z); // Bottom-Right - s01 = float4(r_gather.x, g_gather.x, b_gather.x, a_gather.x); // Top-Left - s11 = float4(r_gather.y, g_gather.y, b_gather.y, a_gather.y); // Top-Right - } - // Fallback for older targets or legacy GLSL versions - default: { - s00 = image.Sample(texcoord + float2(-0.25, -0.25) * texel_size); - s10 = image.Sample(texcoord + float2( 0.25, -0.25) * texel_size); - s01 = image.Sample(texcoord + float2(-0.25, 0.25) * texel_size); - s11 = image.Sample(texcoord + float2( 0.25, 0.25) * texel_size); - } + s00 = float4(r_gather.w, g_gather.w, b_gather.w, a_gather.w); // Bottom-Left + s10 = float4(r_gather.z, g_gather.z, b_gather.z, a_gather.z); // Bottom-Right + s01 = float4(r_gather.x, g_gather.x, b_gather.x, a_gather.x); // Top-Left + s11 = float4(r_gather.y, g_gather.y, b_gather.y, a_gather.y); // Top-Right + } + // Fallback for older targets or legacy GLSL versions + default: { + s00 = image.Sample(texcoord + float2(-0.25, -0.25) * texel_size); + s10 = image.Sample(texcoord + float2(0.25, -0.25) * texel_size); + s01 = image.Sample(texcoord + float2(-0.25, 0.25) * texel_size); + s11 = image.Sample(texcoord + float2(0.25, 0.25) * texel_size); + } } // Unpremultiply and convert to linear for each sample diff --git a/kitty/shaders/slang.py b/kitty/shaders/slang.py index 37291af0d..39deee049 100644 --- a/kitty/shaders/slang.py +++ b/kitty/shaders/slang.py @@ -406,8 +406,8 @@ def parse_slang_text(src_code: str, path: str = '') -> SlangFile: entry_points.append(EntryPoint(Stage.vertex, name)) case 'fragment' | 'pixel': entry_points.append(EntryPoint(Stage.fragment, name)) + found_entry_point = '' break - found_entry_point = '' else: match words[0]: case 'module': diff --git a/kitty/shaders/tint.slang b/kitty/shaders/tint.slang index c8005d39a..1893c1a74 100644 --- a/kitty/shaders/tint.slang +++ b/kitty/shaders/tint.slang @@ -4,29 +4,28 @@ // Main Vertex Shader Entry Point [shader("vertex")] -float4 vertex_main( - uint vertex_id : SV_VertexID, +float4 +vertex_main( + uint vertex_id: SV_VertexID, uniform float4 edges, // [ left, top, right, bottom ] -) : SV_Position { + ) + : SV_Position { // Extract boundaries from the edges vector - float left = edges[0]; - float top = edges[1]; - float right = edges[2]; + float left = edges[0]; + float top = edges[1]; + float right = edges[2]; float bottom = edges[3]; // Static mapping table for vertex positions - const float2 pos_map[4] = { - float2(left, top), - float2(left, bottom), - float2(right, bottom), - float2(right, top) - }; + const float2 pos_map[4] = { float2(left, top), float2(left, bottom), float2(right, bottom), float2(right, top) }; // Calculate final position return float4(pos_map[vertex_id], 0.0, 1.0); } [shader("fragment")] -float4 fragment_main(uniform float4 tint_color) : SV_Target { +float4 +fragment_main(uniform float4 tint_color) + : SV_Target { return tint_color; } diff --git a/kitty/shaders/trail.slang b/kitty/shaders/trail.slang index e55c30e06..d47a0dea0 100644 --- a/kitty/shaders/trail.slang +++ b/kitty/shaders/trail.slang @@ -2,15 +2,15 @@ // Copyright (C) 2026 Kovid Goyal // Distributed under terms of the GPLv3 license. -struct VertexOutput -{ +struct VertexOutput { float2 frag_pos : TEXCOORD; float4 position : SV_Position; }; // Main Vertex Shader Entry Point [shader("vertex")] -VertexOutput vertex_main(uint vertex_id : SV_VertexID, uniform float4 x_coords, uniform float4 y_coords) { +VertexOutput +vertex_main(uint vertex_id: SV_VertexID, uniform float4 x_coords, uniform float4 y_coords) { VertexOutput output; float2 pos = float2(x_coords[vertex_id], y_coords[vertex_id]); output.position = float4(pos, 1.0, 1.0); @@ -21,13 +21,9 @@ VertexOutput vertex_main(uint vertex_id : SV_VertexID, uniform float4 x_coords, // Main Fragment Shader Entry Point [shader("fragment")] -float4 fragment_main( - float2 frag_pos : TEXCOORD, - uniform float2 cursor_edge_x, - uniform float2 cursor_edge_y, - uniform float3 trail_color, - uniform float trail_opacity -) : SV_Target { +float4 +fragment_main(float2 frag_pos: TEXCOORD, uniform float2 cursor_edge_x, uniform float2 cursor_edge_y, uniform float3 trail_color, uniform float trail_opacity) + : SV_Target { float opacity = trail_opacity; // Evaluate if the fragment falls inside the bounding box of the cursor float in_x = step(cursor_edge_x[0], frag_pos.x) * step(frag_pos.x, cursor_edge_x[1]); diff --git a/kitty/shaders/utils.slang b/kitty/shaders/utils.slang index 0d2b03e9f..292d9e7a4 100644 --- a/kitty/shaders/utils.slang +++ b/kitty/shaders/utils.slang @@ -5,30 +5,31 @@ module utils; // Return 0 if x < 1 otherwise 1 -public __generic -vector zero_or_one(vector x) { +public __generic vector +zero_or_one(vector x) { return step((vector)1.0f, x); } // condition must be zero or one. When 1 thenval is returned otherwise elseval -public __generic -vector if_one_then(vector condition, vector thenval, vector elseval) { +public __generic vector +if_one_then(vector condition, vector thenval, vector elseval) { return lerp(elseval, thenval, condition); } // a < b ? thenval : elseval -public __generic -vector if_less_than(vector a, vector b, vector thenval, vector elseval) { +public __generic vector +if_less_than(vector a, vector b, vector thenval, vector elseval) { return lerp(thenval, elseval, step(b, a)); } // Replaces vec4(rgb * a, a) -public float4 vec4_premul(float3 rgb, float a) { +public float4 +vec4_premul(float3 rgb, float a) { return float4(rgb * a, a); } // Overloaded variation replacing vec4(rgba.rgb * rgba.a, rgba.a) -public float4 vec4_premul(float4 rgba) { +public float4 +vec4_premul(float4 rgba) { return float4(rgba.rgb * rgba.a, rgba.a); } - diff --git a/kitty_tests/slang.py b/kitty_tests/slang.py index a3a0883ba..5d08875f9 100644 --- a/kitty_tests/slang.py +++ b/kitty_tests/slang.py @@ -152,6 +152,58 @@ void vsMain() {} SlangFile('', '', frozenset({'common'}), frozenset({EntryPoint(Stage.vertex, 'vsMain')}), 'myshader'), ) + # Autoformat style: return type on its own line before function name + check( + """ +[shader("vertex")] +VertexOutput +vertex_main(uint vertex_id : SV_VertexID) {} +""", + SlangFile('', '', frozenset(), frozenset({EntryPoint(Stage.vertex, 'vertex_main')})), + ) + + # Autoformat style: return type on own line, function name + open-paren on next, params on following lines + check( + """ +[shader("fragment")] +float4 +fragment_main( + float2 texcoord : TEXCOORD +) : SV_Target { return float4(0); } +""", + SlangFile('', '', frozenset(), frozenset({EntryPoint(Stage.fragment, 'fragment_main')})), + ) + + # Autoformat style: attribute lines between [shader] and split return-type/name declaration + check( + """ +[shader("fragment")] +[numthreads(1, 1, 1)] +float4 +psMain() : SV_Target { return float4(1, 0, 0, 1); } +""", + SlangFile('', '', frozenset(), frozenset({EntryPoint(Stage.fragment, 'psMain')})), + ) + + # Autoformat style: vertex and fragment both split across lines + check( + """ +[shader("vertex")] +VertexOutput +vsMain(uint id : SV_VertexID) {} + +[shader("fragment")] +float4 +fsMain(VertexOutput vo) : SV_Target { return float4(0); } +""", + SlangFile( + '', + '', + frozenset(), + frozenset({EntryPoint(Stage.vertex, 'vsMain'), EntryPoint(Stage.fragment, 'fsMain')}), + ), + ) + def test_slang_ordering(self): # Test topological_sort with a manually constructed linear chain: a <- b <- c graph: dict[str, SlangFile] = {