I tried to make a custom shader class like so:
class CustomShader extends Shader
{
public function new(fragshaderpath:String, vertshaderpath:String)
{
glVertexSource = Assets.getText(vertshaderpath);
glFragmentSource = Assets.getText(fragshaderpath);
super();
__isGenerated = false;
__initGL();
}
}
However, this cannot be directly applied on haxeflixel’s FlxSprites (using the flxsprite.shader property). A workaround I used for this was to create an FlxCamera, apply it to the sprite, and then apply this custom shader to that camera (using flxcamera.setFilters). I used it as shown:
class PlayState extends FlxState
{
var sampleSprite:FlxSprite;
var mycustomshader:CustomShader;
override public function create()
{
super.create();
// initializing a sprite
sampleSprite = new FlxSprite(50, 50);
sampleSprite.frames = FlxAtlasFrames.fromSparrow('path/to/spritesheet', 'path/to/xml');
sampleSprite.animation.addByPrefix("idle", 'example-prefix');
sampleSprite.animation.play("idle");
sampleSprite.setGraphicSize(200);
// initializing a shader
mycustomshader = new CustomShader("path/to/fragment-shader", "path/to/vertex-shader");
mycustomshader.data.uTime.value = [0.0];
// add an FlxCamera to sampleSprite
var sampleSpriteCam = new FlxCamera(Std.int(sampleSprite.x), Std.int(sampleSprite.y), Std.int(sampleSprite.width), Std.int(sampleSprite.height));
sampleSpriteCam.bgColor = FlxColor.TRANSPARENT;
sampleSpriteCam.follow(sampleSprite);
sampleSprite.offset.set();
sampleSprite.updateHitbox();
add(sampleSprite);
// adding the custom shader to the camera
sampleSprite.camera = sampleSpriteCam;
sampleSpriteCam.setFilters([new ShaderFilter(cast mycustomshader)]);
FlxG.cameras.add(sampleSpriteCam);
}
override public function update(elapsed:Float)
{
super.update(elapsed);
mycustomshader.data.uTime.value[0] += elapsed;
}
}
The vertex shader looked like this:
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;
attribute float alpha;
attribute vec4 colorMultiplier;
attribute vec4 colorOffset;
uniform bool hasColorTransform;
void main(void)
{
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;
openfl_Alphav = openfl_Alpha * alpha;
if (hasColorTransform)
{
openfl_ColorOffsetv = colorOffset / 255.0;
openfl_ColorMultiplierv = colorMultiplier;
}
}
And the fragment shader looked like this (this example fragment shader makes the sprite “blink”):
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;
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);
}
uniform float uTime;
void main(void)
{
vec4 color = flixel_texture2D(bitmap, fract((openfl_TextureCoordv+uTime*0.1)*uTime));
gl_FragColor = color;
}
I had to explicitly declare some uniforms and attributes that were used internally (such as openfl_Alphav), which would usually be done automatically at compile time, however I think some modifications to the CustomShader class could solve that.