mirror of https://github.com/mgba-emu/mgba.git
Res: Add gbc-lcd shader, imitates GBC LCD subpixel arrangement with an optional backlight effect (#3097)
This commit is contained in:
parent
48253afc54
commit
3becd63ef5
|
@ -0,0 +1,119 @@
|
||||||
|
/**
|
||||||
|
* This shader creates a backlight bleeding effect,
|
||||||
|
* and an internal reflection or ghosting effect.
|
||||||
|
*/
|
||||||
|
|
||||||
|
varying vec2 texCoord;
|
||||||
|
uniform sampler2D tex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the color of the backlight bleed.
|
||||||
|
* Lower values produce less, dimmer light.
|
||||||
|
* Higher values produce brighter or more colorful light.
|
||||||
|
* You'll normally want each of these numbers to be close
|
||||||
|
* to 1, and not normally lower than 0.
|
||||||
|
*/
|
||||||
|
uniform vec3 LightColor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Affects the shape of the backlight bleed glow.
|
||||||
|
* Lower values cause the light bleed to fade out quickly
|
||||||
|
* from the edges.
|
||||||
|
* Higher values cause the light bleed to fade out more
|
||||||
|
* softly and gradually toward the center.
|
||||||
|
* You'll normally want this to be a number from 0 to 1.
|
||||||
|
*/
|
||||||
|
uniform float LightSoftness;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lower values result in a less visible or intense
|
||||||
|
* backlight bleed.
|
||||||
|
* Higher values make the backlight bleed more pronounced.
|
||||||
|
* You'll normally want this to be a number close to 0,
|
||||||
|
* and not normally higher than 1.
|
||||||
|
*/
|
||||||
|
uniform float LightIntensity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lower values cause the internal reflection or ghosting
|
||||||
|
* effect to be less visible.
|
||||||
|
* Higher values cause the effect to be brighter and more
|
||||||
|
* visible.
|
||||||
|
* You'll normally want this to be a number close to 0,
|
||||||
|
* and not normally higher than 1.
|
||||||
|
*/
|
||||||
|
uniform float ReflectionBrightness;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lower values have the internal reflection or ghosting
|
||||||
|
* effect appear offset by a lesser distance.
|
||||||
|
* Higher values have the effect offset by a greater
|
||||||
|
* distance.
|
||||||
|
* You'll normally want each of these numbers to be close
|
||||||
|
* to 0, and not normally higher than 1.
|
||||||
|
*/
|
||||||
|
uniform vec2 ReflectionDistance;
|
||||||
|
|
||||||
|
#define M_PI 3.1415926535897932384626433832795
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to compute backlight bleed intensity
|
||||||
|
* for a texCoord input.
|
||||||
|
*/
|
||||||
|
float getLightIntensity(vec2 coord) {
|
||||||
|
vec2 coordCentered = coord - vec2(0.5, 0.5);
|
||||||
|
float coordDistCenter = (
|
||||||
|
length(coordCentered) / sqrt(0.5)
|
||||||
|
);
|
||||||
|
vec2 coordQuadrant = vec2(
|
||||||
|
1.0 - (1.5 * min(coord.x, 1.0 - coord.x)),
|
||||||
|
1.0 - (1.5 * min(coord.y, 1.0 - coord.y))
|
||||||
|
);
|
||||||
|
float lightIntensityEdges = (
|
||||||
|
pow(coordQuadrant.x, 5.0) +
|
||||||
|
pow(coordQuadrant.y, 5.0)
|
||||||
|
);
|
||||||
|
float lightIntensity = (
|
||||||
|
(1.0 - LightSoftness) * lightIntensityEdges +
|
||||||
|
LightSoftness * coordDistCenter
|
||||||
|
);
|
||||||
|
return clamp(lightIntensity, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to convert an intensity value into a white
|
||||||
|
* gray color with that intensity. A radial distortion
|
||||||
|
* effect with subtle chromatic abberation is applied that
|
||||||
|
* makes it look a little more like a real old or cheap
|
||||||
|
* backlight, and also helps to reduce color banding.
|
||||||
|
*/
|
||||||
|
vec3 getWhiteVector(float intensity) {
|
||||||
|
const float DeformAmount = 0.0025;
|
||||||
|
vec2 texCoordCentered = texCoord - vec2(0.5, 0.5);
|
||||||
|
float radians = atan(texCoordCentered.y, texCoordCentered.x);
|
||||||
|
float rot = pow(2.0, 4.0 + floor(6.0 * length(texCoordCentered)));
|
||||||
|
float deformRed = cos(rot * radians + (2.0 / 3.0 * M_PI));
|
||||||
|
float deformGreen = cos(rot * radians);
|
||||||
|
float deformBlue = cos(rot * radians + (4.0 / 3.0 * M_PI));
|
||||||
|
return clamp(vec3(
|
||||||
|
intensity + (deformRed * DeformAmount),
|
||||||
|
intensity + (deformGreen * DeformAmount),
|
||||||
|
intensity + (deformBlue * DeformAmount)
|
||||||
|
), 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 colorSource = texture2D(tex, texCoord).rgb;
|
||||||
|
vec3 lightWhiteVector = getWhiteVector(getLightIntensity(texCoord));
|
||||||
|
vec3 colorLight = LightColor * lightWhiteVector;
|
||||||
|
vec3 colorReflection = texture2D(tex, texCoord - ReflectionDistance).rgb;
|
||||||
|
vec3 colorResult = (
|
||||||
|
colorSource +
|
||||||
|
(colorLight * LightIntensity) +
|
||||||
|
(colorReflection * ReflectionBrightness)
|
||||||
|
);
|
||||||
|
gl_FragColor = vec4(
|
||||||
|
colorResult,
|
||||||
|
1.0
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,421 @@
|
||||||
|
/**
|
||||||
|
* This shader imitates the GameBoy Color subpixel
|
||||||
|
* arrangement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
varying vec2 texCoord;
|
||||||
|
uniform sampler2D tex;
|
||||||
|
uniform vec2 texSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a base color to everything.
|
||||||
|
* Lower values make black colors darker.
|
||||||
|
* Higher values make black colors lighter.
|
||||||
|
* You'll normally want each of these numbers to be close
|
||||||
|
* to 0, and not normally higher than 1.
|
||||||
|
*/
|
||||||
|
uniform vec3 BaseColor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifies the contrast or saturation of the image.
|
||||||
|
* Lower values make the image more gray and higher values
|
||||||
|
* make it more colorful.
|
||||||
|
* A value of 1 represents a normal, baseline level of
|
||||||
|
* contrast.
|
||||||
|
* You'll normally want this to be somewhere around 1.
|
||||||
|
*/
|
||||||
|
uniform float SourceContrast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifies the luminosity of the image.
|
||||||
|
* Lower values make the image darker and higher values make
|
||||||
|
* it lighter.
|
||||||
|
* A value of 1 represents normal, baseline luminosity.
|
||||||
|
* You'll normally want this to be somewhere around 1.
|
||||||
|
*/
|
||||||
|
uniform float SourceLuminosity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lower values look more like a sharp, unshaded image.
|
||||||
|
* Higher values look more like an LCD display with subpixels.
|
||||||
|
* You'll normally want this to be a number from 0 to 1.
|
||||||
|
*/
|
||||||
|
uniform float SubpixelBlendAmount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lower values make subpixels darker.
|
||||||
|
* Higher values make them lighter and over-bright.
|
||||||
|
* A value of 1 represents a normal, baseline gamma value.
|
||||||
|
* You'll normally want this to be somewhere around 1.
|
||||||
|
*/
|
||||||
|
uniform float SubpixelGamma;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Higher values allow subpixels to be more blended-together
|
||||||
|
* and brighter.
|
||||||
|
* Lower values keep subpixel colors more separated.
|
||||||
|
* You'll normally want this to be a number from 0 to 1.
|
||||||
|
*/
|
||||||
|
uniform float SubpixelColorBleed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the distance between subpixels.
|
||||||
|
* Lower values put the red, green, and blue subpixels
|
||||||
|
* within a single pixel closer together.
|
||||||
|
* Higher values put them farther apart.
|
||||||
|
* You'll normally want this to be a number from 0 to 1.
|
||||||
|
*/
|
||||||
|
uniform float SubpixelSpread;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the vertical offset of subpixels within
|
||||||
|
* a pixel.
|
||||||
|
* Lower values put the red, green, and blue subpixels
|
||||||
|
* within a single pixel higher up.
|
||||||
|
* Higher values put them further down.
|
||||||
|
* You'll normally want this to be a number from 0 to 1.
|
||||||
|
*/
|
||||||
|
uniform float SubpixelVerticalOffset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lower values make the subpixels horizontally thinner,
|
||||||
|
* and higher values make them thicker.
|
||||||
|
* You'll normally want this to be a number from 0 to 1.
|
||||||
|
*/
|
||||||
|
uniform float SubpixelLightWidth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lower values make the subpixels vertically taller,
|
||||||
|
* and higher values make them shorter.
|
||||||
|
* You'll normally want this to be a number from 0 to 1.
|
||||||
|
*/
|
||||||
|
uniform float SubpixelLightHeight;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lower values make the subpixels sharper and more
|
||||||
|
* individually distinct.
|
||||||
|
* Higher values add an increasingly intense glowing
|
||||||
|
* effect around each subpixel.
|
||||||
|
* You'll normally want this to be a number from 0 to 1.
|
||||||
|
*/
|
||||||
|
uniform float SubpixelLightGlow;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scale the size of pixels up or down.
|
||||||
|
* Useful for looking at larger than 8x8 subpixel sizes.
|
||||||
|
* You'll normally want this number to be exactly 1,
|
||||||
|
* meaning that every group of 3 subpixels corresponds
|
||||||
|
* to one pixel in the display.
|
||||||
|
*/
|
||||||
|
uniform float SubpixelScale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GBC subpixels are roughly rectangular shaped, but
|
||||||
|
* with a rectangular gap in the lower-right corner.
|
||||||
|
* Lower values make the lower-right gap in each GBC
|
||||||
|
* subpixel less distinct. A value of 0 results in no
|
||||||
|
* gap being shown at all.
|
||||||
|
* Higher values make the gap more distinct.
|
||||||
|
* You'll normally want this to be a number from 0 to 1.
|
||||||
|
*/
|
||||||
|
uniform float SubpixelTabHeight;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The following three uniforms decide the base colors
|
||||||
|
* of each of the subpixels.
|
||||||
|
*
|
||||||
|
* Default subpixel colors are based on this resource:
|
||||||
|
* https://gbcc.dev/technology/
|
||||||
|
* R: #FF7145 (1.00, 0.44, 0.27)
|
||||||
|
* G: #C1D650 (0.75, 0.84, 0.31)
|
||||||
|
* B: #3BCEFF (0.23, 0.81, 1.00)
|
||||||
|
*/
|
||||||
|
uniform vec3 SubpixelColorRed; // vec3(1.00, 0.38, 0.22);
|
||||||
|
uniform vec3 SubpixelColorGreen; // vec3(0.60, 0.88, 0.30);
|
||||||
|
uniform vec3 SubpixelColorBlue; // vec3(0.23, 0.65, 1.00);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to get luminosity of an RGB color.
|
||||||
|
* Used with HCL color space related code.
|
||||||
|
*/
|
||||||
|
float getColorLumosity(in vec3 rgb) {
|
||||||
|
return (
|
||||||
|
(rgb.r * (5.0 / 16.0)) +
|
||||||
|
(rgb.g * (9.0 / 16.0)) +
|
||||||
|
(rgb.b * (2.0 / 16.0))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to convert RGB color to HCL. (Hue, Chroma, Luma)
|
||||||
|
*/
|
||||||
|
vec3 convertRgbToHcl(in vec3 rgb) {
|
||||||
|
float xMin = min(rgb.r, min(rgb.g, rgb.b));
|
||||||
|
float xMax = max(rgb.r, max(rgb.g, rgb.b));
|
||||||
|
float c = xMax - xMin;
|
||||||
|
float l = getColorLumosity(rgb);
|
||||||
|
float h = mod((
|
||||||
|
c == 0 ? 0.0 :
|
||||||
|
xMax == rgb.r ? ((rgb.g - rgb.b) / c) :
|
||||||
|
xMax == rgb.g ? ((rgb.b - rgb.r) / c) + 2.0 :
|
||||||
|
xMax == rgb.b ? ((rgb.r - rgb.g) / c) + 4.0 :
|
||||||
|
0.0
|
||||||
|
), 6.0);
|
||||||
|
return vec3(h, c, l);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to convert HCL color to RGB. (Hue, Chroma, Luma)
|
||||||
|
*/
|
||||||
|
vec3 convertHclToRgb(in vec3 hcl) {
|
||||||
|
vec3 rgb;
|
||||||
|
float h = mod(hcl.x, 6.0);
|
||||||
|
float c = hcl.y;
|
||||||
|
float l = hcl.z;
|
||||||
|
float x = c * (1.0 - abs(mod(h, 2.0) - 1.0));
|
||||||
|
if(h <= 1.0) {
|
||||||
|
rgb = vec3(c, x, 0.0);
|
||||||
|
}
|
||||||
|
else if(h <= 2.0) {
|
||||||
|
rgb = vec3(x, c, 0.0);
|
||||||
|
}
|
||||||
|
else if(h <= 3.0) {
|
||||||
|
rgb = vec3(0.0, c, x);
|
||||||
|
}
|
||||||
|
else if(h <= 4.0) {
|
||||||
|
rgb = vec3(0.0, x, c);
|
||||||
|
}
|
||||||
|
else if(h <= 5.0) {
|
||||||
|
rgb = vec3(x, 0.0, c);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rgb = vec3(c, 0.0, x);
|
||||||
|
}
|
||||||
|
float lRgb = getColorLumosity(rgb);
|
||||||
|
float m = l - lRgb;
|
||||||
|
return clamp(vec3(m, m, m) + rgb, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to check if a point is contained within
|
||||||
|
* a rectangular area.
|
||||||
|
*/
|
||||||
|
bool getPointInRect(
|
||||||
|
vec2 point,
|
||||||
|
vec2 rectTopLeft,
|
||||||
|
vec2 rectBottomRight
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
point.x >= rectTopLeft.x &&
|
||||||
|
point.y >= rectTopLeft.y &&
|
||||||
|
point.x <= rectBottomRight.x &&
|
||||||
|
point.y <= rectBottomRight.y
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to get the nearest offset vector from a
|
||||||
|
* point to a line segment.
|
||||||
|
* (The length of this offset vector is the nearest
|
||||||
|
* distance from the point to the line segment.)
|
||||||
|
* Thank you to https://stackoverflow.com/a/1501725
|
||||||
|
*/
|
||||||
|
vec2 getPointLineDistance(
|
||||||
|
vec2 point,
|
||||||
|
vec2 line0,
|
||||||
|
vec2 line1
|
||||||
|
) {
|
||||||
|
vec2 lineDelta = line0 - line1;
|
||||||
|
float lineLengthSq = dot(lineDelta, lineDelta);
|
||||||
|
if(lineLengthSq <= 0) {
|
||||||
|
return line0 - point;
|
||||||
|
}
|
||||||
|
float t = (
|
||||||
|
dot(point - line0, line1 - line0) / lineLengthSq
|
||||||
|
);
|
||||||
|
vec2 projection = (
|
||||||
|
line0 + clamp(t, 0.0, 1.0) * (line1 - line0)
|
||||||
|
);
|
||||||
|
return projection - point;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to get the nearest offset vector from a
|
||||||
|
* point to a rectangle.
|
||||||
|
* Returns (0, 0) for points within the rectangle.
|
||||||
|
*/
|
||||||
|
vec2 getPointRectDistance(
|
||||||
|
vec2 point,
|
||||||
|
vec2 rectTopLeft,
|
||||||
|
vec2 rectBottomRight
|
||||||
|
) {
|
||||||
|
if(getPointInRect(point, rectTopLeft, rectBottomRight)) {
|
||||||
|
return vec2(0.0, 0.0);
|
||||||
|
}
|
||||||
|
vec2 rectTopRight = vec2(rectBottomRight.x, rectTopLeft.y);
|
||||||
|
vec2 rectBottomLeft = vec2(rectTopLeft.x, rectBottomRight.y);
|
||||||
|
vec2 v0 = getPointLineDistance(point, rectTopLeft, rectTopRight);
|
||||||
|
vec2 v1 = getPointLineDistance(point, rectBottomLeft, rectBottomRight);
|
||||||
|
vec2 v2 = getPointLineDistance(point, rectTopLeft, rectBottomLeft);
|
||||||
|
vec2 v3 = getPointLineDistance(point, rectTopRight, rectBottomRight);
|
||||||
|
float v0LengthSq = dot(v0, v0);
|
||||||
|
float v1LengthSq = dot(v1, v1);
|
||||||
|
float v2LengthSq = dot(v2, v2);
|
||||||
|
float v3LengthSq = dot(v3, v3);
|
||||||
|
float minLengthSq = min(
|
||||||
|
min(v0LengthSq, v1LengthSq),
|
||||||
|
min(v2LengthSq, v3LengthSq)
|
||||||
|
);
|
||||||
|
if(minLengthSq == v0LengthSq) {
|
||||||
|
return v0;
|
||||||
|
}
|
||||||
|
else if(minLengthSq == v1LengthSq) {
|
||||||
|
return v1;
|
||||||
|
}
|
||||||
|
else if(minLengthSq == v2LengthSq) {
|
||||||
|
return v2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return v3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to get the nearest offset vector from a
|
||||||
|
* point to a subpixel.
|
||||||
|
* GBC subpixels are roughly rectangular in shape,
|
||||||
|
* but have a rectangular gap in their bottom-left
|
||||||
|
* corner.
|
||||||
|
* Returns (0, 0) for points within the subpixel.
|
||||||
|
*/
|
||||||
|
vec2 getPointSubpixelDistance(
|
||||||
|
vec2 point,
|
||||||
|
vec2 subpixelCenter,
|
||||||
|
vec2 subpixelSizeHalf
|
||||||
|
) {
|
||||||
|
float rectLeft = subpixelCenter.x - subpixelSizeHalf.x;
|
||||||
|
float rectRight = subpixelCenter.x + subpixelSizeHalf.x;
|
||||||
|
float rectTop = subpixelCenter.y - subpixelSizeHalf.y;
|
||||||
|
float rectBottom = subpixelCenter.y + subpixelSizeHalf.y;
|
||||||
|
vec2 offsetLeft = getPointRectDistance(
|
||||||
|
point,
|
||||||
|
vec2(rectLeft, rectTop + SubpixelTabHeight),
|
||||||
|
vec2(subpixelCenter.x, rectBottom)
|
||||||
|
);
|
||||||
|
vec2 offsetRight = getPointRectDistance(
|
||||||
|
point,
|
||||||
|
vec2(subpixelCenter.x, rectTop),
|
||||||
|
vec2(rectRight, rectBottom)
|
||||||
|
);
|
||||||
|
float offsetLeftLengthSq = dot(offsetLeft, offsetLeft);
|
||||||
|
float offsetRightLengthSq = dot(offsetRight, offsetRight);
|
||||||
|
if(offsetLeftLengthSq <= offsetRightLengthSq) {
|
||||||
|
return offsetLeft;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return offsetRight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to get the intensity of light from a
|
||||||
|
* subpixel.
|
||||||
|
* The pixelPosition argument represents a
|
||||||
|
* fragment's position within a pixel.
|
||||||
|
* Spread represents the subpixel's horizontal
|
||||||
|
* position within the pixel.
|
||||||
|
*/
|
||||||
|
float getSubpixelIntensity(
|
||||||
|
vec2 pixelPosition,
|
||||||
|
float spread
|
||||||
|
) {
|
||||||
|
vec2 subpixelCenter = vec2(
|
||||||
|
0.5 + (spread * SubpixelSpread),
|
||||||
|
1.0 - SubpixelVerticalOffset
|
||||||
|
);
|
||||||
|
vec2 subpixelSizeHalf = 0.5 * vec2(
|
||||||
|
SubpixelLightWidth,
|
||||||
|
SubpixelLightHeight
|
||||||
|
);
|
||||||
|
vec2 offset = getPointSubpixelDistance(
|
||||||
|
pixelPosition,
|
||||||
|
subpixelCenter,
|
||||||
|
subpixelSizeHalf
|
||||||
|
);
|
||||||
|
if(SubpixelLightGlow <= 0) {
|
||||||
|
return dot(offset, offset) <= 0.0 ? 1.0 : 0.0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float dist = length(offset);
|
||||||
|
float glow = max(0.0,
|
||||||
|
1.0 - (dist / SubpixelLightGlow)
|
||||||
|
);
|
||||||
|
return glow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to apply SubpixelColorBleed to the intensity
|
||||||
|
* value computed for a fragment and subpixel.
|
||||||
|
* Subpixel color bleed allows subpixel colors to be
|
||||||
|
* more strongly coerced to more accurately represent
|
||||||
|
* the underlying pixel color.
|
||||||
|
*/
|
||||||
|
float applySubpixelBleed(
|
||||||
|
float subpixelIntensity,
|
||||||
|
float colorSourceChannel
|
||||||
|
) {
|
||||||
|
return subpixelIntensity * (
|
||||||
|
SubpixelColorBleed +
|
||||||
|
((1.0 - SubpixelColorBleed) * colorSourceChannel)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// Get base color of the pixel, adjust based on
|
||||||
|
// contrast and luminosity settings.
|
||||||
|
vec3 colorSource = texture2D(tex, texCoord).rgb;
|
||||||
|
vec3 colorSourceHcl = convertRgbToHcl(colorSource);
|
||||||
|
vec3 colorSourceAdjusted = convertHclToRgb(vec3(
|
||||||
|
colorSourceHcl.x,
|
||||||
|
colorSourceHcl.y * SourceContrast,
|
||||||
|
colorSourceHcl.z * SourceLuminosity
|
||||||
|
));
|
||||||
|
// Determine how much each subpixel's light should
|
||||||
|
// affect this fragment.
|
||||||
|
vec2 pixelPosition = (
|
||||||
|
mod(texCoord * texSize * SubpixelScale, 1.0)
|
||||||
|
);
|
||||||
|
float subpixelIntensityRed = applySubpixelBleed(
|
||||||
|
getSubpixelIntensity(pixelPosition, -1.0),
|
||||||
|
colorSourceAdjusted.r
|
||||||
|
);
|
||||||
|
float subpixelIntensityGreen = applySubpixelBleed(
|
||||||
|
getSubpixelIntensity(pixelPosition, +0.0),
|
||||||
|
colorSourceAdjusted.g
|
||||||
|
);
|
||||||
|
float subpixelIntensityBlue = applySubpixelBleed(
|
||||||
|
getSubpixelIntensity(pixelPosition, +1.0),
|
||||||
|
colorSourceAdjusted.b
|
||||||
|
);
|
||||||
|
vec3 subpixelLightColor = SubpixelGamma * (
|
||||||
|
(subpixelIntensityRed * SubpixelColorRed) +
|
||||||
|
(subpixelIntensityGreen * SubpixelColorGreen) +
|
||||||
|
(subpixelIntensityBlue * SubpixelColorBlue)
|
||||||
|
);
|
||||||
|
// Compute final color
|
||||||
|
vec3 colorResult = clamp(
|
||||||
|
subpixelLightColor * colorSourceAdjusted, 0.0, 1.0
|
||||||
|
);
|
||||||
|
vec3 colorResultBlended = (
|
||||||
|
((1.0 - SubpixelBlendAmount) * colorSourceAdjusted) +
|
||||||
|
(SubpixelBlendAmount * colorResult)
|
||||||
|
);
|
||||||
|
colorResultBlended = BaseColor + (
|
||||||
|
(colorResultBlended * (vec3(1.0, 1.0, 1.0) - BaseColor))
|
||||||
|
);
|
||||||
|
gl_FragColor = vec4(
|
||||||
|
colorResultBlended,
|
||||||
|
1.0
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
distribute this software, either in source code form or as a compiled
|
||||||
|
binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
means.
|
||||||
|
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
of this software dedicate any and all copyright interest in the
|
||||||
|
software to the public domain. We make this dedication for the benefit
|
||||||
|
of the public at large and to the detriment of our heirs and
|
||||||
|
successors. We intend this dedication to be an overt act of
|
||||||
|
relinquishment in perpetuity of all present and future rights to this
|
||||||
|
software under copyright law.
|
||||||
|
|
||||||
|
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 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.
|
||||||
|
|
||||||
|
For more information, please refer to <http://unlicense.org/>
|
|
@ -0,0 +1,129 @@
|
||||||
|
[shader]
|
||||||
|
name=gbc-lcd
|
||||||
|
author=Sophie Kirschner
|
||||||
|
description=Imitates the GameBoy Color LCD screen subpixel arrangement, with an optional backlight effect.
|
||||||
|
passes=2
|
||||||
|
|
||||||
|
[pass.0]
|
||||||
|
integerScaling=1
|
||||||
|
fragmentShader=gbc-lcd.fs
|
||||||
|
blend=1
|
||||||
|
|
||||||
|
[pass.1]
|
||||||
|
fragmentShader=gbc-lcd-light.fs
|
||||||
|
|
||||||
|
[pass.0.uniform.BaseColor]
|
||||||
|
type=float3
|
||||||
|
default[0]=0.130
|
||||||
|
default[1]=0.128
|
||||||
|
default[2]=0.101
|
||||||
|
readableName=Screen base color
|
||||||
|
|
||||||
|
[pass.0.uniform.SubpixelColorRed]
|
||||||
|
type=float3
|
||||||
|
default[0]=1.00
|
||||||
|
default[1]=0.38
|
||||||
|
default[2]=0.22
|
||||||
|
readableName=Red subpixel color
|
||||||
|
|
||||||
|
[pass.0.uniform.SubpixelColorGreen]
|
||||||
|
type=float3
|
||||||
|
default[0]=0.60
|
||||||
|
default[1]=0.88
|
||||||
|
default[2]=0.30
|
||||||
|
readableName=Green subpixel color
|
||||||
|
|
||||||
|
[pass.0.uniform.SubpixelColorBlue]
|
||||||
|
type=float3
|
||||||
|
default[0]=0.23
|
||||||
|
default[1]=0.65
|
||||||
|
default[2]=1.00
|
||||||
|
readableName=Blue subpixel color
|
||||||
|
|
||||||
|
[pass.0.uniform.SourceContrast]
|
||||||
|
type=float
|
||||||
|
default=0.85
|
||||||
|
readableName=Screen contrast
|
||||||
|
|
||||||
|
[pass.0.uniform.SourceLuminosity]
|
||||||
|
type=float
|
||||||
|
default=0.88
|
||||||
|
readableName=Screen luminosity
|
||||||
|
|
||||||
|
[pass.0.uniform.SubpixelBlendAmount]
|
||||||
|
type=float
|
||||||
|
default=1.0
|
||||||
|
readableName=Subpixel effect amount
|
||||||
|
|
||||||
|
[pass.0.uniform.SubpixelColorBleed]
|
||||||
|
type=float
|
||||||
|
default=0.31
|
||||||
|
readableName=Subpixel color bleeding
|
||||||
|
|
||||||
|
[pass.0.uniform.SubpixelSpread]
|
||||||
|
type=float
|
||||||
|
default=0.333
|
||||||
|
readableName=Subpixel spread X
|
||||||
|
|
||||||
|
[pass.0.uniform.SubpixelVerticalOffset]
|
||||||
|
type=float
|
||||||
|
default=0.48
|
||||||
|
readableName=Subpixel offset Y
|
||||||
|
|
||||||
|
[pass.0.uniform.SubpixelGamma]
|
||||||
|
type=float
|
||||||
|
default=1.040
|
||||||
|
readableName=Subpixel brightness
|
||||||
|
|
||||||
|
[pass.0.uniform.SubpixelLightWidth]
|
||||||
|
type=float
|
||||||
|
default=0.220
|
||||||
|
readableName=Subpixel width
|
||||||
|
|
||||||
|
[pass.0.uniform.SubpixelLightHeight]
|
||||||
|
type=float
|
||||||
|
default=0.850
|
||||||
|
readableName=Subpixel height
|
||||||
|
|
||||||
|
[pass.0.uniform.SubpixelLightGlow]
|
||||||
|
type=float
|
||||||
|
default=0.195
|
||||||
|
readableName=Subpixel glow size
|
||||||
|
|
||||||
|
[pass.0.uniform.SubpixelTabHeight]
|
||||||
|
type=float
|
||||||
|
default=0.175
|
||||||
|
readableName=Subpixel tab shaping
|
||||||
|
|
||||||
|
[pass.0.uniform.SubpixelScale]
|
||||||
|
type=float
|
||||||
|
default=1.0
|
||||||
|
readableName=Subpixel scale
|
||||||
|
|
||||||
|
[pass.1.uniform.LightColor]
|
||||||
|
type=float3
|
||||||
|
default[0]=1.000
|
||||||
|
default[1]=0.968
|
||||||
|
default[2]=0.882
|
||||||
|
readableName=Backlight color
|
||||||
|
|
||||||
|
[pass.1.uniform.LightIntensity]
|
||||||
|
type=float
|
||||||
|
default=0.06
|
||||||
|
readableName=Backlight intensity
|
||||||
|
|
||||||
|
[pass.1.uniform.LightSoftness]
|
||||||
|
type=float
|
||||||
|
default=0.67
|
||||||
|
readableName=Backlight softness
|
||||||
|
|
||||||
|
[pass.1.uniform.ReflectionDistance]
|
||||||
|
type=float2
|
||||||
|
default[0]=0
|
||||||
|
default[1]=0.025
|
||||||
|
readableName=Internal reflection distance
|
||||||
|
|
||||||
|
[pass.1.uniform.ReflectionBrightness]
|
||||||
|
type=float
|
||||||
|
default=0.032
|
||||||
|
readableName=Internal reflection brightness
|
Loading…
Reference in New Issue