Funkin/source/funkin/graphics/shaders/RuntimePostEffectShader.hx

136 lines
4 KiB
Haxe

package funkin.graphics.shaders;
import flixel.FlxCamera;
import flixel.FlxG;
import flixel.graphics.frames.FlxFrame;
import flixel.addons.display.FlxRuntimeShader;
import lime.graphics.opengl.GLProgram;
import lime.utils.Log;
class RuntimePostEffectShader extends FlxRuntimeShader
{
@:glVertexHeader('
// normalized screen coord
// (0, 0) is the top left of the window
// (1, 1) is the bottom right of the window
varying vec2 screenCoord;
', true)
@:glVertexBody('
screenCoord = vec2(
openfl_TextureCoord.x > 0.0 ? 1.0 : 0.0,
openfl_TextureCoord.y > 0.0 ? 1.0 : 0.0
);
')
@:glFragmentHeader('
// normalized screen coord
// (0, 0) is the top left of the window
// (1, 1) is the bottom right of the window
varying vec2 screenCoord;
// equals (FlxG.width, FlxG.height)
uniform vec2 uScreenResolution;
// equals (camera.viewLeft, camera.viewTop, camera.viewRight, camera.viewBottom)
uniform vec4 uCameraBounds;
// equals (frame.left, frame.top, frame.right, frame.bottom)
uniform vec4 uFrameBounds;
// screen coord -> world coord conversion
// returns world coord in px
vec2 screenToWorld(vec2 screenCoord) {
float left = uCameraBounds.x;
float top = uCameraBounds.y;
float right = uCameraBounds.z;
float bottom = uCameraBounds.w;
vec2 scale = vec2(right - left, bottom - top);
vec2 offset = vec2(left, top);
return screenCoord * scale + offset;
}
// world coord -> screen coord conversion
// returns normalized screen coord
vec2 worldToScreen(vec2 worldCoord) {
float left = uCameraBounds.x;
float top = uCameraBounds.y;
float right = uCameraBounds.z;
float bottom = uCameraBounds.w;
vec2 scale = vec2(right - left, bottom - top);
vec2 offset = vec2(left, top);
return (worldCoord - offset) / scale;
}
// screen coord -> frame coord conversion
// returns normalized frame coord
vec2 screenToFrame(vec2 screenCoord) {
float left = uFrameBounds.x;
float top = uFrameBounds.y;
float right = uFrameBounds.z;
float bottom = uFrameBounds.w;
float width = right - left;
float height = bottom - top;
float clampedX = clamp(screenCoord.x, left, right);
float clampedY = clamp(screenCoord.y, top, bottom);
return vec2(
(clampedX - left) / (width),
(clampedY - top) / (height)
);
}
// internally used to get the maximum `openfl_TextureCoordv`
vec2 bitmapCoordScale() {
return openfl_TextureCoordv / screenCoord;
}
// internally used to compute bitmap coord
vec2 screenToBitmap(vec2 screenCoord) {
return screenCoord * bitmapCoordScale();
}
// samples the frame buffer using a screen coord
vec4 sampleBitmapScreen(vec2 screenCoord) {
return texture2D(bitmap, screenToBitmap(screenCoord));
}
// samples the frame buffer using a world coord
vec4 sampleBitmapWorld(vec2 worldCoord) {
return sampleBitmapScreen(worldToScreen(worldCoord));
}
', true)
public function new(fragmentSource:String = null, glVersion:String = null)
{
super(fragmentSource, null, glVersion);
uScreenResolution.value = [FlxG.width, FlxG.height];
uCameraBounds.value = [0, 0, FlxG.width, FlxG.height];
uFrameBounds.value = [0, 0, FlxG.width, FlxG.height];
}
// basically `updateViewInfo(FlxG.width, FlxG.height, FlxG.camera)` is good
public function updateViewInfo(screenWidth:Float, screenHeight:Float, camera:FlxCamera):Void
{
uScreenResolution.value = [screenWidth, screenHeight];
uCameraBounds.value = [camera.viewLeft, camera.viewTop, camera.viewRight, camera.viewBottom];
}
public function updateFrameInfo(frame:FlxFrame)
{
// NOTE: uv.width is actually the right pos and uv.height is the bottom pos
uFrameBounds.value = [frame.uv.x, frame.uv.y, frame.uv.width, frame.uv.height];
}
override function __createGLProgram(vertexSource:String, fragmentSource:String):GLProgram
{
try
{
final res = super.__createGLProgram(vertexSource, fragmentSource);
return res;
}
catch (error)
{
Log.warn(error); // prevent the app from dying immediately
return null;
}
}
}