From 23d5c0b0ab67cb7befee089a72c5eccd2f9db75b Mon Sep 17 00:00:00 2001 From: Nat Quayle Nelson Date: Thu, 11 Jul 2024 16:44:45 -0600 Subject: [PATCH] god rays shader --- shader-test/source/GodRaysState.kiss | 31 ++++++++ shader-test/source/MenuState.kiss | 3 +- src/kiss_flixel/ShaderFrontend.hx | 2 +- src/kiss_flixel/shaders/GodRays.frag | 103 +++++++++++++++++++++++++++ 4 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 shader-test/source/GodRaysState.kiss create mode 100644 src/kiss_flixel/shaders/GodRays.frag diff --git a/shader-test/source/GodRaysState.kiss b/shader-test/source/GodRaysState.kiss new file mode 100644 index 0000000..8209fc2 --- /dev/null +++ b/shader-test/source/GodRaysState.kiss @@ -0,0 +1,31 @@ +(import flixel.FlxState) +(import flixel.FlxG) +(import flixel.FlxSprite) +(import flixel.util.FlxColor) +(import openfl.filters.BitmapFilter) +(import openfl.filters.ShaderFilter) +(import flixel.system.FlxAssets.FlxShader) +(import flixel.FlxCamera) + +(extends FlxState) + +(prop &mut shader null) +(prop &mut sprite null) +(prop &mut fgCamera null) + +(method &override :Void create [] + (super.create) + (set shader (new kiss_flixel.shaders.GodRays)) + (set sprite (new FlxSprite 0 0 "assets/images/flymanEdited.png")) + (add sprite) + + (set FlxG.camera.bgColor FlxColor.GRAY) + + (set fgCamera (new FlxCamera)) + (set fgCamera.bgColor FlxColor.TRANSPARENT) + (FlxG.cameras.add fgCamera false) + + (kiss_flixel.CameraTools.addShaderFilter fgCamera (array FlxShader shader))) + +(method &override :Void update [:Float elapsed] + (super.update elapsed)) \ No newline at end of file diff --git a/shader-test/source/MenuState.kiss b/shader-test/source/MenuState.kiss index c6291e8..58b4b49 100644 --- a/shader-test/source/MenuState.kiss +++ b/shader-test/source/MenuState.kiss @@ -20,6 +20,7 @@ InvertState MirroredOrnamentState ShadowState - TVStaticState) + TVStaticState + GodRaysState) ->choice (FlxG.switchState (Type.createInstance choice []))))) \ No newline at end of file diff --git a/src/kiss_flixel/ShaderFrontend.hx b/src/kiss_flixel/ShaderFrontend.hx index f40933c..d23e5e7 100644 --- a/src/kiss_flixel/ShaderFrontend.hx +++ b/src/kiss_flixel/ShaderFrontend.hx @@ -203,7 +203,7 @@ class ShaderFrontend implements FrontendPlugin { transformedCode += "iTime"; case Some("COLOR"): transformedCode += "gl_FragColor"; - case Some("TEXTURE"): + case Some("SCREEN_TEXTURE" | "TEXTURE"): transformedCode += "bitmap"; case Some("texture"): diff --git a/src/kiss_flixel/shaders/GodRays.frag b/src/kiss_flixel/shaders/GodRays.frag new file mode 100644 index 0000000..9a919ac --- /dev/null +++ b/src/kiss_flixel/shaders/GodRays.frag @@ -0,0 +1,103 @@ +#pragma header + +// Source: https://godotshaders.com/shader/god-rays/ +// by pend00 (CC0) +// Ported to kiss-flixel by NQNStudios + +uniform float angle = -0.3; +uniform float position = -0.2; +uniform float spread : hint_range(0.0, 1.0) = 0.5; +uniform float cutoff : hint_range(-1.0, 1.0) = 0.1; +uniform float falloff : hint_range(0.0, 1.0) = 0.2; +uniform float edge_fade : hint_range(0.0, 1.0) = 0.15; + +uniform float speed = 1.0; +uniform float ray1_density = 8.0; +uniform float ray2_density = 30.0; +uniform float ray2_intensity : hint_range(0.0, 1.0) = 0.3; + +uniform vec4 color : hint_color = vec4(1.0, 0.9, 0.65, 0.8); + +uniform bool hdr = false; +uniform float seed = 5.0; + +// Random and noise functions from Book of Shader's chapter on Noise. +float random(vec2 _uv) { + return fract(sin(dot(_uv.xy, + vec2(12.9898, 78.233))) * + 43758.5453123); +} + +float noise (in vec2 uv) { + vec2 i = floor(uv); + vec2 f = fract(uv); + + // Four corners in 2D of a tile + float a = random(i); + float b = random(i + vec2(1.0, 0.0)); + float c = random(i + vec2(0.0, 1.0)); + float d = random(i + vec2(1.0, 1.0)); + + + // Smooth Interpolation + + // Cubic Hermine Curve. Same as SmoothStep() + vec2 u = f * f * (3.0-2.0 * f); + + // Mix 4 coorners percentages + return mix(a, b, u.x) + + (c - a)* u.y * (1.0 - u.x) + + (d - b) * u.x * u.y; +} + +mat2 rotate(float _angle){ + return mat2(vec2(cos(_angle), -sin(_angle)), + vec2(sin(_angle), cos(_angle))); +} + +vec4 screen(vec4 base, vec4 blend){ + return 1.0 - (1.0 - base) * (1.0 - blend); +} + +void fragment() +{ + + // Rotate, skew and move the UVs + vec2 transformed_uv = ( rotate(angle) * (UV - position) ) / ( (UV.y + spread) - (UV.y * spread) ); + + // Animate the ray according the the new transformed UVs + vec2 ray1 = vec2(transformed_uv.x * ray1_density + sin(TIME * 0.1 * speed) * (ray1_density * 0.2) + seed, 1.0); + vec2 ray2 = vec2(transformed_uv.x * ray2_density + sin(TIME * 0.2 * speed) * (ray1_density * 0.2) + seed, 1.0); + + // Cut off the ray's edges + float cut = step(cutoff, transformed_uv.x) * step(cutoff, 1.0 - transformed_uv.x); + ray1 *= cut; + ray2 *= cut; + + // Apply the noise pattern (i.e. create the rays) + float rays; + + if (hdr){ + // This is not really HDR, but check this to not clamp the two merged rays making + // their values go over 1.0. Can make for some nice effect + rays = noise(ray1) + (noise(ray2) * ray2_intensity); + } + else{ + rays = clamp(noise(ray1) + (noise(ray2) * ray2_intensity), 0., 1.); + } + + // Fade out edges + rays *= smoothstep(0.0, falloff, (1.0 - UV.y)); // Bottom + rays *= smoothstep(0.0 + cutoff, edge_fade + cutoff, transformed_uv.x); // Left + rays *= smoothstep(0.0 + cutoff, edge_fade + cutoff, 1.0 - transformed_uv.x); // Right + + // Color to the rays + vec3 shine = vec3(rays) * color.rgb; + + // Try different blending modes for a nicer effect. "Screen" is included in the code, + // but take a look at https://godotshaders.com/snippet/blending-modes/ for more. + // With "Screen" blend mode: + shine = screen(texture(SCREEN_TEXTURE, SCREEN_UV), vec4(color)).rgb; + + COLOR = vec4(shine, rays * color.a); +} \ No newline at end of file