Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add configurable custom LCD shader #2386

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

EddyHg80
Copy link
Contributor

@EddyHg80 EddyHg80 commented Dec 20, 2021

Yesterday I got carried over and learned a bit of glsl shaders in order to create my own personal shader, and since I made it totally customizable through the UI I might as well share it.

End result (default):

Click to expand

screen1

It is designed to work best at x6 integer resolution or higher (1080p or higher). Now I'll go into details.
Feedback is welcome!

  • Pass0: Brightness and Subpixels
    Since we are going to remove some colors in order to simulate subpixels I added a brightness boost before anything else
    Brightness is a parameter passed through the UI, the default is 1.1.
    Then I basically stole copied endrift's code from the ags001 shader and reworked it to make it configurable through the UI.
varying vec2 texCoord;
uniform sampler2D tex;
uniform vec2 texSize;
uniform float brightness;
uniform float subPixelDimming;

void main() {
	vec4 color = texture2D(tex, texCoord);
	vec3 subPixels[3];
	float subPixelDimming = 1.0 - subPixelDimming;
	subPixels[0] = vec3(1.0, subPixelDimming, subPixelDimming);
	subPixels[1] = vec3(subPixelDimming, 1.0, subPixelDimming);
	subPixels[2] = vec3(subPixelDimming, subPixelDimming, 1.0);
	color.rgb *= brightness;
	color.rgb *= subPixels[int(mod(texCoord.s * texSize.x * 3.0, 3.0))];
	gl_FragColor = color;
}	

image

  • Pass1: Pixel boundaries
    Here I implemented Dominus Iniquitatis' LCD shader code, but insted of working with a 3x3 grid for each pixel it's a 6x6, so the borders are thinner. Also the darkened pixels are the top and right instead of bottom and left.
    And again, the borders brightness is configurable in the UI.
varying vec2 texCoord;
uniform sampler2D tex;
uniform vec2 texSize;
uniform float boundBrightness;

void main() {
	vec4 color = texture2D(tex, texCoord);
	if (int(mod(texCoord.s * texSize.x * 6.0, 6.0)) == 5 ||
		int(mod(texCoord.t * texSize.y * 6.0, 6.0)) == 5)
	{
		color.rgb *= vec3(1.0, 1.0, 1.0) * boundBrightness;
	}
	gl_FragColor = color;
}

image

  • Pass2: endrift's recreation of light and reflection of the AGS-001 screen (this was actually a joke, but I fell for it so the least I can do is to include it anyway lol)
    Yep copy-pasted (since I don't have time to dive in), but I added a flag to enable or disable it in the UI, so it's disabled by default.
varying vec2 texCoord;
uniform sampler2D tex;
uniform float reflectionBrightness;
uniform vec2 reflectionDistance;
uniform float lightBrightness;
uniform int enable;

const float speed = 2.0;
const float decay = 2.0;
const float coeff = 2.5;

void main() {
	if(enable==1)
	{
	float sp = pow(speed, lightBrightness);
	float dc = pow(decay, -lightBrightness);
	float s = (sp - dc) / (sp + dc);
	vec2 radius = (texCoord.st - vec2(0.5, 0.5)) * vec2(coeff * s);
	radius = pow(abs(radius), vec2(4.0));
	vec3 bleed = vec3(0.12, 0.14, 0.19);
	bleed += (dot(radius, radius) + vec3(0.02, 0.03, 0.05)) * vec3(0.14, 0.18, 0.2);

	vec4 color = texture2D(tex, texCoord);
	color.rgb += pow(bleed, pow(vec3(lightBrightness), vec3(-0.5)));

	vec4 reflection = texture2D(tex, texCoord - reflectionDistance);
	color.rgb += reflection.rgb * reflectionBrightness;
	color.a = 1.0;
	gl_FragColor = color;
	}
	else
	{	
	vec4 color = texture2D(tex, texCoord);
	gl_FragColor = color;
	}
}

image
The text now says "Enable AGS-001's pristine light and reflections"

@EddyHg80
Copy link
Contributor Author

Is there a way to know the window's current scaling and use it as a parameter in the shaders? With that value I could make the best filter for other integer scalings without going crazy with math.

Comment on lines 29 to 44
[pass.2]
fragmentShader=customLCD-pixelBounds.fs
blend=1
width=-6
height=-6

[pass.2.uniform.boundBrightness]
type=float
default=0.9
readableName=Pixel bound brightness

[pass.3]
fragmentShader=ags001-light.fs
blend=1
width=-6
height=-6
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since these two passes operate at the same size, I might recommend merging them into a single pass.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason it's separated is because I wanted to keep the settings in two different tabs.
Tell me if you agree or if I should merge them anyway.

@EddyHg80
Copy link
Contributor Author

EddyHg80 commented Dec 23, 2021

There is a problem when using higher OpenGL resolutions: the texSize.x and texSize.y (240 and 160) gets multiplied too resulting in wrong coordinates in those higher resolutions.
From my testing I can simply substitute with 240 and 160 (since the gba has this fixed res) and it works, but if there is a better solution to take into account OpenGL multiplication I'll implement something like this:

const int resX = texSize.x/OGLres;
const int resY = texSize.y/OGLres;

this will only be useful for future widescreen hacks which I don't even know if are possible on the gba.

(EDIT: since OGL res breaks practically every shader maybe a fix should be implemented globally before applying shaders)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants