├── .gitattributes ├── go.mod ├── antialiasing └── fxaa │ ├── README.md │ └── fxaa.kage ├── crt └── mattias │ ├── README.md │ └── crt.kage ├── LICENSE └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | *.kage linguist-language=go -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Zyko0/kage-shaders 2 | 3 | go 1.21.1 4 | -------------------------------------------------------------------------------- /antialiasing/fxaa/README.md: -------------------------------------------------------------------------------- 1 | # FXAA 3.11 2 | 3 | This is a single pass, screen-space, anti-aliasing shader originally implemented by NVIDIA. 4 | For more info: https://docs.nvidia.com/gameworks/content/gameworkslibrary/graphicssamples/d3d_samples/fxaa311sample.htm -------------------------------------------------------------------------------- /crt/mattias/README.md: -------------------------------------------------------------------------------- 1 | # CRT - By Mattias 2 | 3 | - Shadertoy link: https://www.shadertoy.com/view/Ms23DR 4 | - Libretro example: https://docs.libretro.com/shader/crt/#crt-mattias 5 | - https://github.com/quasilyte/roboden-game from quasilyte uses this implementation with a few modifications to suit the game, feel free to check it out! -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Zyko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kage shaders 2 | 3 | List of general-purpose shaders written in Kage for Ebitengine (https://github.com/hajimehoshi/ebiten) 4 | 5 | More info about Kage (Ebitengine shading language): https://ebitengine.org/en/documents/shader.html
6 | It starts with mine, mostly fullscreen and post-processing shaders, but anyone is welcome to open a PR 7 | 8 | For a gentle introduction to Kage (include tutorials and examples), please have a look at https://github.com/tinne26/kage-desk 9 | 10 | ## Notes 11 | 12 | - Shaders are all written based on Kage's pixel-mode (https://ebitengine.org/en/documents/shader.html#Unit_mode) 13 | 14 | ## Contribute 15 | 16 | Just open a PR! 17 | 18 | - If some shaders are defining constants for actual parameters (that could be defined as uniforms), feel free to make a suggestion! 19 | - Some features might be cut, some numbers might be tweaked wrongly based on what's visually expected when porting, so again feel free to contribute with fixes if something is not right! 20 | - Feel free to request for a glsl/hlsl translation to Kage as an issue, though I can't guarantee that I'll access to the request, but again² anyone can tackle these 21 | - Example applications? Why not! 22 | -------------------------------------------------------------------------------- /crt/mattias/crt.kage: -------------------------------------------------------------------------------- 1 | // A Kage port of https://www.shadertoy.com/view/Ms23DR 2 | // 3 | // The original license comment is: 4 | // Loosely based on postprocessing shader by inigo quilez, 5 | // License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. 6 | // https://creativecommons.org/licenses/by-nc-sa/3.0/deed.en 7 | 8 | package main 9 | 10 | //kage:unit pixels 11 | 12 | func curve(uv vec2) vec2 { 13 | uv = (uv - 0.5) * 2 14 | uv *= 1.1 15 | uv.x *= (1 + pow((abs(uv.y)/5), 2)) 16 | uv.y *= (1 + pow((abs(uv.x)/4), 2)) 17 | uv = uv/2 + 0.5 18 | uv = uv*0.92 + 0.04 19 | 20 | return uv 21 | } 22 | 23 | func Fragment(dst vec4, src vec2, color vec4) vec4 { 24 | origin, size := imageSrcRegionOnTexture() 25 | q := (src - origin) / size 26 | uv := q 27 | uv = curve(uv) 28 | 29 | var col vec3 30 | col.r = imageSrc0At(vec2(uv.x+0.001, uv.y+0.001)*size+origin).x + 0.05 31 | col.g = imageSrc0At(vec2(uv.x+0.000, uv.y-0.002)*size+origin).y + 0.05 32 | col.b = imageSrc0At(vec2(uv.x-0.002, uv.y+0.000)*size+origin).z + 0.05 33 | col.r += 0.08 * imageSrc0At((0.75*vec2(0.025, -0.027)+vec2(uv.x+0.001, uv.y+0.001))*size+origin).x 34 | col.g += 0.05 * imageSrc0At((0.75*vec2(-0.022, -0.02)+vec2(uv.x+0.000, uv.y-0.002))*size+origin).y 35 | col.b += 0.08 * imageSrc0At((0.75*vec2(-0.02, -0.018)+vec2(uv.x-0.002, uv.y+0.000))*size+origin).z 36 | 37 | col = clamp(col*0.6+0.4*col*col, 0, 1) 38 | 39 | vig := (16.0 * uv.x * uv.y * (1 - uv.x) * (1 - uv.y)) 40 | col *= vec3(pow(vig, 0.3)) 41 | col *= vec3(0.95, 1.05, 0.95) 42 | col *= 2.8 43 | 44 | scans := clamp(0.35+0.35*sin(uv.y*size.y*1.5), 0, 1) 45 | s := pow(scans, 1.7) 46 | col *= vec3(0.4 + 0.7*s) 47 | 48 | if uv.x < 0.0 || uv.x > 1.0 || uv.y < 0 || uv.y > 1 { 49 | col *= 0 50 | } 51 | 52 | col *= (1 - 0.65*vec3(clamp((mod(src.x, 2)-1)*2, 0, 1))) 53 | 54 | return vec4(col, 1) 55 | } 56 | -------------------------------------------------------------------------------- /antialiasing/fxaa/fxaa.kage: -------------------------------------------------------------------------------- 1 | // License: Subject to the terms of this Agreement, NVIDIA hereby grants to Developer a royalty-free, non-exclusive license to possess and to use the Materials. 2 | // The following terms apply to the specified type of Material: 3 | // Source Code: Developer shall have the right to modify and create derivative works with the Source Code. 4 | // Developer shall own any derivative works ("Derivatives") it creates to the Source Code, provided that Developer uses the Materials in accordance with the terms of this Agreement. 5 | // Developer may distribute the Derivatives, provided that all NVIDIA copyright notices and trademarks are used properly and the Derivatives include the following statement: 6 | // "This software contains source code provided by NVIDIA Corporation." 7 | 8 | //kage:unit pixels 9 | 10 | // Based on https://www.shadertoy.com/view/stlSzf 11 | // And therefore based on the tutorial: http://blog.simonrodriguez.fr/articles/2016/07/implementing_fxaa.html 12 | 13 | package main 14 | 15 | /* pixel index in 3*3 kernel 16 | +---+---+---+ 17 | | 0 | 1 | 2 | 18 | +---+---+---+ 19 | | 3 | 4 | 5 | 20 | +---+---+---+ 21 | | 6 | 7 | 8 | 22 | +---+---+---+ 23 | */ 24 | /* 25 | #define UP_LEFT 0 26 | #define UP 1 27 | #define UP_RIGHT 2 28 | #define LEFT 3 29 | #define CENTER 4 30 | #define RIGHT 5 31 | #define DOWN_LEFT 6 32 | #define DOWN 7 33 | #define DOWN_RIGHT 8 34 | */ 35 | 36 | const ( 37 | UP_LEFT = 0 38 | UP = 1 39 | UP_RIGHT = 2 40 | LEFT = 3 41 | CENTER = 4 42 | RIGHT = 5 43 | DOWN_LEFT = 6 44 | DOWN = 7 45 | DOWN_RIGHT = 8 46 | ) 47 | 48 | /* in order to accelerate exploring along tangent bidirectional, step by an increasing amount of pixels QUALITY(i) 49 | the max step count is 12 50 | +-----------------+---+---+---+---+---+---+---+---+---+---+---+---+ 51 | |step index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |10 |11 | 52 | +-----------------+---+---+---+---+---+---+---+---+---+---+---+---+ 53 | |step pixels count|1.0|1.0|1.0|1.0|1.0|1.5|2.0|2.0|2.0|2.0|4.0|8.0| 54 | +-----------------+---+---+---+---+---+---+---+---+---+---+---+---+ 55 | */ 56 | //#define STEP_COUNT_MAX 12 57 | const STEP_COUNT_MAX = 12 58 | 59 | func QUALITY(i int) float { 60 | if i < 5 { 61 | return 1.0 62 | } 63 | if i == 5 { 64 | return 1.5 65 | } 66 | if i < 10 { 67 | return 2.0 68 | } 69 | if i == 10 { 70 | return 4.0 71 | } 72 | if i == 11 { 73 | return 8.0 74 | } 75 | return 8.0 76 | } 77 | 78 | // L = 0.299 * R + 0.587 * G + 0.114 * B 79 | func RGB2LUMA(color vec3) float { 80 | return dot(vec3(0.299, 0.587, 0.114), color) 81 | } 82 | 83 | /* 84 | #define EDGE_THRESHOLD_MIN 0.0312 85 | #define EDGE_THRESHOLD_MAX 0.125 86 | #define SUBPIXEL_QUALITY 0.75 87 | #define GRADIENT_SCALE 0.25 88 | */ 89 | const ( 90 | EDGE_THRESHOLD_MIN = 0.0312 91 | EDGE_THRESHOLD_MAX = 0.125 92 | SUBPIXEL_QUALITY = 0.75 93 | GRADIENT_SCALE = 0.25 94 | ) 95 | 96 | func texture(uv vec2) vec4 { 97 | size := imageSrc0Size() 98 | uv.y = 1 - uv.y 99 | uv *= size 100 | p0 := uv - 1/2.0 101 | p1 := uv + 1/2.0 102 | c0 := imageSrc0UnsafeAt(p0) 103 | c1 := imageSrc0UnsafeAt(vec2(p1.x, p0.y)) 104 | c2 := imageSrc0UnsafeAt(vec2(p0.x, p1.y)) 105 | c3 := imageSrc0UnsafeAt(p1) 106 | rate := fract(p1) 107 | clr := mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y) 108 | return clr 109 | } 110 | 111 | func fxaa_3_11(uv, uv_step vec2) vec4 { 112 | KERNEL_STEP_MAT := [9]vec2{ 113 | vec2(-1.0, 1.0), vec2(0.0, 1.0), vec2(1.0, 1.0), 114 | vec2(-1.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 0.0), 115 | vec2(-1.0, -1.0), vec2(0.0, -1.0), vec2(1.0, -1.0), 116 | } 117 | // get luma of kernel 118 | luma_mat := [9]float{} 119 | for i := 0; i < 9; i++ { 120 | luma_mat[i] = RGB2LUMA(texture(uv + uv_step*KERNEL_STEP_MAT[i]).rgb) 121 | } 122 | 123 | // detecting where to apply FXAA, return the pixel color if not 124 | luma_min := min(luma_mat[CENTER], min(min(luma_mat[UP], luma_mat[DOWN]), min(luma_mat[LEFT], luma_mat[RIGHT]))) 125 | luma_max := max(luma_mat[CENTER], max(max(luma_mat[UP], luma_mat[DOWN]), max(luma_mat[LEFT], luma_mat[RIGHT]))) 126 | luma_range := luma_max - luma_min 127 | if luma_range < max(EDGE_THRESHOLD_MIN, luma_max*EDGE_THRESHOLD_MAX) { 128 | return texture(uv) 129 | } 130 | 131 | // choosing edge tangent 132 | // horizontal: |(upleft-left)-(left-downleft)|+2*|(up-center)-(center-down)|+|(upright-right)-(right-downright)| 133 | // vertical: |(upright-up)-(up-upleft)|+2*|(right-center)-(center-left)|+|(downright-down)-(down-downleft)| 134 | luma_horizontal := abs(luma_mat[UP_LEFT]+luma_mat[DOWN_LEFT]-2.0*luma_mat[LEFT]) + 135 | 2.0*abs(luma_mat[UP]+luma_mat[DOWN]-2.0*luma_mat[CENTER]) + 136 | abs(luma_mat[UP_RIGHT]+luma_mat[DOWN_RIGHT]-2.0*luma_mat[RIGHT]) 137 | luma_vertical := abs(luma_mat[UP_LEFT]+luma_mat[UP_RIGHT]-2.0*luma_mat[UP]) + 138 | 2.0*abs(luma_mat[LEFT]+luma_mat[RIGHT]-2.0*luma_mat[CENTER]) + 139 | abs(luma_mat[DOWN_LEFT]+luma_mat[DOWN_RIGHT]-2.0*luma_mat[DOWN]) 140 | is_horizontal := luma_horizontal > luma_vertical 141 | 142 | // choosing edge normal 143 | gradient_down_left := luma_mat[LEFT] 144 | gradient_up_right := luma_mat[RIGHT] 145 | if is_horizontal { 146 | gradient_down_left = luma_mat[DOWN] 147 | gradient_up_right = luma_mat[UP] 148 | } 149 | gradient_down_left -= luma_mat[CENTER] 150 | gradient_up_right -= luma_mat[CENTER] 151 | is_down_left := abs(gradient_down_left) > abs(gradient_up_right) 152 | 153 | // get the change rate of gradient in normal per pixel 154 | gradient := gradient_up_right 155 | // get the tangent uv step vector and the normal uv step vector 156 | step_normal := vec2(1.0) 157 | if is_down_left { 158 | gradient = gradient_down_left 159 | step_normal *= -1.0 160 | } 161 | step_tangent := vec2(0.0, 1.0) 162 | if is_horizontal { 163 | step_tangent = vec2(1.0, 0.0) 164 | step_normal *= vec2(0, 1) 165 | } else { 166 | step_normal *= vec2(1, 0) 167 | } 168 | step_normal *= uv_step 169 | step_tangent *= uv_step 170 | 171 | // start at middle point of tangent edge 172 | uv_start := uv + 0.5*step_normal 173 | luma_average_start := luma_mat[CENTER] + gradient*0.5 174 | 175 | // explore along tangent bidirectional until reach the edge both 176 | uv_pos := uv_start + step_tangent 177 | uv_neg := uv_start - step_tangent 178 | delta_luma_pos := RGB2LUMA(texture(uv_pos).rgb) - luma_average_start 179 | delta_luma_neg := RGB2LUMA(texture(uv_neg).rgb) - luma_average_start 180 | reached_pos := abs(delta_luma_pos) > (GRADIENT_SCALE * abs(gradient)) 181 | reached_neg := abs(delta_luma_neg) > (GRADIENT_SCALE * abs(gradient)) 182 | reached_both := reached_pos && reached_neg 183 | if !reached_pos { 184 | uv_pos += step_tangent 185 | } 186 | if !reached_neg { 187 | uv_neg -= step_tangent 188 | } 189 | if !reached_both { 190 | for i := 2; i < STEP_COUNT_MAX; i++ { 191 | if !reached_pos { 192 | delta_luma_pos = RGB2LUMA(texture(uv_pos).rgb) - luma_average_start 193 | } 194 | if !reached_neg { 195 | delta_luma_neg = RGB2LUMA(texture(uv_neg).rgb) - luma_average_start 196 | } 197 | 198 | reached_pos := abs(delta_luma_pos) > (GRADIENT_SCALE * abs(gradient)) 199 | reached_neg := abs(delta_luma_neg) > (GRADIENT_SCALE * abs(gradient)) 200 | reached_both := reached_pos && reached_neg 201 | 202 | if !reached_pos { 203 | uv_pos += (QUALITY(i) * step_tangent) 204 | } 205 | if !reached_neg { 206 | uv_neg -= (QUALITY(i) * step_tangent) 207 | } 208 | if reached_both { 209 | break 210 | } 211 | } 212 | } 213 | 214 | // estimating offset 215 | length_pos := max(abs(uv_pos-uv_start).x, abs(uv_pos-uv_start).y) 216 | length_neg := max(abs(uv_neg-uv_start).x, abs(uv_neg-uv_start).y) 217 | is_pos_near := length_pos < length_neg 218 | 219 | length_pixel_off := length_neg 220 | delta_luma := delta_luma_neg 221 | if is_pos_near { 222 | length_pixel_off = length_pos 223 | delta_luma = delta_luma_pos 224 | } 225 | pixel_offset := -1.*length_pixel_off/(length_pos+length_neg) + 0.5 226 | 227 | // no offset if the bidirectional point is too far 228 | if (delta_luma < 0) == (luma_mat[CENTER] < luma_average_start) { 229 | pixel_offset = 0 230 | } 231 | 232 | // subpixel antialiasing 233 | luma_average_center := 0.0 234 | average_weight_mat := [9]float{ 235 | 1.0, 2.0, 1.0, 236 | 2.0, 0.0, 2.0, 237 | 1.0, 2.0, 1.0, 238 | } 239 | for i := 0; i < 9; i++ { 240 | luma_average_center += (average_weight_mat[i] * luma_mat[i]) 241 | } 242 | luma_average_center /= 12.0 243 | 244 | subpixel_luma_range := clamp(abs(luma_average_center-luma_mat[CENTER])/luma_range, 0.0, 1.0) 245 | subpixel_offset := (-2.0*subpixel_luma_range + 3.0) * subpixel_luma_range * subpixel_luma_range 246 | subpixel_offset = subpixel_offset * subpixel_offset * SUBPIXEL_QUALITY 247 | 248 | // use the max offset between subpixel offset with before 249 | pixel_offset = max(pixel_offset, subpixel_offset) 250 | return texture(uv + pixel_offset*step_normal) 251 | } 252 | 253 | func Fragment(dst vec4, src vec2, color vec4) vec4 { 254 | origin := imageSrc0Origin() 255 | size := imageSrc0Size() 256 | uv_step := vec2(1.) / size 257 | uv := (src - origin) / size 258 | uv.y = 1 - uv.y 259 | 260 | return fxaa_3_11(uv, uv_step) 261 | } 262 | --------------------------------------------------------------------------------