Work with WebGL 1.0 and WebGL 2.0

This commit is contained in:
Maximilian Mader 2019-06-11 04:21:32 +02:00
parent f9c932b737
commit c2432b7348
Signed by: Max
GPG Key ID: F71D56A3151C4FB3
12 changed files with 557 additions and 143 deletions

View File

@ -7,36 +7,36 @@
STATIC vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 output_resolution)
{
/* Curve and pixel ratio */
float y_curve = cos(position.x - 0.5) * CURVENESS + (1 - CURVENESS);
float y_curve = cos(position.x - 0.5) * CURVENESS + (1.0 - CURVENESS);
float y_multiplier = 8.0 / 7.0 / y_curve;
position.y *= y_multiplier;
position.y -= (y_multiplier - 1) / 2;
position.y -= (y_multiplier - 1.0) / 2.0;
if (position.y < 0.0) return vec4(0,0,0,0);
if (position.y > 1.0) return vec4(0,0,0,0);
float x_curve = cos(position.y - 0.5) * CURVENESS + (1 - CURVENESS);
float x_multiplier = 1/x_curve;
float x_curve = cos(position.y - 0.5) * CURVENESS + (1.0 - CURVENESS);
float x_multiplier = 1.0 / x_curve;
position.x *= x_multiplier;
position.x -= (x_multiplier - 1) / 2;
if (position.x < 0.0) return vec4(0,0,0,0);
if (position.x > 1.0) return vec4(0,0,0,0);
position.x -= (x_multiplier - 1.0) / 2.0;
if (position.x < 0.0) return vec4(0.0, 0.0, 0.0, 0.0);
if (position.x > 1.0) return vec4(0.0, 0.0, 0.0, 0.0);
/* Setting up common vars */
vec2 pos = fract(position * input_resolution);
vec2 sub_pos = fract(position * input_resolution * 6);
vec2 sub_pos = fract(position * input_resolution * 6.0);
vec4 center = texture(image, position);
vec4 left = texture(image, position - vec2(1.0 / input_resolution.x, 0));
vec4 right = texture(image, position + vec2(1.0 / input_resolution.x, 0));
vec4 left = texture(image, position - vec2(1.0 / input_resolution.x, 0.0));
vec4 right = texture(image, position + vec2(1.0 / input_resolution.x, 0.0));
/* Vertical blurring */
if (pos.y < 1.0 / 6.0) {
center = mix(center, texture(image, position + vec2(0, -1.0 / input_resolution.y)), 0.5 - sub_pos.y / 2.0);
center = mix(center, texture(image, position + vec2(0.0, -1.0 / input_resolution.y)), 0.5 - sub_pos.y / 2.0);
left = mix(left, texture(image, position + vec2(-1.0 / input_resolution.x, -1.0 / input_resolution.y)), 0.5 - sub_pos.y / 2.0);
right = mix(right, texture(image, position + vec2( 1.0 / input_resolution.x, -1.0 / input_resolution.y)), 0.5 - sub_pos.y / 2.0);
}
else if (pos.y > 5.0 / 6.0) {
center = mix(center, texture(image, position + vec2(0, 1.0 / input_resolution.y)), sub_pos.y / 2.0);
center = mix(center, texture(image, position + vec2(0.0, 1.0 / input_resolution.y)), sub_pos.y / 2.0);
left = mix(left, texture(image, position + vec2(-1.0 / input_resolution.x, 1.0 / input_resolution.y)), sub_pos.y / 2.0);
right = mix(right, texture(image, position + vec2( 1.0 / input_resolution.x, 1.0 / input_resolution.y)), sub_pos.y / 2.0);
}
@ -44,10 +44,10 @@ STATIC vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 ou
/* Scanlines */
float scanline_multiplier;
if (pos.y < 0.5) {
scanline_multiplier = (pos.y * 2) * SCANLINE_DEPTH + (1 - SCANLINE_DEPTH);
scanline_multiplier = (pos.y * 2.0) * SCANLINE_DEPTH + (1.0 - SCANLINE_DEPTH);
}
else {
scanline_multiplier = ((1 - pos.y) * 2) * SCANLINE_DEPTH + (1 - SCANLINE_DEPTH);
scanline_multiplier = ((1.0 - pos.y) * 2.0) * SCANLINE_DEPTH + (1.0 - SCANLINE_DEPTH);
}
center *= scanline_multiplier;
@ -55,7 +55,7 @@ STATIC vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 ou
right *= scanline_multiplier;
/* Vertical seperator for shadow masks */
bool odd = bool(int((position * input_resolution).x) & 1);
bool odd = bool(int((position * input_resolution).x) >= 1);
if (odd) {
pos.y += 0.5;
pos.y = fract(pos.y);
@ -63,15 +63,15 @@ STATIC vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 ou
if (pos.y < 1.0 / 3.0) {
float gradient_position = pos.y * 3.0;
center *= gradient_position * VERTICAL_BORDER_DEPTH + (1 - VERTICAL_BORDER_DEPTH);
left *= gradient_position * VERTICAL_BORDER_DEPTH + (1 - VERTICAL_BORDER_DEPTH);
right *= gradient_position * VERTICAL_BORDER_DEPTH + (1 - VERTICAL_BORDER_DEPTH);
center *= gradient_position * VERTICAL_BORDER_DEPTH + (1.0 - VERTICAL_BORDER_DEPTH);
left *= gradient_position * VERTICAL_BORDER_DEPTH + (1.0 - VERTICAL_BORDER_DEPTH);
right *= gradient_position * VERTICAL_BORDER_DEPTH + (1.0 - VERTICAL_BORDER_DEPTH);
}
else if (pos.y > 2.0 / 3.0) {
float gradient_position = (1 - pos.y) * 3.0;
center *= gradient_position * VERTICAL_BORDER_DEPTH + (1 - VERTICAL_BORDER_DEPTH);
left *= gradient_position * VERTICAL_BORDER_DEPTH + (1 - VERTICAL_BORDER_DEPTH);
right *= gradient_position * VERTICAL_BORDER_DEPTH + (1 - VERTICAL_BORDER_DEPTH);
float gradient_position = (1.0 - pos.y) * 3.0;
center *= gradient_position * VERTICAL_BORDER_DEPTH + (1.0 - VERTICAL_BORDER_DEPTH);
left *= gradient_position * VERTICAL_BORDER_DEPTH + (1.0 - VERTICAL_BORDER_DEPTH);
right *= gradient_position * VERTICAL_BORDER_DEPTH + (1.0 - VERTICAL_BORDER_DEPTH);
}
/* Blur the edges of the separators of adjacent columns */
@ -82,26 +82,26 @@ STATIC vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 ou
if (pos.y < 1.0 / 3.0) {
float gradient_position = pos.y * 3.0;
if (pos.x < 0.5) {
gradient_position = 1 - (1 - gradient_position) * (1 - (pos.x) * 6.0);
gradient_position = 1.0 - (1.0 - gradient_position) * (1.0 - (pos.x) * 6.0);
}
else {
gradient_position = 1 - (1 - gradient_position) * (1 - (1 - pos.x) * 6.0);
gradient_position = 1.0 - (1.0 - gradient_position) * (1.0 - (1.0 - pos.x) * 6.0);
}
center *= gradient_position * VERTICAL_BORDER_DEPTH + (1 - VERTICAL_BORDER_DEPTH);
left *= gradient_position * VERTICAL_BORDER_DEPTH + (1 - VERTICAL_BORDER_DEPTH);
right *= gradient_position * VERTICAL_BORDER_DEPTH + (1 - VERTICAL_BORDER_DEPTH);
center *= gradient_position * VERTICAL_BORDER_DEPTH + (1.0 - VERTICAL_BORDER_DEPTH);
left *= gradient_position * VERTICAL_BORDER_DEPTH + (1.0 - VERTICAL_BORDER_DEPTH);
right *= gradient_position * VERTICAL_BORDER_DEPTH + (1.0 - VERTICAL_BORDER_DEPTH);
}
else if (pos.y > 2.0 / 3.0) {
float gradient_position = (1 - pos.y) * 3.0;
float gradient_position = (1.0 - pos.y) * 3.0;
if (pos.x < 0.5) {
gradient_position = 1 - (1 - gradient_position) * (1 - (pos.x) * 6.0);
gradient_position = 1.0 - (1.0 - gradient_position) * (1.0 - (pos.x) * 6.0);
}
else {
gradient_position = 1 - (1 - gradient_position) * (1 - (1 - pos.x) * 6.0);
gradient_position = 1.0 - (1.0 - gradient_position) * (1.0 - (1.0 - pos.x) * 6.0);
}
center *= gradient_position * VERTICAL_BORDER_DEPTH + (1 - VERTICAL_BORDER_DEPTH);
left *= gradient_position * VERTICAL_BORDER_DEPTH + (1 - VERTICAL_BORDER_DEPTH);
right *= gradient_position * VERTICAL_BORDER_DEPTH + (1 - VERTICAL_BORDER_DEPTH);
center *= gradient_position * VERTICAL_BORDER_DEPTH + (1.0 - VERTICAL_BORDER_DEPTH);
left *= gradient_position * VERTICAL_BORDER_DEPTH + (1.0 - VERTICAL_BORDER_DEPTH);
right *= gradient_position * VERTICAL_BORDER_DEPTH + (1.0 - VERTICAL_BORDER_DEPTH);
}
}
@ -113,48 +113,48 @@ STATIC vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 ou
vec4 ret;
if (pos.x < 1.0 / 6.0) {
ret = mix(vec4(COLOR_HIGH * center.r, COLOR_LOW * center.g, COLOR_HIGH * left.b, 1),
vec4(COLOR_HIGH * center.r, COLOR_LOW * center.g, COLOR_LOW * left.b, 1),
ret = mix(vec4(COLOR_HIGH * center.r, COLOR_LOW * center.g, COLOR_HIGH * left.b, 1.0),
vec4(COLOR_HIGH * center.r, COLOR_LOW * center.g, COLOR_LOW * left.b, 1.0),
sub_pos.x);
}
else if (pos.x < 2.0 / 6.0) {
ret = mix(vec4(COLOR_HIGH * center.r, COLOR_LOW * center.g, COLOR_LOW * left.b, 1),
vec4(COLOR_HIGH * center.r, COLOR_HIGH * center.g, COLOR_LOW * midleft.b, 1),
ret = mix(vec4(COLOR_HIGH * center.r, COLOR_LOW * center.g, COLOR_LOW * left.b, 1.0),
vec4(COLOR_HIGH * center.r, COLOR_HIGH * center.g, COLOR_LOW * midleft.b, 1.0),
sub_pos.x);
}
else if (pos.x < 3.0 / 6.0) {
ret = mix(vec4(COLOR_HIGH * center.r , COLOR_HIGH * center.g, COLOR_LOW * midleft.b, 1),
vec4(COLOR_LOW * midright.r, COLOR_HIGH * center.g, COLOR_LOW * center.b, 1),
ret = mix(vec4(COLOR_HIGH * center.r , COLOR_HIGH * center.g, COLOR_LOW * midleft.b, 1.0),
vec4(COLOR_LOW * midright.r, COLOR_HIGH * center.g, COLOR_LOW * center.b, 1.0),
sub_pos.x);
}
else if (pos.x < 4.0 / 6.0) {
ret = mix(vec4(COLOR_LOW * midright.r, COLOR_HIGH * center.g , COLOR_LOW * center.b, 1),
vec4(COLOR_LOW * right.r , COLOR_HIGH * center.g, COLOR_HIGH * center.b, 1),
ret = mix(vec4(COLOR_LOW * midright.r, COLOR_HIGH * center.g , COLOR_LOW * center.b, 1.0),
vec4(COLOR_LOW * right.r , COLOR_HIGH * center.g, COLOR_HIGH * center.b, 1.0),
sub_pos.x);
}
else if (pos.x < 5.0 / 6.0) {
ret = mix(vec4(COLOR_LOW * right.r, COLOR_HIGH * center.g , COLOR_HIGH * center.b, 1),
vec4(COLOR_LOW * right.r, COLOR_LOW * midright.g, COLOR_HIGH * center.b, 1),
ret = mix(vec4(COLOR_LOW * right.r, COLOR_HIGH * center.g , COLOR_HIGH * center.b, 1.0),
vec4(COLOR_LOW * right.r, COLOR_LOW * midright.g, COLOR_HIGH * center.b, 1.0),
sub_pos.x);
}
else {
ret = mix(vec4(COLOR_LOW * right.r, COLOR_LOW * midright.g, COLOR_HIGH * center.b, 1),
vec4(COLOR_HIGH * right.r, COLOR_LOW * right.g , COLOR_HIGH * center.b, 1),
ret = mix(vec4(COLOR_LOW * right.r, COLOR_LOW * midright.g, COLOR_HIGH * center.b, 1.0),
vec4(COLOR_HIGH * right.r, COLOR_LOW * right.g , COLOR_HIGH * center.b, 1.0),
sub_pos.x);
}
/* Anti alias the curve */
vec2 pixel_position = position * output_resolution;
if (pixel_position.x < 1) {
if (pixel_position.x < 1.0) {
ret *= pixel_position.x;
}
else if (pixel_position.x > output_resolution.x - 1) {
else if (pixel_position.x > output_resolution.x - 1.0) {
ret *= output_resolution.x - pixel_position.x;
}
if (pixel_position.y < 1) {
if (pixel_position.y < 1.0) {
ret *= pixel_position.y;
}
else if (pixel_position.y > output_resolution.y - 1) {
else if (pixel_position.y > output_resolution.y - 1.0) {
ret *= output_resolution.y - pixel_position.y;
}

View File

@ -2,7 +2,7 @@
/* The colorspace used by the HQnx filters is not really YUV, despite the algorithm description claims it is. It is
also not normalized. Therefore, we shall call the colorspace used by HQnx "HQ Colorspace" to avoid confusion. */
STATIC vec3 rgb_to_hq_colospace(vec4 rgb)
STATIC vec3 rgb_to_hq_colorspace(vec4 rgb)
{
return vec3( 0.250 * rgb.r + 0.250 * rgb.g + 0.250 * rgb.b,
0.250 * rgb.r - 0.000 * rgb.g - 0.250 * rgb.b,
@ -11,7 +11,7 @@ STATIC vec3 rgb_to_hq_colospace(vec4 rgb)
STATIC bool is_different(vec4 a, vec4 b)
{
vec3 diff = abs(rgb_to_hq_colospace(a) - rgb_to_hq_colospace(b));
vec3 diff = abs(rgb_to_hq_colorspace(a) - rgb_to_hq_colorspace(b));
return diff.x > 0.188 || diff.y > 0.027 || diff.z > 0.031;
}
@ -43,13 +43,13 @@ STATIC vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 ou
vec4 w0 = texture(image, position + vec2( -o.x, -o.y));
vec4 w1 = texture(image, position + vec2( 0, -o.y));
vec4 w1 = texture(image, position + vec2( 0.0, -o.y));
vec4 w2 = texture(image, position + vec2( o.x, -o.y));
vec4 w3 = texture(image, position + vec2( -o.x, 0));
vec4 w4 = texture(image, position + vec2( 0, 0));
vec4 w5 = texture(image, position + vec2( o.x, 0));
vec4 w3 = texture(image, position + vec2( -o.x, 0.0));
vec4 w4 = texture(image, position + vec2( 0.0, 0.0));
vec4 w5 = texture(image, position + vec2( o.x, 0.0));
vec4 w6 = texture(image, position + vec2( -o.x, o.y));
vec4 w7 = texture(image, position + vec2( 0, o.y));
vec4 w7 = texture(image, position + vec2( 0.0, o.y));
vec4 w8 = texture(image, position + vec2( o.x, o.y));
int pattern = 0;

View File

@ -5,27 +5,27 @@
STATIC vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 output_resolution)
{
vec2 pos = fract(position * input_resolution);
vec2 sub_pos = fract(position * input_resolution * 6);
vec2 sub_pos = fract(position * input_resolution * 6.0);
vec4 center = texture(image, position);
vec4 left = texture(image, position - vec2(1.0 / input_resolution.x, 0));
vec4 right = texture(image, position + vec2(1.0 / input_resolution.x, 0));
if (pos.y < 1.0 / 6.0) {
center = mix(center, texture(image, position + vec2(0, -1.0 / input_resolution.y)), 0.5 - sub_pos.y / 2.0);
center = mix(center, texture(image, position + vec2(0.0, -1.0 / input_resolution.y)), 0.5 - sub_pos.y / 2.0);
left = mix(left, texture(image, position + vec2(-1.0 / input_resolution.x, -1.0 / input_resolution.y)), 0.5 - sub_pos.y / 2.0);
right = mix(right, texture(image, position + vec2( 1.0 / input_resolution.x, -1.0 / input_resolution.y)), 0.5 - sub_pos.y / 2.0);
center *= sub_pos.y * SCANLINE_DEPTH + (1 - SCANLINE_DEPTH);
left *= sub_pos.y * SCANLINE_DEPTH + (1 - SCANLINE_DEPTH);
right *= sub_pos.y * SCANLINE_DEPTH + (1 - SCANLINE_DEPTH);
center *= sub_pos.y * SCANLINE_DEPTH + (1.0 - SCANLINE_DEPTH);
left *= sub_pos.y * SCANLINE_DEPTH + (1.0 - SCANLINE_DEPTH);
right *= sub_pos.y * SCANLINE_DEPTH + (1.0 - SCANLINE_DEPTH);
}
else if (pos.y > 5.0 / 6.0) {
center = mix(center, texture(image, position + vec2(0, 1.0 / input_resolution.y)), sub_pos.y / 2.0);
center = mix(center, texture(image, position + vec2(0.0, 1.0 / input_resolution.y)), sub_pos.y / 2.0);
left = mix(left, texture(image, position + vec2(-1.0 / input_resolution.x, 1.0 / input_resolution.y)), sub_pos.y / 2.0);
right = mix(right, texture(image, position + vec2( 1.0 / input_resolution.x, 1.0 / input_resolution.y)), sub_pos.y / 2.0);
center *= (1.0 - sub_pos.y) * SCANLINE_DEPTH + (1 - SCANLINE_DEPTH);
left *= (1.0 - sub_pos.y) * SCANLINE_DEPTH + (1 - SCANLINE_DEPTH);
right *= (1.0 - sub_pos.y) * SCANLINE_DEPTH + (1 - SCANLINE_DEPTH);
center *= (1.0 - sub_pos.y) * SCANLINE_DEPTH + (1.0 - SCANLINE_DEPTH);
left *= (1.0 - sub_pos.y) * SCANLINE_DEPTH + (1.0 - SCANLINE_DEPTH);
right *= (1.0 - sub_pos.y) * SCANLINE_DEPTH + (1.0 - SCANLINE_DEPTH);
}
@ -34,33 +34,33 @@ STATIC vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 ou
vec4 ret;
if (pos.x < 1.0 / 6.0) {
ret = mix(vec4(COLOR_HIGH * center.r, COLOR_LOW * center.g, COLOR_HIGH * left.b, 1),
vec4(COLOR_HIGH * center.r, COLOR_LOW * center.g, COLOR_LOW * left.b, 1),
ret = mix(vec4(COLOR_HIGH * center.r, COLOR_LOW * center.g, COLOR_HIGH * left.b, 1.0),
vec4(COLOR_HIGH * center.r, COLOR_LOW * center.g, COLOR_LOW * left.b, 1.0),
sub_pos.x);
}
else if (pos.x < 2.0 / 6.0) {
ret = mix(vec4(COLOR_HIGH * center.r, COLOR_LOW * center.g, COLOR_LOW * left.b, 1),
vec4(COLOR_HIGH * center.r, COLOR_HIGH * center.g, COLOR_LOW * midleft.b, 1),
ret = mix(vec4(COLOR_HIGH * center.r, COLOR_LOW * center.g, COLOR_LOW * left.b, 1.0),
vec4(COLOR_HIGH * center.r, COLOR_HIGH * center.g, COLOR_LOW * midleft.b, 1.0),
sub_pos.x);
}
else if (pos.x < 3.0 / 6.0) {
ret = mix(vec4(COLOR_HIGH * center.r , COLOR_HIGH * center.g, COLOR_LOW * midleft.b, 1),
vec4(COLOR_LOW * midright.r, COLOR_HIGH * center.g, COLOR_LOW * center.b, 1),
ret = mix(vec4(COLOR_HIGH * center.r , COLOR_HIGH * center.g, COLOR_LOW * midleft.b, 1.0),
vec4(COLOR_LOW * midright.r, COLOR_HIGH * center.g, COLOR_LOW * center.b, 1.0),
sub_pos.x);
}
else if (pos.x < 4.0 / 6.0) {
ret = mix(vec4(COLOR_LOW * midright.r, COLOR_HIGH * center.g , COLOR_LOW * center.b, 1),
vec4(COLOR_LOW * right.r , COLOR_HIGH * center.g, COLOR_HIGH * center.b, 1),
ret = mix(vec4(COLOR_LOW * midright.r, COLOR_HIGH * center.g , COLOR_LOW * center.b, 1.0),
vec4(COLOR_LOW * right.r , COLOR_HIGH * center.g, COLOR_HIGH * center.b, 1.0),
sub_pos.x);
}
else if (pos.x < 5.0 / 6.0) {
ret = mix(vec4(COLOR_LOW * right.r, COLOR_HIGH * center.g , COLOR_HIGH * center.b, 1),
vec4(COLOR_LOW * right.r, COLOR_LOW * midright.g, COLOR_HIGH * center.b, 1),
ret = mix(vec4(COLOR_LOW * right.r, COLOR_HIGH * center.g , COLOR_HIGH * center.b, 1.0),
vec4(COLOR_LOW * right.r, COLOR_LOW * midright.g, COLOR_HIGH * center.b, 1.0),
sub_pos.x);
}
else {
ret = mix(vec4(COLOR_LOW * right.r, COLOR_LOW * midright.g, COLOR_HIGH * center.b, 1),
vec4(COLOR_HIGH * right.r, COLOR_LOW * right.g , COLOR_HIGH * center.b, 1),
ret = mix(vec4(COLOR_LOW * right.r, COLOR_LOW * midright.g, COLOR_HIGH * center.b, 1.0),
vec4(COLOR_HIGH * right.r, COLOR_LOW * right.g , COLOR_HIGH * center.b, 1.0),
sub_pos.x);
}

View File

@ -2,9 +2,9 @@
- The actual output calculating was completely redesigned as resolution independent graphic generator. This allows
scaling to any factor.
- HQnx approximations that were good enough for a 2x/3x/4x factor were refined, creating smoother gradients.
- "Quarters" can be interpolated in more ways than in the HQnx filters
- If a pattern does not provide enough information to determine the suitable scaling interpolation, up to 16 pixels
per quarter are sampled (in contrast to the usual 9) in order to determine the best interpolation.
- "Quarters" can be interpolated in more ways than in the HQnx filters
- If a pattern does not provide enough information to determine the suitable scaling interpolation, up to 16 pixels
per quarter are sampled (in contrast to the usual 9) in order to determine the best interpolation.
*/
/* We use the same colorspace as the HQ algorithms. */
@ -28,7 +28,7 @@ STATIC vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 ou
{
// o = offset, the width of a pixel
vec2 o = 1.0 / input_resolution;
/* We always calculate the top left quarter. If we need a different quarter, we flip our co-ordinates */
// p = the position within a pixel [0...1]
@ -44,13 +44,13 @@ STATIC vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 ou
}
vec4 w0 = texture(image, position + vec2( -o.x, -o.y));
vec4 w1 = texture(image, position + vec2( 0, -o.y));
vec4 w1 = texture(image, position + vec2( 0.0, -o.y));
vec4 w2 = texture(image, position + vec2( o.x, -o.y));
vec4 w3 = texture(image, position + vec2( -o.x, 0));
vec4 w4 = texture(image, position + vec2( 0, 0));
vec4 w5 = texture(image, position + vec2( o.x, 0));
vec4 w3 = texture(image, position + vec2( -o.x, 0.0));
vec4 w4 = texture(image, position + vec2( 0.0, 0.0));
vec4 w5 = texture(image, position + vec2( o.x, 0.0));
vec4 w6 = texture(image, position + vec2( -o.x, o.y));
vec4 w7 = texture(image, position + vec2( 0, o.y));
vec4 w7 = texture(image, position + vec2( 0.0, o.y));
vec4 w8 = texture(image, position + vec2( o.x, o.y));
int pattern = 0;
@ -81,7 +81,7 @@ STATIC vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 ou
if (P(0x2f,0x2f)) {
float dist = length(p - vec2(0.5));
float pixel_size = length(1.0 / (output_resolution / input_resolution));
if (dist < 0.5 - pixel_size / 2) {
if (dist < 0.5 - pixel_size / 2.0) {
return w4;
}
vec4 r;
@ -92,40 +92,40 @@ STATIC vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 ou
r = mix(mix(w1 * 0.375 + w0 * 0.25 + w3 * 0.375, w3, p.y * 2.0), w1, p.x * 2.0);
}
if (dist > 0.5 + pixel_size / 2) {
if (dist > 0.5 + pixel_size / 2.0) {
return r;
}
return mix(w4, r, (dist - 0.5 + pixel_size / 2) / pixel_size);
return mix(w4, r, (dist - 0.5 + pixel_size / 2.0) / pixel_size);
}
if (P(0xbf,0x37) || P(0xdb,0x13)) {
float dist = p.x - 2.0 * p.y;
float pixel_size = length(1.0 / (output_resolution / input_resolution)) * sqrt(5.0);
if (dist > pixel_size / 2) {
if (dist > pixel_size / 2.0) {
return w1;
}
vec4 r = mix(w3, w4, p.x + 0.5);
if (dist < -pixel_size / 2) {
if (dist < -pixel_size / 2.0) {
return r;
}
return mix(r, w1, (dist + pixel_size / 2) / pixel_size);
return mix(r, w1, (dist + pixel_size / 2.0) / pixel_size);
}
if (P(0xdb,0x49) || P(0xef,0x6d)) {
float dist = p.y - 2.0 * p.x;
float pixel_size = length(1.0 / (output_resolution / input_resolution)) * sqrt(5.0);
if (p.y - 2.0 * p.x > pixel_size / 2) {
if (p.y - 2.0 * p.x > pixel_size / 2.0) {
return w3;
}
vec4 r = mix(w1, w4, p.x + 0.5);
if (dist < -pixel_size / 2) {
if (dist < -pixel_size / 2.0) {
return r;
}
return mix(r, w3, (dist + pixel_size / 2) / pixel_size);
return mix(r, w3, (dist + pixel_size / 2.0) / pixel_size);
}
if (P(0xbf,0x8f) || P(0x7e,0x0e)) {
float dist = p.x + 2.0 * p.y;
float pixel_size = length(1.0 / (output_resolution / input_resolution)) * sqrt(5.0);
if (dist > 1.0 + pixel_size / 2) {
if (dist > 1.0 + pixel_size / 2.0) {
return w4;
}
@ -137,11 +137,11 @@ STATIC vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 ou
r = mix(mix(w1 * 0.375 + w0 * 0.25 + w3 * 0.375, w3, p.y * 2.0), w1, p.x * 2.0);
}
if (dist < 1.0 - pixel_size / 2) {
if (dist < 1.0 - pixel_size / 2.0) {
return r;
}
return mix(r, w4, (dist + pixel_size / 2 - 1.0) / pixel_size);
return mix(r, w4, (dist + pixel_size / 2.0 - 1.0) / pixel_size);
}
@ -149,7 +149,7 @@ STATIC vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 ou
float dist = p.y + 2.0 * p.x;
float pixel_size = length(1.0 / (output_resolution / input_resolution)) * sqrt(5.0);
if (p.y + 2.0 * p.x > 1.0 + pixel_size / 2) {
if (p.y + 2.0 * p.x > 1.0 + pixel_size / 2.0) {
return w4;
}
@ -162,11 +162,11 @@ STATIC vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 ou
r = mix(mix(w1 * 0.375 + w0 * 0.25 + w3 * 0.375, w3, p.y * 2.0), w1, p.x * 2.0);
}
if (dist < 1.0 - pixel_size / 2) {
if (dist < 1.0 - pixel_size / 2.0) {
return r;
}
return mix(r, w4, (dist + pixel_size / 2 - 1.0) / pixel_size);
return mix(r, w4, (dist + pixel_size / 2.0 - 1.0) / pixel_size);
}
if (P(0x1b,0x03) || P(0x4f,0x43) || P(0x8b,0x83) || P(0x6b,0x43))
@ -185,7 +185,7 @@ STATIC vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 ou
float dist = p.x + p.y;
float pixel_size = length(1.0 / (output_resolution / input_resolution));
if (dist > 0.5 + pixel_size / 2) {
if (dist > 0.5 + pixel_size / 2.0) {
return w4;
}
@ -197,11 +197,11 @@ STATIC vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 ou
r = mix(mix(w1 * 0.375 + w0 * 0.25 + w3 * 0.375, w3, p.y * 2.0), w1, p.x * 2.0);
}
if (dist < 0.5 - pixel_size / 2) {
if (dist < 0.5 - pixel_size / 2.0) {
return r;
}
return mix(r, w4, (dist + pixel_size / 2 - 0.5) / pixel_size);
return mix(r, w4, (dist + pixel_size / 2.0 - 0.5) / pixel_size);
}
if (P(0x0b,0x01))
@ -213,7 +213,7 @@ STATIC vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 ou
float dist = p.x + p.y;
float pixel_size = length(1.0 / (output_resolution / input_resolution));
if (dist > 0.5 + pixel_size / 2)
if (dist > 0.5 + pixel_size / 2.)
return w4;
/* We need more samples to "solve" this diagonal */
@ -241,11 +241,11 @@ STATIC vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 ou
if (diagonal_bias <= 0) {
vec4 r = mix(w1, w3, p.y - p.x + 0.5);
if (dist < 0.5 - pixel_size / 2) {
if (dist < 0.5 - pixel_size / 2.0) {
return r;
}
return mix(r, w4, (dist + pixel_size / 2 - 0.5) / pixel_size);
return mix(r, w4, (dist + pixel_size / 2.0 - 0.5) / pixel_size);
}
return w4;
}

View File

@ -0,0 +1,40 @@
precision mediump float;
uniform sampler2D image;
uniform sampler2D previous_image;
uniform bool mix_previous;
uniform vec2 input_resolution;
uniform vec2 output_resolution;
uniform vec2 origin;
#define equal(x, y) ((x) == (y))
#define inequal(x, y) ((x) != (y))
#define STATIC
#if VERSION >= 0x300
#define FRAG_COLOR fragColor
out vec4 FRAG_COLOR;
#else
#define FRAG_COLOR gl_FragColor
vec4 texture(sampler2D s, vec2 c) { return texture2D(s, c); }
vec4 texture(sampler2D s, vec2 c, float b) { return texture2D(s, c, b); }
#endif
#line 1
{filter}
void main()
{
vec2 position = gl_FragCoord.xy - origin;
position /= output_resolution;
position.y = 1.0 - position.y;
if (mix_previous) {
FRAG_COLOR = mix(scale(image, position, input_resolution, output_resolution),
scale(previous_image, position, input_resolution, output_resolution), 0.5);
}
else {
FRAG_COLOR = scale(image, position, input_resolution, output_resolution);
}
}

View File

@ -58,7 +58,8 @@ endif
CFLAGS += -Werror -Wall -Wno-strict-aliasing -Wno-unknown-warning -Wno-unknown-warning-option -Wno-multichar -Wno-int-in-bool-context -std=gnu11 -D_GNU_SOURCE -DVERSION="$(VERSION)" -I. -D_USE_MATH_DEFINES
# CFLAGS += -DGB_INTERNAL=1 # get access to internal APIs
CFLAGS += -I$(CORE_DIR)
CFLAGS += -s WASM=1 -s USE_SDL=2 --preload-file $(BOOTROMS_DIR)@/BootROMs -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall', 'cwrap', 'getValue', 'AsciiToString', 'FS']"
CFLAGS += -s WASM=1 -s USE_SDL=2 --preload-file $(BOOTROMS_DIR)@/BootROMs --preload-file $(CORE_DIR)/Shaders@/Shaders -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall', 'cwrap', 'getValue', 'AsciiToString', 'FS']"
CFLAGS += -s USE_WEBGL2=1
# CFLAGS += -Wcast-align -Wover-aligned -s SAFE_HEAP=1 -s WARN_UNALIGNED=1
WASM_LDFLAGS :=
@ -95,7 +96,9 @@ WASM_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(WASM_SOURCES))
WEB_SOURCES := $(shell ls ressources/.)
WEB_OBJECTS := $(patsubst %,$(BIN)/ressources/%,$(WEB_SOURCES))
wasm: bootroms $(BIN)/index.html $(WEB_OBJECTS)
SHADERS := $(shell ls $(CORE_DIR)/Shaders/*.fsh)
wasm: bootroms $(BIN)/index.html $(WEB_OBJECTS) $(SHADERS)
all: wasm
# Automatic dependency generation

View File

@ -1,7 +1,6 @@
#include <emscripten.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>
#include <SDL2/SDL_video.h>
#include <SDL2/SDL.h>
#include <stdbool.h>
#include <stdio.h>
@ -9,6 +8,7 @@
#include <Core/gb.h>
#include "main.h"
#include "utils.h"
#include "shader.h"
GB_gameboy_t gb;
@ -19,12 +19,36 @@ SDL_Texture *texture;
SDL_PixelFormat *pixel_format;
SDL_AudioDeviceID device_id;
shader_t shader;
static SDL_Rect rect;
static unsigned factor;
static SDL_AudioSpec want_aspec, have_aspec;
static uint32_t pixel_buffer_1[256 * 224], pixel_buffer_2[256 * 224];
static uint32_t *active_pixel_buffer = pixel_buffer_1;
static uint32_t *previous_pixel_buffer = pixel_buffer_2;
static char *battery_save_path_ptr;
struct shader_name {
const char *file_name;
const char *display_name;
} shaders[] =
{
{"NearestNeighbor", "Nearest Neighbor"},
{"Bilinear", "Bilinear"},
{"SmoothBilinear", "Smooth Bilinear"},
{"LCD", "LCD Display"},
{"CRT", "CRT Display"},
{"Scale2x", "Scale2x"},
{"Scale4x", "Scale4x"},
{"AAScale2x", "Anti-aliased Scale2x"},
{"AAScale4x", "Anti-aliased Scale4x"},
// {"HQ2x", "HQ2x"}, // requires OpenGL ES 1.30 features
// {"OmniScale", "OmniScale"}, // requires OpenGL ES 1.30 features
{"OmniScaleLegacy", "OmniScale Legacy"},
{"AAOmniScaleLegacy", "AA OmniScale Legacy"},
};
configuration_t configuration =
{
.keys = {
@ -62,9 +86,11 @@ configuration_t configuration =
},
.color_correction_mode = GB_COLOR_CORRECTION_EMULATE_HARDWARE,
.highpass_mode = GB_HIGHPASS_ACCURATE,
.scaling_mode = GB_SDL_SCALING_INTEGER_FACTOR,
.blend_frames = true,
.rewind_length = 60 * 2,
.model = MODEL_CGB
.model = MODEL_CGB,
.filter = "OmniScale",
};
// Use this function instead of GB_save_battery()
@ -102,6 +128,46 @@ static void audio_callback(void *gb, Uint8 *stream, int len)
}
}
void update_viewport(void)
{
int win_width, win_height;
SDL_GL_GetDrawableSize(window, &win_width, &win_height);
int logical_width, logical_height;
SDL_GetWindowSize(window, &logical_width, &logical_height);
factor = win_width / logical_width;
double x_factor = win_width / (double) GB_get_screen_width(&gb);
double y_factor = win_height / (double) GB_get_screen_height(&gb);
if (configuration.scaling_mode == GB_SDL_SCALING_INTEGER_FACTOR) {
x_factor = (int)(x_factor);
y_factor = (int)(y_factor);
}
if (configuration.scaling_mode != GB_SDL_SCALING_ENTIRE_WINDOW) {
if (x_factor > y_factor) {
x_factor = y_factor;
}
else {
y_factor = x_factor;
}
}
unsigned new_width = x_factor * GB_get_screen_width(&gb);
unsigned new_height = y_factor * GB_get_screen_height(&gb);
rect = (SDL_Rect){(win_width - new_width) / 2, (win_height - new_height) / 2,
new_width, new_height};
if (renderer) {
SDL_RenderSetViewport(renderer, &rect);
}
else {
glViewport(rect.x, rect.y, rect.w, rect.h);
}
}
void render_texture(void *pixels, void *previous)
{
if (renderer) {
@ -112,7 +178,7 @@ void render_texture(void *pixels, void *previous)
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
}
/*else {
else {
static void *_pixels = NULL;
if (pixels) {
_pixels = pixels;
@ -123,7 +189,7 @@ void render_texture(void *pixels, void *previous)
GB_get_screen_width(&gb), GB_get_screen_height(&gb),
rect.x, rect.y, rect.w, rect.h);
SDL_GL_SwapWindow(window);
}*/
}
}
static void handle_events(GB_gameboy_t *gb) {
@ -243,8 +309,8 @@ int EMSCRIPTEN_KEEPALIVE init() {
"SameBoy v" xstr(VERSION),
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
VIDEO_WIDTH,
VIDEO_HEIGHT,
VIDEO_WIDTH * 4,
VIDEO_HEIGHT * 4,
SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS | SDL_WINDOW_ALLOW_HIGHDPI
);
@ -256,37 +322,31 @@ int EMSCRIPTEN_KEEPALIVE init() {
SDL_SetWindowMinimumSize(window, VIDEO_WIDTH, VIDEO_HEIGHT);
SDL_SetWindowMaximumSize(window, VIDEO_WIDTH, VIDEO_HEIGHT);
renderer = SDL_CreateRenderer(
window,
-1,
SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC
);
// Try to get a GLES 3.0 context
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
SDL_GLContext gl_context = SDL_GL_CreateContext(window);
if (!renderer) {
fprintf(stderr, "SDL_CreateRenderer Error: %s\n", SDL_GetError());
return EXIT_FAILURE;
if (gl_context == NULL) {
// Try to get a GLES 2.0 context
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
gl_context = SDL_GL_CreateContext(window);
}
screen = SDL_CreateRGBSurface(
0,
VIDEO_WIDTH,
VIDEO_HEIGHT,
32,
0, 0, 0, 0
);
if (!screen) {
SDL_Log("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
exit(1);
if (gl_context == NULL) {
fprintf(stderr, "Using software renderer!\n");
renderer = SDL_CreateRenderer(window, -1, 0);
texture = SDL_CreateTexture(renderer, SDL_GetWindowPixelFormat(window), SDL_TEXTUREACCESS_STREAMING, 160, 144);
pixel_format = SDL_AllocFormat(SDL_GetWindowPixelFormat(window));
}
else {
fprintf(stderr, "Using OpenGL renderer!\n");
pixel_format = SDL_AllocFormat(SDL_PIXELFORMAT_ABGR8888);
pixel_format = screen->format;
texture = SDL_CreateTextureFromSurface(renderer, screen);
if (!texture) {
fprintf(stderr, "SDL_CreateTextureFromSurface Error: %s\n", SDL_GetError());
return EXIT_FAILURE;
fprintf(stderr, "GLES: %s\n", glGetString(GL_VERSION));
fprintf(stderr, "GLSL: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
fprintf(stderr, "Parsed GL version: %hu\n", get_gl_version());
}
unsigned audio_sample_rate = query_sample_rate_of_audiocontexts();
@ -355,6 +415,11 @@ int EMSCRIPTEN_KEEPALIVE init() {
init_gb();
if (!init_shader_with_name(&shader, configuration.filter)) {
init_shader_with_name(&shader, "NearestNeighbor");
}
update_viewport();
SDL_PauseAudioDevice(device_id, 0);
return EXIT_SUCCESS;

View File

@ -34,9 +34,17 @@ typedef enum {
JOYPAD_AXISES_MAX
} joypad_axis_t;
enum scaling_mode {
GB_SDL_SCALING_ENTIRE_WINDOW,
GB_SDL_SCALING_KEEP_RATIO,
GB_SDL_SCALING_INTEGER_FACTOR,
GB_SDL_SCALING_MAX,
};
typedef struct {
SDL_Scancode keys[9];
GB_color_correction_mode_t color_correction_mode;
enum scaling_mode scaling_mode;
bool blend_frames;
GB_highpass_mode_t highpass_mode;

1
wasm/opengl_compat.c Normal file
View File

@ -0,0 +1 @@
#include "opengl_compat.h"

7
wasm/opengl_compat.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef opengl_compat_h
#define opengl_compat_h
#define GL_GLEXT_PROTOTYPES 1
#include <GLES3/gl3.h>
#endif /* opengl_compat_h */

261
wasm/shader.c Normal file
View File

@ -0,0 +1,261 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "shader.h"
#include "utils.h"
static const char *vertex_shader_100 = "\
#version 100 \n\
attribute vec4 aPosition;\n\
void main(void) {\n\
gl_Position = aPosition;\n\
}\n\
";
static const char *vertex_shader_300 = "\
#version 300 es\n\
in vec4 aPosition;\n\
void main(void) {\n\
gl_Position = aPosition;\n\
}\n\
";
uint16_t get_gl_version() {
GLint major = 0, minor = 0;
#if defined(GL_MAJOR_VERSION) && defined(GL_MINOR_VERSION)
glGetIntegerv(GL_MAJOR_VERSION, &major);
glGetIntegerv(GL_MINOR_VERSION, &minor);
#else
char *version = (char *) glGetString(GL_VERSION);
int res = sscanf(version, "OpenGL ES %d.%d", &major, &minor);
if (res != 2) {
// Maybe the OpenGL ES prefixed was missing
res = sscanf(version, "%d.%d", &major, &minor);
}
if (res != 2) {
major = 0;
minor = 0;
}
#endif
return (uint16_t)(major * 0x100 + minor);
}
static GLuint create_shader(const char *source, GLenum type)
{
// Create the shader object
GLuint shader = glCreateShader(type);
// Load the shader source
glShaderSource(shader, 1, &source, 0);
// Compile the shader
glCompileShader(shader);
// Check for errors
GLint status = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE) {
GLchar messages[1024];
glGetShaderInfoLog(shader, sizeof(messages), 0, &messages[0]);
fprintf(stderr, "GLSL Shader Error: %s", messages);
}
return shader;
}
static GLuint create_program(const char *vsh, const char *fsh)
{
// Build shaders
GLuint vertex_shader = create_shader(vsh, GL_VERTEX_SHADER);
GLuint fragment_shader = create_shader(fsh, GL_FRAGMENT_SHADER);
// Create program
GLuint program = glCreateProgram();
fprintf(stderr, "Creating program...\n");
// Attach shaders
glAttachShader(program, vertex_shader);
glAttachShader(program, fragment_shader);
fprintf(stderr, "Linking program...\n");
// Link program
glLinkProgram(program);
fprintf(stderr, "Checking for errors...\n");
// Check for errors
GLint status;
glGetProgramiv(program, GL_LINK_STATUS, &status);
if (status == GL_FALSE) {
GLint info_len = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_len);
if (info_len > 1) {
char* info_log = (char*)malloc(sizeof(char) * info_len);
glGetProgramInfoLog(program, info_len, NULL, info_log);
printf("Error linking program:\n%s\n", info_log);
free(info_log);
}
glDeleteProgram(program);
}
// Delete shaders
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
return program;
}
bool init_shader_with_name(shader_t *shader, const char *name)
{
uint16_t gl_version = get_gl_version();
static char master_shader_code[0x801] = {0,};
static char shader_code[0x10001] = {0,};
static char final_shader_code[0x10801] = {0,};
static signed long filter_token_location = 0;
if (!master_shader_code[0]) {
size_t header_len = 0;
if (gl_version >= 0x300) {
char* header = "#version 300 es\n#define VERSION 0x300\n";
header_len = strlen(header);
strcpy(master_shader_code, header);
}
else {
char* header = "#version 100\n#define VERSION 0x100\n";
header_len = strlen(header);
strcpy(master_shader_code, header);
}
FILE *master_shader_f = fopen(resource_path("Shaders/WasmMasterShader.fsh"), "r");
if (!master_shader_f) return false;
fread(master_shader_code + header_len, 1, sizeof(master_shader_code) - 1, master_shader_f);
fclose(master_shader_f);
filter_token_location = strstr(master_shader_code, "{filter}") - master_shader_code;
if (filter_token_location < 0) {
master_shader_code[0] = 0;
return false;
}
}
char shader_path[1024];
sprintf(shader_path, "Shaders/%s.fsh", name);
FILE *shader_f = fopen(resource_path(shader_path), "r");
if (!shader_f) return false;
memset(shader_code, 0, sizeof(shader_code));
fread(shader_code, 1, sizeof(shader_code) - 1, shader_f);
fclose(shader_f);
memset(final_shader_code, 0, sizeof(final_shader_code));
memcpy(final_shader_code, master_shader_code, filter_token_location);
strcpy(final_shader_code + filter_token_location, shader_code);
strcat(final_shader_code + filter_token_location,
master_shader_code + filter_token_location + sizeof("{filter}") - 1);
fprintf(stderr, "Shader code:\n%s\n", final_shader_code);
if (gl_version >= 0x300) {
shader->program = create_program(vertex_shader_300, final_shader_code);
}
else {
shader->program = create_program(vertex_shader_100, final_shader_code);
}
// Attributes
shader->position_attribute = glGetAttribLocation(shader->program, "aPosition");
// Uniforms
shader->input_resolution_uniform = glGetUniformLocation(shader->program, "input_resolution");
shader->resolution_uniform = glGetUniformLocation(shader->program, "output_resolution");
shader->origin_uniform = glGetUniformLocation(shader->program, "origin");
glGenTextures(1, &shader->texture);
glBindTexture(GL_TEXTURE_2D, shader->texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
shader->texture_uniform = glGetUniformLocation(shader->program, "image");
glGenTextures(1, &shader->previous_texture);
glBindTexture(GL_TEXTURE_2D, shader->previous_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
shader->previous_texture_uniform = glGetUniformLocation(shader->program, "previous_image");
shader->mix_previous_uniform = glGetUniformLocation(shader->program, "mix_previous");
// Program
glUseProgram(shader->program);
GLuint vao;
if (gl_version >= 0x300) {
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
}
GLuint vbo;
glGenBuffers(1, &vbo);
// Attributes
static GLfloat const quad[16] = {
-1.f, -1.f, 0, 1,
-1.f, +1.f, 0, 1,
+1.f, -1.f, 0, 1,
+1.f, +1.f, 0, 1,
};
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW);
glEnableVertexAttribArray(shader->position_attribute);
glVertexAttribPointer(shader->position_attribute, 4, GL_FLOAT, GL_FALSE, 0, 0);
return true;
}
void render_bitmap_with_shader(shader_t *shader, void *bitmap, void *previous,
unsigned source_width, unsigned source_height,
unsigned x, unsigned y, unsigned w, unsigned h)
{
glUseProgram(shader->program);
glUniform2f(shader->origin_uniform, x, y);
glUniform2f(shader->input_resolution_uniform, source_width, source_height);
glUniform2f(shader->resolution_uniform, w, h);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, shader->texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, source_width, source_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap);
glUniform1i(shader->texture_uniform, 0);
glUniform1i(shader->mix_previous_uniform, previous != NULL);
if (previous) {
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, shader->previous_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, source_width, source_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, previous);
glUniform1i(shader->previous_texture_uniform, 1);
}
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
void free_shader(shader_t *shader)
{
glDeleteProgram(shader->program);
glDeleteTextures(1, &shader->texture);
glDeleteTextures(1, &shader->previous_texture);
}

29
wasm/shader.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef shader_h
#define shader_h
#include "opengl_compat.h"
#include <stdbool.h>
typedef struct shader_s {
GLuint input_resolution_uniform;
GLuint resolution_uniform;
GLuint origin_uniform;
GLuint texture_uniform;
GLuint previous_texture_uniform;
GLuint mix_previous_uniform;
GLuint position_attribute;
GLuint texture;
GLuint previous_texture;
GLuint program;
} shader_t;
uint16_t get_gl_version();
bool init_shader_with_name(shader_t *shader, const char *name);
void render_bitmap_with_shader(shader_t *shader, void *bitmap, void *previous,
unsigned source_width, unsigned source_height,
unsigned x, unsigned y, unsigned w, unsigned h);
void free_shader(struct shader_s *shader);
#endif /* shader_h */