re-add libretro shader support, add default shaders and lua API

This commit is contained in:
Tim Felgentreff 2020-06-01 11:00:21 +02:00
parent d67a0319da
commit fee744aa1b
10 changed files with 1370 additions and 26 deletions

View file

@ -341,6 +341,7 @@ set(video_SRCS
src/video/png.cpp
src/video/sdl.cpp
src/video/video.cpp
src/video/shaders.cpp
)
source_group(video FILES ${video_SRCS})
@ -573,6 +574,7 @@ set(stratagus_generic_HDRS
src/include/vec2i.h
src/include/version.h
src/include/video.h
src/include/shaders.h
src/include/viewport.h
src/include/wav.h
src/include/widgets.h

20
src/include/shaders.h Normal file
View file

@ -0,0 +1,20 @@
#ifndef __SHADERS_H__
#define __SHADERS_H__
#include <SDL.h>
#ifndef __APPLE__
extern bool LoadShaderExtensions();
extern void RenderWithShader(SDL_Renderer *renderer, SDL_Window* win, SDL_Texture* backBuffer);
extern const char* NextShader();
#else
bool LoadShaderExtensions() {
}
void RenderWithShader(SDL_Renderer*, SDL_Window*, SDL_Texture*) {
}
int NextShader() {
return 0;
}
#endif
#endif

View file

@ -34,7 +34,7 @@
//@{
#include "SDL.h"
#include "shaders.h"
#include "guichan.h"
#include "color.h"

View file

@ -18,7 +18,6 @@ public:
CVideo Video;
void ToggleFullScreen(void);
void SwitchToShader(void);
class CGraphic
{

View file

@ -102,6 +102,8 @@ double FrameTicks; /// Frame length in ms
const EventCallback *Callbacks;
static bool CanUseShaders = false;
bool IsSDLWindowVisible = true;
/*----------------------------------------------------------------------------
@ -255,17 +257,11 @@ static void InitKey2Str()
*/
void InitVideoSdl()
{
Uint32 flags = 0;
Uint32 flags = SDL_WINDOW_ALLOW_HIGHDPI;
if (SDL_WasInit(SDL_INIT_VIDEO) == 0) {
//Wyrmgus start
//#ifndef USE_WIN32
//Wyrmgus end
// Fix tablet input in full-screen mode
SDL_setenv("SDL_MOUSE_RELATIVE", "0", 1);
//Wyrmgus start
//#endif
//Wyrmgus end
int res = SDL_Init(
SDL_INIT_AUDIO | SDL_INIT_VIDEO |
SDL_INIT_TIMER);
@ -333,6 +329,7 @@ void InitVideoSdl()
SDL_GetRendererInfo(TheRenderer, &rendererInfo);
if(!strncmp(rendererInfo.name, "opengl", 6)) {
puts("[Renderer] Got OpenGL");
CanUseShaders = LoadShaderExtensions();
}
SDL_SetRenderDrawColor(TheRenderer, 0, 0, 0, 255);
Video.ResizeScreen(Video.Width, Video.Height);
@ -555,10 +552,6 @@ static void SdlDoEvent(const EventCallback &callbacks, SDL_Event &event)
event.key.keysym.sym, event.key.keysym.sym < 128 ? event.key.keysym.sym : 0);
break;
case SDL_WINDOWEVENT_RESIZED:
Video.ResizeScreen(event.window.data1, event.window.data2);
break;
case SDL_QUIT:
Exit(0);
break;
@ -659,20 +652,24 @@ void RealizeVideoMemory()
if (NumRects) {
//SDL_UpdateWindowSurfaceRects(TheWindow, Rects, NumRects);
SDL_UpdateTexture(TheTexture, NULL, TheScreen->pixels, TheScreen->pitch);
SDL_RenderClear(TheRenderer);
//for (int i = 0; i < NumRects; i++)
// SDL_UpdateTexture(TheTexture, &Rects[i], TheScreen->pixels, TheScreen->pitch);
SDL_RenderCopy(TheRenderer, TheTexture, NULL, NULL);
if (EnableDebugPrint) {
// show a bar representing fps scaled by 10
SDL_SetRenderDrawColor(TheRenderer, 255, 0, 0, 255);
Uint32 nextTick = SDL_GetTicks();
double fps = 10000.0 / (nextTick - LastTick);
SDL_RenderDrawLine(TheRenderer, 0, 0, floorl(fps), 0);
SDL_SetRenderDrawColor(TheRenderer, 0, 0, 0, 255);
LastTick = nextTick;
if (CanUseShaders) {
RenderWithShader(TheRenderer, TheWindow, TheTexture);
} else {
SDL_RenderClear(TheRenderer);
//for (int i = 0; i < NumRects; i++)
// SDL_UpdateTexture(TheTexture, &Rects[i], TheScreen->pixels, TheScreen->pitch);
SDL_RenderCopy(TheRenderer, TheTexture, NULL, NULL);
if (EnableDebugPrint) {
// show a bar representing fps scaled by 10
SDL_SetRenderDrawColor(TheRenderer, 255, 0, 0, 255);
Uint32 nextTick = SDL_GetTicks();
double fps = 10000.0 / (nextTick - LastTick);
SDL_RenderDrawLine(TheRenderer, 0, 0, floorl(fps), 0);
SDL_SetRenderDrawColor(TheRenderer, 0, 0, 0, 255);
LastTick = nextTick;
}
SDL_RenderPresent(TheRenderer);
}
SDL_RenderPresent(TheRenderer);
NumRects = 0;
}
HideCursor();

431
src/video/shaders.cpp Normal file
View file

@ -0,0 +1,431 @@
/**
* MIT License
*
* Copyright (c) 2020 Tim Felgentreff
* Copyright (c) 2017 Augusto Ruiz
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "shaders.h"
#include <SDL.h>
#include <SDL_opengl.h>
#include <SDL_opengl_glext.h>
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include "stratagus.h"
#include "parameters.h"
#include "video.h"
#include "game.h"
#include "iolib.h"
#include "script.h"
#ifndef __APPLE__
// Avoiding the use of GLEW or some extensions handler
PFNGLCREATESHADERPROC glCreateShader;
PFNGLSHADERSOURCEPROC glShaderSource;
PFNGLCOMPILESHADERPROC glCompileShader;
PFNGLGETSHADERIVPROC glGetShaderiv;
PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
PFNGLDELETESHADERPROC glDeleteShader;
PFNGLATTACHSHADERPROC glAttachShader;
PFNGLCREATEPROGRAMPROC glCreateProgram;
PFNGLLINKPROGRAMPROC glLinkProgram;
PFNGLVALIDATEPROGRAMPROC glValidateProgram;
PFNGLGETPROGRAMIVPROC glGetProgramiv;
PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
PFNGLUSEPROGRAMPROC glUseProgram;
PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation;
PFNGLUNIFORM1IPROC glUniform1i;
PFNGLUNIFORM1FPROC glUniform1f;
PFNGLUNIFORM2FPROC glUniform2f;
PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv;
PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation;
PFNGLVERTEXATTRIB4FPROC glVertexAttrib4f;
#ifdef WIN32
#define CCONV __stdcall
#else
#define CCONV
#endif
void (CCONV *lazyGlBegin)(GLenum);
void (CCONV *lazyGlEnd)(void);
void (CCONV *lazyGlTexCoord2f)(GLfloat, GLfloat);
void (CCONV *lazyGlVertex2f)(GLfloat, GLfloat);
void (CCONV *lazyGlGetIntegerv)(GLenum, GLint*);
void (CCONV *lazyGlGetFloatv)(GLenum, GLfloat*);
void (CCONV *lazyGlViewport)(GLint, GLint, GLsizei, GLsizei);
void (CCONV *lazyGlMatrixMode)(GLenum);
void (CCONV *lazyGlLoadIdentity)(void);
void (CCONV *lazyGlOrtho)(GLdouble, GLdouble, GLdouble, GLdouble, GLdouble, GLdouble);
static const int MAX_SHADERS = 128;
static GLuint shaderPrograms[MAX_SHADERS + 1] = { (GLuint) 0 };
static const char* shaderNames[MAX_SHADERS + 1] = { NULL };
static char shadersLoaded = -1;
static int currentShaderIdx = 0;
const char* none =
#include "./shaders/noshader.glsl"
;
const char* CRT =
#include "./shaders/crt.glsl"
;
const char* VHS =
#include "./shaders/vhs.glsl"
;
const char* xBRZ =
#include "./shaders/xbrz.glsl"
;
static GLuint compileShader(const char* source, GLuint shaderType) {
// Create ID for shader
GLuint result = glCreateShader(shaderType);
// Define shader text
glShaderSource(result, 1, &source, NULL);
// Compile shader
glCompileShader(result);
// Check vertex shader for errors
GLint shaderCompiled = GL_FALSE;
glGetShaderiv( result, GL_COMPILE_STATUS, &shaderCompiled );
if( shaderCompiled != GL_TRUE ) {
std::cout << "Error during compilation: " << result << "!" << std::endl;
GLint logLength;
glGetShaderiv(result, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0) {
GLchar *log = (GLchar*)malloc(logLength);
glGetShaderInfoLog(result, logLength, &logLength, log);
std::cout << "Shader compile log: " << log << std::endl;
free(log);
}
glDeleteShader(result);
result = 0;
} else {
std::cout << "Shader compiled correctly. Id = " << result << std::endl;
}
return result;
}
static GLuint compileProgramSource(std::string source) {
GLuint programId = 0;
GLuint vtxShaderId, fragShaderId;
vtxShaderId = compileShader((std::string("#define VERTEX\n") + source).c_str(), GL_VERTEX_SHADER);
fragShaderId = compileShader((std::string("#define FRAGMENT\n") + source).c_str(), GL_FRAGMENT_SHADER);
if(vtxShaderId && fragShaderId) {
programId = glCreateProgram();
// Associate shader with program
glAttachShader(programId, vtxShaderId);
glAttachShader(programId, fragShaderId);
glLinkProgram(programId);
glValidateProgram(programId);
// Check the status of the compile/link
GLint logLen;
glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &logLen);
if(logLen > 0) {
char* log = (char*) malloc(logLen * sizeof(char));
// Show any errors as appropriate
glGetProgramInfoLog(programId, logLen, &logLen, log);
std::cout << "Prog Info Log: " << std::endl << log << std::endl;
free(log);
}
}
if(vtxShaderId) {
glDeleteShader(vtxShaderId);
}
if(fragShaderId) {
glDeleteShader(fragShaderId);
}
return programId;
}
static GLuint compileProgram(std::string shaderFile) {
std::ifstream f(shaderFile);
std::string source((std::istreambuf_iterator<char>(f)),
std::istreambuf_iterator<char>());
std::cout << "[Shaders] Compiling shader: " << shaderFile << std::endl;
return compileProgramSource(source);
}
static void loadShaders() {
int numShdr = 0;
#define COMPILE_BUILTIN_SHADER(name) \
std::cout << "[Shaders] Compiling shader: " #name << std::endl; \
shaderPrograms[numShdr] = compileProgramSource(std::string(name)); \
shaderNames[numShdr] = #name ; \
numShdr++;
COMPILE_BUILTIN_SHADER(none);
COMPILE_BUILTIN_SHADER(xBRZ);
COMPILE_BUILTIN_SHADER(CRT);
COMPILE_BUILTIN_SHADER(VHS);
#undef COMPILE_BUILTIN_SHADER
std::vector<FileList> flp;
std::string shaderPath(StratagusLibPath);
char *cShaderPath;
#ifdef _WIN32
shaderPath.append("\\shaders\\");
int fullpathsize = ExpandEnvironmentStrings(shaderPath.c_str(), NULL, 0);
cShaderPath = (char*)calloc(fullpathsize + 1, sizeof(char));
ExpandEnvironmentStrings(shaderPath.c_str(), cShaderPath, fullpathsize);
#else
shaderPath.append("/shaders/");
cShaderPath = (char*)shaderPath.c_str();
#endif
int n = ReadDataDirectory(cShaderPath, flp);
int shaderFileToIdx[1024];
for (int i = 0; i < n; ++i) {
int pos = flp[i].name.find(".glsl");
if (pos > 0) {
GLuint program = compileProgram(shaderPath + flp[i].name);
if (program) {
shaderPrograms[numShdr] = program;
shaderNames[numShdr] = strdup(flp[i].name.c_str());
numShdr += 1;
if (numShdr >= MAX_SHADERS) {
break;
}
}
}
}
}
void RenderWithShader(SDL_Renderer *renderer, SDL_Window* win, SDL_Texture* backBuffer) {
GLint oldProgramId;
// Detach the texture
SDL_SetRenderTarget(renderer, NULL);
SDL_RenderClear(renderer);
SDL_GL_BindTexture(backBuffer, NULL, NULL);
GLuint shaderProgram = shaderPrograms[currentShaderIdx];
if (shaderProgram != 0) {
lazyGlGetIntegerv(GL_CURRENT_PROGRAM, &oldProgramId);
glUseProgram(shaderProgram);
}
// These are the default uniforms and attrs for glsl converted libretro shaders
GLint Texture = glGetUniformLocation(shaderProgram, "Texture");
GLint MVPMatrix = glGetUniformLocation(shaderProgram, "MVPMatrix");
GLint FrameDirection = glGetUniformLocation(shaderProgram, "FrameDirection");
GLint FrameCount = glGetUniformLocation(shaderProgram, "FrameCount");
GLint OutputSize = glGetUniformLocation(shaderProgram, "OutputSize");
GLint TextureSize = glGetUniformLocation(shaderProgram, "TextureSize");
GLint InputSize = glGetUniformLocation(shaderProgram, "InputSize");
// (timfel): If I manually set the VertexCoord, it's wrong? But I have to set TexCoord? no idea...
// GLint VertexCoord = glGetAttribLocation(shaderProgram, "VertexCoord");
GLint TexCoord = glGetAttribLocation(shaderProgram, "TexCoord");
// Window coordinates
int w, h, xBorder = 0, yBorder = 0;
SDL_GL_GetDrawableSize(win, &w, &h);
// letterboxing
double xScale = (double)w / Video.Width;
double yScale = (double)h / Video.Height;
if (xScale > yScale) {
xScale = yScale;
xBorder = std::floor((w - (Video.Width * yScale)) / 2.0);
w = Video.Width * yScale;
} else {
yScale = xScale;
yBorder = std::floor((h - (Video.Height * xScale)) / 2.0);
h = Video.Height * xScale;
}
glUniform1i(Texture, 0);
GLfloat modelview[4 * 4];
GLfloat projection[4 * 4];
lazyGlGetFloatv(GL_MODELVIEW_MATRIX, modelview);
lazyGlGetFloatv(GL_PROJECTION_MATRIX, projection);
GLfloat matrix[4 * 4] = {0.0f};
for (int i = 0; i < 4; i++) {
for(int j = 0; j < 4; j++) {
for (int k = 0; k < 4; k++) {
matrix[i * 4 + j] += modelview[i * 4 + k] * projection[k * 4 + j];
}
}
}
glUniformMatrix4fv(MVPMatrix, 1, GL_FALSE, matrix);
glUniform1f(FrameDirection, 1);
glUniform1f(FrameCount, 1);
glUniform2f(OutputSize, (float)w, (float)h);
glUniform2f(TextureSize, (float)Video.Width, (float)Video.Height);
glUniform2f(InputSize, (float)Video.Width, (float)Video.Height);
GLfloat minx, miny, maxx, maxy;
GLfloat minu, maxu, minv, maxv;
minx = 0.0f;
miny = 0.0f;
maxx = w;
maxy = h;
minu = 0.0f;
maxu = 1.0f;
minv = 0.0f;
maxv = 1.0f;
lazyGlMatrixMode(GL_PROJECTION);
lazyGlLoadIdentity();
lazyGlOrtho(0.0f, w, h, 0.0f, 0.0f, 1.0f);
lazyGlViewport(xBorder, yBorder, w, h);
lazyGlBegin(GL_TRIANGLE_STRIP);
glVertexAttrib4f(TexCoord, minu, minv, 0, 0);
lazyGlTexCoord2f(minu, minv);
lazyGlVertex2f(minx, miny);
glVertexAttrib4f(TexCoord, maxu, minv, 0, 0);
lazyGlTexCoord2f(maxu, minv);
lazyGlVertex2f(maxx, miny);
glVertexAttrib4f(TexCoord, minu, maxv, 0, 0);
lazyGlTexCoord2f(minu, maxv);
lazyGlVertex2f(minx, maxy);
glVertexAttrib4f(TexCoord, maxu, maxv, 0, 0);
lazyGlTexCoord2f(maxu, maxv);
lazyGlVertex2f(maxx, maxy);
lazyGlEnd();
SDL_GL_SwapWindow(win);
if (shaderProgram != 0) {
glUseProgram(oldProgramId);
}
}
const char* NextShader() {
if (shaderPrograms[++currentShaderIdx] == 0) {
currentShaderIdx = 0;
}
std::cout << "NextShader: " << shaderNames[currentShaderIdx] << std::endl;
return shaderNames[currentShaderIdx];
}
static int CclGetShader(lua_State *l) {
LuaCheckArgs(l, 0);
const char* shaderName = shaderNames[currentShaderIdx];
if (shaderName) {
lua_pushstring(l, shaderName);
} else {
lua_pushnil(l);
}
return 1;
}
static int CclSetShader(lua_State *l) {
LuaCheckArgs(l, 1);
const char* shaderName = LuaToString(l, 1);
for (int i = 0; i < MAX_SHADERS; i++) {
const char* n = shaderNames[i];
if (n) {
if (!strcmp(n, shaderName)) {
currentShaderIdx = i;
std::cout << "SetShader: " << shaderNames[currentShaderIdx] << std::endl;
lua_pushboolean(l, 1);
return 1;
}
} else {
break;
}
}
lua_pushboolean(l, 0);
return 1;
}
static int CclGetShaderNames(lua_State *l) {
LuaCheckArgs(l, 0);
lua_newtable(l);
for (int i = 0; shaderNames[i] != NULL; i++) {
lua_pushstring(l, shaderNames[i]);
lua_rawseti(l, -2, i + 1);
}
return 1;
}
bool LoadShaderExtensions() {
if (shadersLoaded != -1) {
return shadersLoaded == 1;
}
*(void **) (&lazyGlBegin) = SDL_GL_GetProcAddress("glBegin");
*(void **) (&lazyGlEnd) = SDL_GL_GetProcAddress("glEnd");
*(void **) (&lazyGlTexCoord2f) = SDL_GL_GetProcAddress("glTexCoord2f");
*(void **) (&lazyGlVertex2f) = SDL_GL_GetProcAddress("glVertex2f");
*(void **) (&lazyGlGetIntegerv) = SDL_GL_GetProcAddress("glGetIntegerv");
*(void **) (&lazyGlGetFloatv) = SDL_GL_GetProcAddress("glGetFloatv");
*(void **) (&lazyGlViewport) = SDL_GL_GetProcAddress("glViewport");
*(void **) (&lazyGlMatrixMode) = SDL_GL_GetProcAddress("glMatrixMode");
*(void **) (&lazyGlOrtho) = SDL_GL_GetProcAddress("glOrtho");
*(void **) (&lazyGlLoadIdentity) = SDL_GL_GetProcAddress("glLoadIdentity");
glCreateShader = (PFNGLCREATESHADERPROC)SDL_GL_GetProcAddress("glCreateShader");
glShaderSource = (PFNGLSHADERSOURCEPROC)SDL_GL_GetProcAddress("glShaderSource");
glCompileShader = (PFNGLCOMPILESHADERPROC)SDL_GL_GetProcAddress("glCompileShader");
glGetShaderiv = (PFNGLGETSHADERIVPROC)SDL_GL_GetProcAddress("glGetShaderiv");
glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)SDL_GL_GetProcAddress("glGetShaderInfoLog");
glDeleteShader = (PFNGLDELETESHADERPROC)SDL_GL_GetProcAddress("glDeleteShader");
glAttachShader = (PFNGLATTACHSHADERPROC)SDL_GL_GetProcAddress("glAttachShader");
glCreateProgram = (PFNGLCREATEPROGRAMPROC)SDL_GL_GetProcAddress("glCreateProgram");
glLinkProgram = (PFNGLLINKPROGRAMPROC)SDL_GL_GetProcAddress("glLinkProgram");
glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)SDL_GL_GetProcAddress("glValidateProgram");
glGetProgramiv = (PFNGLGETPROGRAMIVPROC)SDL_GL_GetProcAddress("glGetProgramiv");
glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)SDL_GL_GetProcAddress("glGetProgramInfoLog");
glUseProgram = (PFNGLUSEPROGRAMPROC)SDL_GL_GetProcAddress("glUseProgram");
glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)SDL_GL_GetProcAddress("glGetUniformLocation");
glUniform1i = (PFNGLUNIFORM1IPROC)SDL_GL_GetProcAddress("glUniform1i");
glUniform1f = (PFNGLUNIFORM1FPROC)SDL_GL_GetProcAddress("glUniform1f");
glUniform2f = (PFNGLUNIFORM2FPROC)SDL_GL_GetProcAddress("glUniform2f");
glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)SDL_GL_GetProcAddress("glUniformMatrix4fv");
glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC)SDL_GL_GetProcAddress("glGetAttribLocation");
glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC)SDL_GL_GetProcAddress("glVertexAttrib4f");
if (lazyGlBegin && lazyGlEnd && lazyGlTexCoord2f && lazyGlVertex2f && lazyGlGetIntegerv &&
glCreateShader && glShaderSource && glCompileShader && glGetShaderiv &&
glGetShaderInfoLog && glDeleteShader && glAttachShader && glCreateProgram &&
glLinkProgram && glValidateProgram && glGetProgramiv && glGetProgramInfoLog &&
glUseProgram && glGetUniformLocation && glUniform1i && glUniform1f && glUniform2f &&
glUniformMatrix4fv && glGetAttribLocation && glVertexAttrib4f) {
shadersLoaded = 1;
loadShaders();
} else {
shadersLoaded = 0;
}
lua_register(Lua, "GetShaderNames", CclGetShaderNames);
lua_register(Lua, "GetShader", CclGetShader);
lua_register(Lua, "SetShader", CclSetShader);
return shadersLoaded == 1;
}
#endif

271
src/video/shaders/crt.glsl Normal file
View file

@ -0,0 +1,271 @@
R"(
/*
CRT Shader by EasyMode
License: GPL
A flat CRT shader ideally for 1080p or higher displays.
Recommended Settings:
Video
- Aspect Ratio: 4:3
- Integer Scale: Off
Shader
- Filter: Nearest
- Scale: Don't Care
Example RGB Mask Parameter Settings:
Aperture Grille (Default)
- Dot Width: 1
- Dot Height: 1
- Stagger: 0
Lottes' Shadow Mask
- Dot Width: 2
- Dot Height: 1
- Stagger: 3
*/
// Parameter lines go here:
#pragma parameter SHARPNESS_H SharpnessHorizontal 0.5 0.0 1.0 0.05
#pragma parameter SHARPNESS_V SharpnessVertical 1.0 0.0 1.0 0.05
#pragma parameter MASK_STRENGTH MaskStrength 0.3 0.0 1.0 0.01
#pragma parameter MASK_DOT_WIDTH MaskDotWidth 1.0 1.0 100.0 1.0
#pragma parameter MASK_DOT_HEIGHT MaskDotHeight 1.0 1.0 100.0 1.0
#pragma parameter MASK_STAGGER MaskStagger 0.0 0.0 100.0 1.0
#pragma parameter MASK_SIZE MaskSize 1.0 1.0 100.0 1.0
#pragma parameter SCANLINE_STRENGTH ScanlineStrength 1.0 0.0 1.0 0.05
#pragma parameter SCANLINE_BEAM_WIDTH_MIN ScanlineBeamWidthMin. 1.5 0.5 5.0 0.5
#pragma parameter SCANLINE_BEAM_WIDTH_MAX ScanlineBeamWidthMax. 1.5 0.5 5.0 0.5
#pragma parameter SCANLINE_BRIGHT_MIN ScanlineBrightnessMin. 0.35 0.0 1.0 0.05
#pragma parameter SCANLINE_BRIGHT_MAX ScanlineBrightnessMax. 0.65 0.0 1.0 0.05
#pragma parameter SCANLINE_CUTOFF ScanlineCutoff 400.0 1.0 1000.0 1.0
#pragma parameter GAMMA_INPUT GammaInput 2.0 0.1 5.0 0.1
#pragma parameter GAMMA_OUTPUT GammaOutput 1.8 0.1 5.0 0.1
#pragma parameter BRIGHT_BOOST BrightnessBoost 1.2 1.0 2.0 0.01
#pragma parameter DILATION Dilation 1.0 0.0 1.0 1.0
#if defined(VERTEX)
#if __VERSION__ >= 130
#define COMPAT_VARYING out
#define COMPAT_ATTRIBUTE in
#define COMPAT_TEXTURE texture
#else
#define COMPAT_VARYING varying
#define COMPAT_ATTRIBUTE attribute
#define COMPAT_TEXTURE texture2D
#endif
#ifdef GL_ES
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif
COMPAT_ATTRIBUTE vec4 VertexCoord;
COMPAT_ATTRIBUTE vec4 COLOR;
COMPAT_ATTRIBUTE vec4 TexCoord;
COMPAT_VARYING vec4 COL0;
COMPAT_VARYING vec4 TEX0;
vec4 _oPosition1;
uniform mat4 MVPMatrix;
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
void main()
{
gl_Position = MVPMatrix * VertexCoord;
COL0 = COLOR;
TEX0.xy = TexCoord.xy;
}
#elif defined(FRAGMENT)
#if __VERSION__ >= 130
#define COMPAT_VARYING in
#define COMPAT_TEXTURE texture
out vec4 FragColor;
#else
#define COMPAT_VARYING varying
#define FragColor gl_FragColor
#define COMPAT_TEXTURE texture2D
#endif
#ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
precision mediump int;
#endif
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
uniform sampler2D Texture;
COMPAT_VARYING vec4 TEX0;
#define FIX(c) max(abs(c), 1e-5)
#define PI 3.141592653589
#define TEX2D(c) dilate(COMPAT_TEXTURE(Texture, c))
// compatibility #defines
#define Source Texture
#define vTexCoord TEX0.xy
#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
#define outsize vec4(OutputSize, 1.0 / OutputSize)
#ifdef PARAMETER_UNIFORM
// All parameter floats need to have COMPAT_PRECISION in front of them
uniform COMPAT_PRECISION float SHARPNESS_H;
uniform COMPAT_PRECISION float SHARPNESS_V;
uniform COMPAT_PRECISION float MASK_STRENGTH;
uniform COMPAT_PRECISION float MASK_DOT_WIDTH;
uniform COMPAT_PRECISION float MASK_DOT_HEIGHT;
uniform COMPAT_PRECISION float MASK_STAGGER;
uniform COMPAT_PRECISION float MASK_SIZE;
uniform COMPAT_PRECISION float SCANLINE_STRENGTH;
uniform COMPAT_PRECISION float SCANLINE_BEAM_WIDTH_MIN;
uniform COMPAT_PRECISION float SCANLINE_BEAM_WIDTH_MAX;
uniform COMPAT_PRECISION float SCANLINE_BRIGHT_MIN;
uniform COMPAT_PRECISION float SCANLINE_BRIGHT_MAX;
uniform COMPAT_PRECISION float SCANLINE_CUTOFF;
uniform COMPAT_PRECISION float GAMMA_INPUT;
uniform COMPAT_PRECISION float GAMMA_OUTPUT;
uniform COMPAT_PRECISION float BRIGHT_BOOST;
uniform COMPAT_PRECISION float DILATION;
#else
#define SHARPNESS_H 0.5
#define SHARPNESS_V 1.0
#define MASK_STRENGTH 0.3
#define MASK_DOT_WIDTH 1.0
#define MASK_DOT_HEIGHT 1.0
#define MASK_STAGGER 0.0
#define MASK_SIZE 1.0
#define SCANLINE_STRENGTH 1.0
#define SCANLINE_BEAM_WIDTH_MIN 1.5
#define SCANLINE_BEAM_WIDTH_MAX 1.5
#define SCANLINE_BRIGHT_MIN 0.35
#define SCANLINE_BRIGHT_MAX 0.65
#define SCANLINE_CUTOFF 400.0
#define GAMMA_INPUT 2.0
#define GAMMA_OUTPUT 1.8
#define BRIGHT_BOOST 1.2
#define DILATION 1.0
#endif
// Set to 0 to use linear filter and gain speed
#define ENABLE_LANCZOS 1
vec4 dilate(vec4 col)
{
vec4 x = mix(vec4(1.0), col, DILATION);
return col * x;
}
float curve_distance(float x, float sharp)
{
/*
apply half-circle s-curve to distance for sharper (more pixelated) interpolation
single line formula for Graph Toy:
0.5 - sqrt(0.25 - (x - step(0.5, x)) * (x - step(0.5, x))) * sign(0.5 - x)
*/
float x_step = step(0.5, x);
float curve = 0.5 - sqrt(0.25 - (x - x_step) * (x - x_step)) * sign(0.5 - x);
return mix(x, curve, sharp);
}
mat4 get_color_matrix(vec2 co, vec2 dx)
{
return mat4(TEX2D(co - dx), TEX2D(co), TEX2D(co + dx), TEX2D(co + 2.0 * dx));
}
vec3 filter_lanczos(vec4 coeffs, mat4 color_matrix)
{
vec4 col = color_matrix * coeffs;
vec4 sample_min = min(color_matrix[1], color_matrix[2]);
vec4 sample_max = max(color_matrix[1], color_matrix[2]);
col = clamp(col, sample_min, sample_max);
return col.rgb;
}
void main()
{
vec2 dx = vec2(SourceSize.z, 0.0);
vec2 dy = vec2(0.0, SourceSize.w);
vec2 pix_co = vTexCoord * SourceSize.xy - vec2(0.5, 0.5);
vec2 tex_co = (floor(pix_co) + vec2(0.5, 0.5)) * SourceSize.zw;
vec2 dist = fract(pix_co);
float curve_x;
vec3 col, col2;
#if ENABLE_LANCZOS
curve_x = curve_distance(dist.x, SHARPNESS_H * SHARPNESS_H);
vec4 coeffs = PI * vec4(1.0 + curve_x, curve_x, 1.0 - curve_x, 2.0 - curve_x);
coeffs = FIX(coeffs);
coeffs = 2.0 * sin(coeffs) * sin(coeffs * 0.5) / (coeffs * coeffs);
coeffs /= dot(coeffs, vec4(1.0));
col = filter_lanczos(coeffs, get_color_matrix(tex_co, dx));
col2 = filter_lanczos(coeffs, get_color_matrix(tex_co + dy, dx));
#else
curve_x = curve_distance(dist.x, SHARPNESS_H);
col = mix(TEX2D(tex_co).rgb, TEX2D(tex_co + dx).rgb, curve_x);
col2 = mix(TEX2D(tex_co + dy).rgb, TEX2D(tex_co + dx + dy).rgb, curve_x);
#endif
col = mix(col, col2, curve_distance(dist.y, SHARPNESS_V));
col = pow(col, vec3(GAMMA_INPUT / (DILATION + 1.0)));
float luma = dot(vec3(0.2126, 0.7152, 0.0722), col);
float bright = (max(col.r, max(col.g, col.b)) + luma) * 0.5;
float scan_bright = clamp(bright, SCANLINE_BRIGHT_MIN, SCANLINE_BRIGHT_MAX);
float scan_beam = clamp(bright * SCANLINE_BEAM_WIDTH_MAX, SCANLINE_BEAM_WIDTH_MIN, SCANLINE_BEAM_WIDTH_MAX);
float scan_weight = 1.0 - pow(cos(vTexCoord.y * 2.0 * PI * SourceSize.y) * 0.5 + 0.5, scan_beam) * SCANLINE_STRENGTH;
float mask = 1.0 - MASK_STRENGTH;
vec2 mod_fac = floor(vTexCoord * outsize.xy * SourceSize.xy / (InputSize.xy * vec2(MASK_SIZE, MASK_DOT_HEIGHT * MASK_SIZE)));
int dot_no = int(mod((mod_fac.x + mod(mod_fac.y, 2.0) * MASK_STAGGER) / MASK_DOT_WIDTH, 3.0));
vec3 mask_weight;
if (dot_no == 0) mask_weight = vec3(1.0, mask, mask);
else if (dot_no == 1) mask_weight = vec3(mask, 1.0, mask);
else mask_weight = vec3(mask, mask, 1.0);
if (InputSize.y >= SCANLINE_CUTOFF)
scan_weight = 1.0;
col2 = col.rgb;
col *= vec3(scan_weight);
col = mix(col, col2, scan_bright);
col *= mask_weight;
col = pow(col, vec3(1.0 / GAMMA_OUTPUT));
FragColor = vec4(col * BRIGHT_BOOST, 1.0);
}
#endif
)"

View file

@ -0,0 +1,15 @@
R"(
#if defined(VERTEX)
varying vec2 v_texCoord;
void main() {
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
v_texCoord = vec2(gl_MultiTexCoord0);
}
#elif defined(FRAGMENT)
varying vec2 v_texCoord;
uniform sampler2D tex0;
void main() {
gl_FragColor = texture2D(tex0, v_texCoord);
}
#endif
)"

232
src/video/shaders/vhs.glsl Normal file
View file

@ -0,0 +1,232 @@
R"(
// VHS shader
// by hunterk
// adapted from ompuco's more AVdistortion shadertoy:
// https://www.shadertoy.com/view/XlsczN
// Parameter lines go here:
#pragma parameter wiggle Wiggle 0.0 0.0 10.0 1.0
#pragma parameter smear ChromaSmear 0.5 0.0 1.0 0.05
#if defined(VERTEX)
#if __VERSION__ >= 130
#define COMPAT_VARYING out
#define COMPAT_ATTRIBUTE in
#define COMPAT_TEXTURE texture
#else
#define COMPAT_VARYING varying
#define COMPAT_ATTRIBUTE attribute
#define COMPAT_TEXTURE texture2D
#endif
#ifdef GL_ES
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif
COMPAT_ATTRIBUTE vec4 VertexCoord;
COMPAT_ATTRIBUTE vec4 COLOR;
COMPAT_ATTRIBUTE vec4 TexCoord;
COMPAT_VARYING vec4 COL0;
COMPAT_VARYING vec4 TEX0;
vec4 _oPosition1;
uniform mat4 MVPMatrix;
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
// compatibility #defines
#define vTexCoord TEX0.xy
#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
#define OutSize vec4(OutputSize, 1.0 / OutputSize)
void main()
{
gl_Position = MVPMatrix * VertexCoord;
TEX0.xy = TexCoord.xy;
}
#elif defined(FRAGMENT)
#ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif
#if __VERSION__ >= 130
#define COMPAT_VARYING in
#define COMPAT_TEXTURE texture
out COMPAT_PRECISION vec4 FragColor;
#else
#define COMPAT_VARYING varying
#define FragColor gl_FragColor
#define COMPAT_TEXTURE texture2D
#endif
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
uniform sampler2D Texture;
uniform sampler2D play;
COMPAT_VARYING vec4 TEX0;
// compatibility #defines
#define Source Texture
#define vTexCoord TEX0.xy
#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
#define OutSize vec4(OutputSize, 1.0 / OutputSize)
#ifdef PARAMETER_UNIFORM
uniform COMPAT_PRECISION float wiggle;
uniform COMPAT_PRECISION float smear;
#else
#define wiggle 3.0
#define smear 0.5
#endif
#define iTime mod(float(FrameCount), 7.0)
#define iChannel0 Texture
//YIQ/RGB shit
vec3 rgb2yiq(vec3 c){
return vec3(
(0.2989*c.x + 0.5959*c.y + 0.2115*c.z),
(0.5870*c.x - 0.2744*c.y - 0.5229*c.z),
(0.1140*c.x - 0.3216*c.y + 0.3114*c.z)
);
}
vec3 yiq2rgb(vec3 c){
return vec3(
( 1.0*c.x + 1.0*c.y + 1.0*c.z),
( 0.956*c.x - 0.2720*c.y - 1.1060*c.z),
(0.6210*c.x - 0.6474*c.y + 1.7046*c.z)
);
}
vec2 Circle(float Start, float Points, float Point)
{
float Rad = (3.141592 * 2.0 * (1.0 / Points)) * (Point + Start);
//return vec2(sin(Rad), cos(Rad));
return vec2(-(.3+Rad), cos(Rad));
}
vec3 Blur(vec2 uv, float d){
float t = (sin(iTime*5.0+uv.y*5.0))/10.0;
float b = 1.0;
t=0.0;
vec2 PixelOffset=vec2(d+.0005*t,0);
float Start = 2.0 / 14.0;
vec2 Scale = 0.66 * 4.0 * 2.0 * PixelOffset.xy;
vec3 N0 = COMPAT_TEXTURE(iChannel0, uv + Circle(Start, 14.0, 0.0) * Scale).rgb;
vec3 N1 = COMPAT_TEXTURE(iChannel0, uv + Circle(Start, 14.0, 1.0) * Scale).rgb;
vec3 N2 = COMPAT_TEXTURE(iChannel0, uv + Circle(Start, 14.0, 2.0) * Scale).rgb;
vec3 N3 = COMPAT_TEXTURE(iChannel0, uv + Circle(Start, 14.0, 3.0) * Scale).rgb;
vec3 N4 = COMPAT_TEXTURE(iChannel0, uv + Circle(Start, 14.0, 4.0) * Scale).rgb;
vec3 N5 = COMPAT_TEXTURE(iChannel0, uv + Circle(Start, 14.0, 5.0) * Scale).rgb;
vec3 N6 = COMPAT_TEXTURE(iChannel0, uv + Circle(Start, 14.0, 6.0) * Scale).rgb;
vec3 N7 = COMPAT_TEXTURE(iChannel0, uv + Circle(Start, 14.0, 7.0) * Scale).rgb;
vec3 N8 = COMPAT_TEXTURE(iChannel0, uv + Circle(Start, 14.0, 8.0) * Scale).rgb;
vec3 N9 = COMPAT_TEXTURE(iChannel0, uv + Circle(Start, 14.0, 9.0) * Scale).rgb;
vec3 N10 = COMPAT_TEXTURE(iChannel0, uv + Circle(Start, 14.0, 10.0) * Scale).rgb;
vec3 N11 = COMPAT_TEXTURE(iChannel0, uv + Circle(Start, 14.0, 11.0) * Scale).rgb;
vec3 N12 = COMPAT_TEXTURE(iChannel0, uv + Circle(Start, 14.0, 12.0) * Scale).rgb;
vec3 N13 = COMPAT_TEXTURE(iChannel0, uv + Circle(Start, 14.0, 13.0) * Scale).rgb;
vec3 N14 = COMPAT_TEXTURE(iChannel0, uv).rgb;
vec4 clr = COMPAT_TEXTURE(iChannel0, uv);
float W = 1.0 / 15.0;
clr.rgb=
(N0 * W) +
(N1 * W) +
(N2 * W) +
(N3 * W) +
(N4 * W) +
(N5 * W) +
(N6 * W) +
(N7 * W) +
(N8 * W) +
(N9 * W) +
(N10 * W) +
(N11 * W) +
(N12 * W) +
(N13 * W) +
(N14 * W);
return vec3(clr.xyz)*b;
}
float onOff(float a, float b, float c, float framecount)
{
return step(c, sin((framecount * 0.001) + a*cos((framecount * 0.001)*b)));
}
vec2 jumpy(vec2 uv, float framecount)
{
vec2 look = uv;
float window = 1./(1.+80.*(look.y-mod(framecount/4.,1.))*(look.y-mod(framecount/4.,1.)));
look.x += 0.05 * sin(look.y*10. + framecount)/20.*onOff(4.,4.,.3, framecount)*(0.5+cos(framecount*20.))*window;
float vShift = (0.1*wiggle) * 0.4*onOff(2.,3.,.9, framecount)*(sin(framecount)*sin(framecount*20.) +
(0.5 + 0.1*sin(framecount*200.)*cos(framecount)));
look.y = mod(look.y - 0.01 * vShift, 1.);
return look;
}
void main()
{
float timer = (float(FrameDirection) > 0.5) ? float(FrameCount) : 0.0;
float d = 0.1 - ceil(mod(iTime/3.0,1.0) + 0.5) * 0.1;
vec2 uv = jumpy(vTexCoord.xy, iTime);
vec2 uv2 = uv;
float s = 0.0001 * -d + 0.0001 * wiggle * sin(iTime);
float e = min(.30,pow(max(0.0,cos(uv.y*4.0+.3)-.75)*(s+0.5)*1.0,3.0))*25.0;
float r = (iTime*(2.0*s));
uv.x+=abs(r*pow(min(.003,(-uv.y+(.01*mod(iTime, 17.0))))*3.0,2.0));
d=.051+abs(sin(s/4.0));
float c = max(0.0001,.002*d) * smear;
vec2 uvo = uv;
vec4 final;
final.xyz =Blur(uv,c+c*(uv.x));
float y = rgb2yiq(final.xyz).r;
uv.x+=.01*d;
c*=6.0;
final.xyz =Blur(uv,c);
float i = rgb2yiq(final.xyz).g;
uv.x+=.005*d;
c*=2.50;
final.xyz =Blur(uv,c);
float q = rgb2yiq(final.xyz).b;
final = vec4(yiq2rgb(vec3(y,i,q))-pow(s+e*2.0,3.0), 1.0);
vec4 play_osd = COMPAT_TEXTURE(play, uv2 * TextureSize.xy / InputSize.xy);
float show_overlay = (mod(timer, 100.0) < 50.0) && (timer != 0.0) && (timer < 500.0) ? play_osd.a : 0.0;
show_overlay = clamp(show_overlay, 0.0, 1.0);
final = mix(final, play_osd, show_overlay);
FragColor = final;
}
#endif
)"

377
src/video/shaders/xbrz.glsl Normal file
View file

@ -0,0 +1,377 @@
R"(
/*
Hyllian's xBR-vertex code and texel mapping
Copyright (C) 2011/2016 Hyllian - sergiogdb@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// This shader also uses code and/or concepts from xBRZ as it appears
// in the Desmume source code. The license for which is as follows:
// ****************************************************************************
// * This file is part of the HqMAME project. It is distributed under *
// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// * *
// * Additionally and as a special exception, the author gives permission *
// * to link the code of this program with the MAME library (or with modified *
// * versions of MAME that use the same license as MAME), and distribute *
// * linked combinations including the two. You must obey the GNU General *
// * Public License in all respects for all of the code used other than MAME. *
// * If you modify this file, you may extend this exception to your version *
// * of the file, but you are not obligated to do so. If you do not wish to *
// * do so, delete this exception statement from your version. *
// ****************************************************************************
#if defined(VERTEX)
#if __VERSION__ >= 130
#define COMPAT_VARYING out
#define COMPAT_ATTRIBUTE in
#define COMPAT_TEXTURE texture
#else
#define COMPAT_VARYING varying
#define COMPAT_ATTRIBUTE attribute
#define COMPAT_TEXTURE texture2D
#endif
#ifdef GL_ES
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif
COMPAT_ATTRIBUTE vec4 VertexCoord;
COMPAT_ATTRIBUTE vec4 COLOR;
COMPAT_ATTRIBUTE vec4 TexCoord;
COMPAT_VARYING vec4 COL0;
COMPAT_VARYING vec4 TEX0;
vec4 _oPosition1;
uniform mat4 MVPMatrix;
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
// compatibility #defines
#define vTexCoord TEX0.xy
#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
#define OutSize vec4(OutputSize, 1.0 / OutputSize)
#ifdef PARAMETER_UNIFORM
uniform COMPAT_PRECISION float WHATEVER;
#else
#define WHATEVER 0.0
#endif
void main()
{
gl_Position = MVPMatrix * VertexCoord;
TEX0.xy = TexCoord.xy * 1.0001;
}
#elif defined(FRAGMENT)
#ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif
#if __VERSION__ >= 130
#define COMPAT_VARYING in
#define COMPAT_TEXTURE texture
out COMPAT_PRECISION vec4 FragColor;
#else
#define COMPAT_VARYING varying
#define FragColor gl_FragColor
#define COMPAT_TEXTURE texture2D
#endif
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
uniform sampler2D Texture;
COMPAT_VARYING vec4 TEX0;
// compatibility #defines
#define Source Texture
#define vTexCoord TEX0.xy
#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
#define OutSize vec4(OutputSize, 1.0 / OutputSize)
#define BLEND_NONE 0
#define BLEND_NORMAL 1
#define BLEND_DOMINANT 2
#define LUMINANCE_WEIGHT 1.0
#define EQUAL_COLOR_TOLERANCE 30.0/255.0
#define STEEP_DIRECTION_THRESHOLD 2.2
#define DOMINANT_DIRECTION_THRESHOLD 3.6
float DistYCbCr(vec3 pixA, vec3 pixB)
{
const vec3 w = vec3(0.2627, 0.6780, 0.0593);
const float scaleB = 0.5 / (1.0 - w.b);
const float scaleR = 0.5 / (1.0 - w.r);
vec3 diff = pixA - pixB;
float Y = dot(diff.rgb, w);
float Cb = scaleB * (diff.b - Y);
float Cr = scaleR * (diff.r - Y);
return sqrt(((LUMINANCE_WEIGHT * Y) * (LUMINANCE_WEIGHT * Y)) + (Cb * Cb) + (Cr * Cr));
}
bool IsPixEqual(const vec3 pixA, const vec3 pixB)
{
return (DistYCbCr(pixA, pixB) < EQUAL_COLOR_TOLERANCE);
}
float get_left_ratio(vec2 center, vec2 origin, vec2 direction, vec2 scale)
{
vec2 P0 = center - origin;
vec2 proj = direction * (dot(P0, direction) / dot(direction, direction));
vec2 distv = P0 - proj;
vec2 orth = vec2(-direction.y, direction.x);
float side = sign(dot(P0, orth));
float v = side * length(distv * scale);
// return step(0, v);
return smoothstep(-sqrt(2.0)/2.0, sqrt(2.0)/2.0, v);
}
#define eq(a,b) (a == b)
#define neq(a,b) (a != b)
#define P(x,y) COMPAT_TEXTURE(Source, coord + SourceSize.zw * vec2(x, y)).rgb
void main()
{
//---------------------------------------
// Input Pixel Mapping: -|x|x|x|-
// x|A|B|C|x
// x|D|E|F|x
// x|G|H|I|x
// -|x|x|x|-
vec2 scale = OutputSize.xy * SourceSize.zw;
vec2 pos = fract(vTexCoord * SourceSize.xy) - vec2(0.5, 0.5);
vec2 coord = vTexCoord - pos * SourceSize.zw;
vec3 A = P(-1.,-1.);
vec3 B = P( 0.,-1.);
vec3 C = P( 1.,-1.);
vec3 D = P(-1., 0.);
vec3 E = P( 0., 0.);
vec3 F = P( 1., 0.);
vec3 G = P(-1., 1.);
vec3 H = P( 0., 1.);
vec3 I = P( 1., 1.);
// blendResult Mapping: x|y|
// w|z|
ivec4 blendResult = ivec4(BLEND_NONE,BLEND_NONE,BLEND_NONE,BLEND_NONE);
// Preprocess corners
// Pixel Tap Mapping: -|-|-|-|-
// -|-|B|C|-
// -|D|E|F|x
// -|G|H|I|x
// -|-|x|x|-
if (!((eq(E,F) && eq(H,I)) || (eq(E,H) && eq(F,I))))
{
float dist_H_F = DistYCbCr(G, E) + DistYCbCr(E, C) + DistYCbCr(P(0,2), I) + DistYCbCr(I, P(2.,0.)) + (4.0 * DistYCbCr(H, F));
float dist_E_I = DistYCbCr(D, H) + DistYCbCr(H, P(1,2)) + DistYCbCr(B, F) + DistYCbCr(F, P(2.,1.)) + (4.0 * DistYCbCr(E, I));
bool dominantGradient = (DOMINANT_DIRECTION_THRESHOLD * dist_H_F) < dist_E_I;
blendResult.z = ((dist_H_F < dist_E_I) && neq(E,F) && neq(E,H)) ? ((dominantGradient) ? BLEND_DOMINANT : BLEND_NORMAL) : BLEND_NONE;
}
// Pixel Tap Mapping: -|-|-|-|-
// -|A|B|-|-
// x|D|E|F|-
// x|G|H|I|-
// -|x|x|-|-
if (!((eq(D,E) && eq(G,H)) || (eq(D,G) && eq(E,H))))
{
float dist_G_E = DistYCbCr(P(-2.,1.) , D) + DistYCbCr(D, B) + DistYCbCr(P(-1.,2.), H) + DistYCbCr(H, F) + (4.0 * DistYCbCr(G, E));
float dist_D_H = DistYCbCr(P(-2.,0.) , G) + DistYCbCr(G, P(0.,2.)) + DistYCbCr(A, E) + DistYCbCr(E, I) + (4.0 * DistYCbCr(D, H));
bool dominantGradient = (DOMINANT_DIRECTION_THRESHOLD * dist_D_H) < dist_G_E;
blendResult.w = ((dist_G_E > dist_D_H) && neq(E,D) && neq(E,H)) ? ((dominantGradient) ? BLEND_DOMINANT : BLEND_NORMAL) : BLEND_NONE;
}
// Pixel Tap Mapping: -|-|x|x|-
// -|A|B|C|x
// -|D|E|F|x
// -|-|H|I|-
// -|-|-|-|-
if (!((eq(B,C) && eq(E,F)) || (eq(B,E) && eq(C,F))))
{
float dist_E_C = DistYCbCr(D, B) + DistYCbCr(B, P(1.,-2.)) + DistYCbCr(H, F) + DistYCbCr(F, P(2.,-1.)) + (4.0 * DistYCbCr(E, C));
float dist_B_F = DistYCbCr(A, E) + DistYCbCr(E, I) + DistYCbCr(P(0.,-2.), C) + DistYCbCr(C, P(2.,0.)) + (4.0 * DistYCbCr(B, F));
bool dominantGradient = (DOMINANT_DIRECTION_THRESHOLD * dist_B_F) < dist_E_C;
blendResult.y = ((dist_E_C > dist_B_F) && neq(E,B) && neq(E,F)) ? ((dominantGradient) ? BLEND_DOMINANT : BLEND_NORMAL) : BLEND_NONE;
}
// Pixel Tap Mapping: -|x|x|-|-
// x|A|B|C|-
// x|D|E|F|-
// -|G|H|-|-
// -|-|-|-|-
if (!((eq(A,B) && eq(D,E)) || (eq(A,D) && eq(B,E))))
{
float dist_D_B = DistYCbCr(P(-2.,0.), A) + DistYCbCr(A, P(0.,-2.)) + DistYCbCr(G, E) + DistYCbCr(E, C) + (4.0 * DistYCbCr(D, B));
float dist_A_E = DistYCbCr(P(-2.,-1.), D) + DistYCbCr(D, H) + DistYCbCr(P(-1.,-2.), B) + DistYCbCr(B, F) + (4.0 * DistYCbCr(A, E));
bool dominantGradient = (DOMINANT_DIRECTION_THRESHOLD * dist_D_B) < dist_A_E;
blendResult.x = ((dist_D_B < dist_A_E) && neq(E,D) && neq(E,B)) ? ((dominantGradient) ? BLEND_DOMINANT : BLEND_NORMAL) : BLEND_NONE;
}
vec3 res = E;
// Pixel Tap Mapping: -|-|-|-|-
// -|-|B|C|-
// -|D|E|F|x
// -|G|H|I|x
// -|-|x|x|-
if(blendResult.z != BLEND_NONE)
{
float dist_F_G = DistYCbCr(F, G);
float dist_H_C = DistYCbCr(H, C);
bool doLineBlend = (blendResult.z == BLEND_DOMINANT ||
!((blendResult.y != BLEND_NONE && !IsPixEqual(E, G)) || (blendResult.w != BLEND_NONE && !IsPixEqual(E, C)) ||
(IsPixEqual(G, H) && IsPixEqual(H, I) && IsPixEqual(I, F) && IsPixEqual(F, C) && !IsPixEqual(E, I))));
vec2 origin = vec2(0.0, 1.0 / sqrt(2.0));
vec2 direction = vec2(1.0, -1.0);
if(doLineBlend)
{
bool haveShallowLine = (STEEP_DIRECTION_THRESHOLD * dist_F_G <= dist_H_C) && neq(E,G) && neq(D,G);
bool haveSteepLine = (STEEP_DIRECTION_THRESHOLD * dist_H_C <= dist_F_G) && neq(E,C) && neq(B,C);
origin = haveShallowLine? vec2(0.0, 0.25) : vec2(0.0, 0.5);
direction.x += haveShallowLine? 1.0: 0.0;
direction.y -= haveSteepLine? 1.0: 0.0;
}
vec3 blendPix = mix(H,F, step(DistYCbCr(E, F), DistYCbCr(E, H)));
res = mix(res, blendPix, get_left_ratio(pos, origin, direction, scale));
}
// Pixel Tap Mapping: -|-|-|-|-
// -|A|B|-|-
// x|D|E|F|-
// x|G|H|I|-
// -|x|x|-|-
if(blendResult.w != BLEND_NONE)
{
float dist_H_A = DistYCbCr(H, A);
float dist_D_I = DistYCbCr(D, I);
bool doLineBlend = (blendResult.w == BLEND_DOMINANT ||
!((blendResult.z != BLEND_NONE && !IsPixEqual(E, A)) || (blendResult.x != BLEND_NONE && !IsPixEqual(E, I)) ||
(IsPixEqual(A, D) && IsPixEqual(D, G) && IsPixEqual(G, H) && IsPixEqual(H, I) && !IsPixEqual(E, G))));
vec2 origin = vec2(-1.0 / sqrt(2.0), 0.0);
vec2 direction = vec2(1.0, 1.0);
if(doLineBlend)
{
bool haveShallowLine = (STEEP_DIRECTION_THRESHOLD * dist_H_A <= dist_D_I) && neq(E,A) && neq(B,A);
bool haveSteepLine = (STEEP_DIRECTION_THRESHOLD * dist_D_I <= dist_H_A) && neq(E,I) && neq(F,I);
origin = haveShallowLine? vec2(-0.25, 0.0) : vec2(-0.5, 0.0);
direction.y += haveShallowLine? 1.0: 0.0;
direction.x += haveSteepLine? 1.0: 0.0;
}
origin = origin;
direction = direction;
vec3 blendPix = mix(H,D, step(DistYCbCr(E, D), DistYCbCr(E, H)));
res = mix(res, blendPix, get_left_ratio(pos, origin, direction, scale));
}
// Pixel Tap Mapping: -|-|x|x|-
// -|A|B|C|x
// -|D|E|F|x
// -|-|H|I|-
// -|-|-|-|-
if(blendResult.y != BLEND_NONE)
{
float dist_B_I = DistYCbCr(B, I);
float dist_F_A = DistYCbCr(F, A);
bool doLineBlend = (blendResult.y == BLEND_DOMINANT ||
!((blendResult.x != BLEND_NONE && !IsPixEqual(E, I)) || (blendResult.z != BLEND_NONE && !IsPixEqual(E, A)) ||
(IsPixEqual(I, F) && IsPixEqual(F, C) && IsPixEqual(C, B) && IsPixEqual(B, A) && !IsPixEqual(E, C))));
vec2 origin = vec2(1.0 / sqrt(2.0), 0.0);
vec2 direction = vec2(-1.0, -1.0);
if(doLineBlend)
{
bool haveShallowLine = (STEEP_DIRECTION_THRESHOLD * dist_B_I <= dist_F_A) && neq(E,I) && neq(H,I);
bool haveSteepLine = (STEEP_DIRECTION_THRESHOLD * dist_F_A <= dist_B_I) && neq(E,A) && neq(D,A);
origin = haveShallowLine? vec2(0.25, 0.0) : vec2(0.5, 0.0);
direction.y -= haveShallowLine? 1.0: 0.0;
direction.x -= haveSteepLine? 1.0: 0.0;
}
vec3 blendPix = mix(F,B, step(DistYCbCr(E, B), DistYCbCr(E, F)));
res = mix(res, blendPix, get_left_ratio(pos, origin, direction, scale));
}
// Pixel Tap Mapping: -|x|x|-|-
// x|A|B|C|-
// x|D|E|F|-
// -|G|H|-|-
// -|-|-|-|-
if(blendResult.x != BLEND_NONE)
{
float dist_D_C = DistYCbCr(D, C);
float dist_B_G = DistYCbCr(B, G);
bool doLineBlend = (blendResult.x == BLEND_DOMINANT ||
!((blendResult.w != BLEND_NONE && !IsPixEqual(E, C)) || (blendResult.y != BLEND_NONE && !IsPixEqual(E, G)) ||
(IsPixEqual(C, B) && IsPixEqual(B, A) && IsPixEqual(A, D) && IsPixEqual(D, G) && !IsPixEqual(E, A))));
vec2 origin = vec2(0.0, -1.0 / sqrt(2.0));
vec2 direction = vec2(-1.0, 1.0);
if(doLineBlend)
{
bool haveShallowLine = (STEEP_DIRECTION_THRESHOLD * dist_D_C <= dist_B_G) && neq(E,C) && neq(F,C);
bool haveSteepLine = (STEEP_DIRECTION_THRESHOLD * dist_B_G <= dist_D_C) && neq(E,G) && neq(H,G);
origin = haveShallowLine? vec2(0.0, -0.25) : vec2(0.0, -0.5);
direction.x -= haveShallowLine? 1.0: 0.0;
direction.y += haveSteepLine? 1.0: 0.0;
}
vec3 blendPix = mix(D,B, step(DistYCbCr(E, B), DistYCbCr(E, D)));
res = mix(res, blendPix, get_left_ratio(pos, origin, direction, scale));
}
FragColor = vec4(res, 1.0);
}
#endif
)"