Skip to content

Commit

Permalink
Merge pull request #44 from gedehari/runtime-shader-parsing
Browse files Browse the repository at this point in the history
Runtime shader parsing support for modcharts
  • Loading branch information
Yoshubs authored Sep 25, 2021
2 parents 9f61be2 + c32caeb commit b42e63b
Show file tree
Hide file tree
Showing 7 changed files with 1,627 additions and 1 deletion.
1 change: 0 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
"export/**/*.hx": true
},
"[haxe]": {
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
"editor.codeActionsOnSave": {
"source.sortImports": true
Expand Down
86 changes: 86 additions & 0 deletions assets/shaders/vhs.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Based on a shader by FMS_Cat.
// https://www.shadertoy.com/view/XtBXDt
// Modified to support OpenFL.

#pragma header
#define PI 3.14159265

uniform float time;

vec3 tex2D(sampler2D _tex,vec2 _p)
{
vec3 col=texture(_tex,_p).xyz;
if(.5<abs(_p.x-.5)){
col=vec3(.1);
}
return col;
}

float hash(vec2 _v)
{
return fract(sin(dot(_v,vec2(89.44,19.36)))*22189.22);
}

float iHash(vec2 _v,vec2 _r)
{
float h00=hash(vec2(floor(_v*_r+vec2(0.,0.))/_r));
float h10=hash(vec2(floor(_v*_r+vec2(1.,0.))/_r));
float h01=hash(vec2(floor(_v*_r+vec2(0.,1.))/_r));
float h11=hash(vec2(floor(_v*_r+vec2(1.,1.))/_r));
vec2 ip=vec2(smoothstep(vec2(0.,0.),vec2(1.,1.),mod(_v*_r,1.)));
return(h00*(1.-ip.x)+h10*ip.x)*(1.-ip.y)+(h01*(1.-ip.x)+h11*ip.x)*ip.y;
}

float noise(vec2 _v)
{
float sum=0.;
for(int i=1;i<9;i++)
{
sum+=iHash(_v+vec2(i),vec2(2.*pow(2.,float(i))))/pow(2.,float(i));
}
return sum;
}

void main()
{
vec2 uv=openfl_TextureCoordv;
vec2 uvn=uv;
vec3 col=vec3(0.);

// tape wave
uvn.x+=(noise(vec2(uvn.y,time))-.5)*.005;
uvn.x+=(noise(vec2(uvn.y*100.,time*10.))-.5)*.01;

// tape crease
float tcPhase=clamp((sin(uvn.y*8.-time*PI*1.2)-.92)*noise(vec2(time)),0.,.01)*10.;
float tcNoise=max(noise(vec2(uvn.y*100.,time*10.))-.5,0.);
uvn.x=uvn.x-tcNoise*tcPhase;

// switching noise
float snPhase=smoothstep(.03,0.,uvn.y);
uvn.y+=snPhase*.3;
uvn.x+=snPhase*((noise(vec2(uv.y*100.,time*10.))-.5)*.2);

col=tex2D(bitmap,uvn);
col*=1.-tcPhase;
col=mix(
col,
col.yzx,
snPhase
);

// bloom
for(float x=-4.;x<2.5;x+=1.){
col.xyz+=vec3(
tex2D(bitmap,uvn+vec2(x-0.,0.)*7E-3).x,
tex2D(bitmap,uvn+vec2(x-2.,0.)*7E-3).y,
tex2D(bitmap,uvn+vec2(x-4.,0.)*7E-3).z
)*.1;
}
col*=.6;

// ac beat
col*=1.+clamp(noise(vec2(0.,uv.y+time*.2))*.6-.25,0.,.1);

gl_FragColor=vec4(col,1.);
}
106 changes: 106 additions & 0 deletions source/flixel/graphics/tile/FlxGraphicsShader.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package flixel.graphics.tile;

import openfl.display.ShaderParameter;
import sys.io.File;
#if FLX_DRAW_QUADS
import openfl.display.GraphicsShader;

class FlxGraphicsShader extends GraphicsShader
{
public var alpha:ShaderParameter<Float>;
public var colorMultiplier:ShaderParameter<Float>;
public var colorOffset:ShaderParameter<Float>;
public var hasTransform:ShaderParameter<Bool>;
public var hasColorTransform:ShaderParameter<Bool>;

public function new()
{
super(
// Vertex
"#pragma header
attribute float alpha;
attribute vec4 colorMultiplier;
attribute vec4 colorOffset;
uniform bool hasColorTransform;
void main(void)
{
#pragma body
openfl_Alphav = openfl_Alpha * alpha;
if (hasColorTransform)
{
openfl_ColorOffsetv = colorOffset / 255.0;
openfl_ColorMultiplierv = colorMultiplier;
}
}",
// Fragment
"#pragma header
void main(void)
{
gl_FragColor = flixel_texture2D(bitmap, openfl_TextureCoordv);
}", false);

glFragmentHeader += "uniform bool hasTransform;
uniform bool hasColorTransform;
vec4 flixel_texture2D(sampler2D bitmap, vec2 coord)
{
vec4 color = texture2D(bitmap, coord);
if (!hasTransform)
{
return color;
}
if (color.a == 0.0)
{
return vec4(0.0, 0.0, 0.0, 0.0);
}
if (!hasColorTransform)
{
return color * openfl_Alphav;
}
color = vec4(color.rgb / color.a, color.a);
mat4 colorMultiplier = mat4(0);
colorMultiplier[0][0] = openfl_ColorMultiplierv.x;
colorMultiplier[1][1] = openfl_ColorMultiplierv.y;
colorMultiplier[2][2] = openfl_ColorMultiplierv.z;
colorMultiplier[3][3] = openfl_ColorMultiplierv.w;
color = clamp(openfl_ColorOffsetv + (color * colorMultiplier), 0.0, 1.0);
if (color.a > 0.0)
{
return vec4(color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav);
}
return vec4(0.0, 0.0, 0.0, 0.0);
}";

__initGL();

bitmap = data.bitmap;
alpha = data.alpha;
colorMultiplier = data.colorMultiplier;
colorOffset = data.colorOffset;
hasTransform = data.hasTransform;
hasColorTransform = data.hasColorTransform;
}

/* override function __initGL()
{
super.__initGL();
alpha = new ShaderParameter<Float>();
colorMultiplier = new ShaderParameter<Float>();
colorOffset = new ShaderParameter<Float>();
hasTransform = new ShaderParameter<Bool>();
hasColorTransform = new ShaderParameter<Bool>();
} */
}
#end
34 changes: 34 additions & 0 deletions source/gameFolder/meta/state/PlayState.hx
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,12 @@ import gameFolder.meta.data.Song.SwagSong;
import gameFolder.meta.state.charting.*;
import gameFolder.meta.state.menus.*;
import gameFolder.meta.subState.*;
import openfl.display.GraphicsShader;
import openfl.events.KeyboardEvent;
import openfl.filters.ShaderFilter;
import openfl.media.Sound;
import openfl.utils.Assets;
import sys.io.File;

using StringTools;

Expand Down Expand Up @@ -317,6 +320,37 @@ class PlayState extends MusicBeatState
songIntroCutscene();
else
startCountdown();

/**
* SHADERS
*
* This is a highly experimental code by gedehari to support runtime shader parsing.
* Usually, to add a shader, you would make it a class, but now, I modified it so
* you can parse it from a file.
*
* This feature is planned to be used for modcharts
* (at this time of writing, it's not available yet).
*
* This example below shows that you can apply shaders as a FlxCamera filter.
* the GraphicsShader class accepts two arguments, one is for vertex shader, and
* the second is for fragment shader.
* Pass in an empty string to use the default vertex/fragment shader.
*
* Next, the Shader is passed to a new instance of ShaderFilter, neccesary to make
* the filter work. And that's it!
*
* To access shader uniforms, just reference the `data` property of the GraphicsShader
* instance.
*
* Thank you for reading! -gedehari
*/

// Uncomment the code below to apply the effect

/*
var shader:GraphicsShader = new GraphicsShader("", File.getContent("./assets/shaders/vhs.frag"));
FlxG.camera.setFilters([new ShaderFilter(shader)]);
*/
}

var staticDisplace:Int = 0;
Expand Down
134 changes: 134 additions & 0 deletions source/openfl/display/GraphicsShader.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package openfl.display;

import openfl.utils.ByteArray;

#if !openfl_debug
@:fileXml('tags="haxe,release"')
@:noDebug
#end
class GraphicsShader extends Shader
{
public var bitmap:ShaderInput<BitmapData>;

var glVertexHeader:String = "attribute float openfl_Alpha;
attribute vec4 openfl_ColorMultiplier;
attribute vec4 openfl_ColorOffset;
attribute vec4 openfl_Position;
attribute vec2 openfl_TextureCoord;
varying float openfl_Alphav;
varying vec4 openfl_ColorMultiplierv;
varying vec4 openfl_ColorOffsetv;
varying vec2 openfl_TextureCoordv;
uniform mat4 openfl_Matrix;
uniform bool openfl_HasColorTransform;
uniform vec2 openfl_TextureSize;";

var glVertexBody:String = "openfl_Alphav = openfl_Alpha;
openfl_TextureCoordv = openfl_TextureCoord;
if (openfl_HasColorTransform) {
openfl_ColorMultiplierv = openfl_ColorMultiplier;
openfl_ColorOffsetv = openfl_ColorOffset / 255.0;
}
gl_Position = openfl_Matrix * openfl_Position;";

var glFragmentHeader:String = "varying float openfl_Alphav;
varying vec4 openfl_ColorMultiplierv;
varying vec4 openfl_ColorOffsetv;
varying vec2 openfl_TextureCoordv;
uniform bool openfl_HasColorTransform;
uniform vec2 openfl_TextureSize;
uniform sampler2D bitmap;";

var glFragmentBody:String = "vec4 color = texture2D (bitmap, openfl_TextureCoordv);
if (color.a == 0.0) {
gl_FragColor = vec4 (0.0, 0.0, 0.0, 0.0);
} else if (openfl_HasColorTransform) {
color = vec4 (color.rgb / color.a, color.a);
mat4 colorMultiplier = mat4 (0);
colorMultiplier[0][0] = openfl_ColorMultiplierv.x;
colorMultiplier[1][1] = openfl_ColorMultiplierv.y;
colorMultiplier[2][2] = openfl_ColorMultiplierv.z;
colorMultiplier[3][3] = 1.0; // openfl_ColorMultiplierv.w;
color = clamp (openfl_ColorOffsetv + (color * colorMultiplier), 0.0, 1.0);
if (color.a > 0.0) {
gl_FragColor = vec4 (color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav);
} else {
gl_FragColor = vec4 (0.0, 0.0, 0.0, 0.0);
}
} else {
gl_FragColor = color * openfl_Alphav;
}";

public function new(glVertexSource:String = "", glFragmentSource:String = "", initNow:Bool = true)
{
super(null);

if (glVertexSource != "")
this.glVertexSource = glVertexSource;
else
this.glVertexSource = "#pragma header
void main(void) {
#pragma body
}";

if (glFragmentSource != "")
this.glFragmentSource = glFragmentSource;
else
this.glFragmentSource = "#pragma header
void main(void) {
#pragma body
}";

if (initNow)
__initGL();
}

override public function __initGL()
{
processSource();

__isGenerated = true;
super.__initGL();

bitmap = data.bitmap;
}

function processSource()
{
if (glVertexSource != null || glFragmentSource != null)
{
if (glFragmentSource != null && glFragmentHeader != null && glFragmentBody != null)
{
glFragmentSource = StringTools.replace(glFragmentSource, "#pragma header", glFragmentHeader);
glFragmentSource = StringTools.replace(glFragmentSource, "#pragma body", glFragmentBody);
}

if (glVertexSource != null && glVertexHeader != null && glVertexBody != null)
{
glVertexSource = StringTools.replace(glVertexSource, "#pragma header", glVertexHeader);
glVertexSource = StringTools.replace(glVertexSource, "#pragma body", glVertexBody);
}
}
}
}
Loading

0 comments on commit b42e63b

Please sign in to comment.