It is currently Wed Feb 22, 2017 12:38 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 37 posts ]  Go to page 1, 2, 3  Next
Author Message
PostPosted: Thu Dec 29, 2016 6:18 pm 
Offline
Formerly Fx3
User avatar

Joined: Fri Nov 12, 2004 4:59 pm
Posts: 2924
Location: Brazil
Well... I'm not getting a good approach for a 3x resize of the PPU screen. From 256x240 (pixel aspect) to 292x240 (squared aspect) => 3x => 876x720.

But how to approach 876 pixels? There's a good nearby value (896 pixels) if I use the pattern 3-4-3 pixels. With no interpolation, I can add 1 pixel at every 3,6,9... pixels, or add 2 pixels (double it) at every 3,6,9... pixels, but the result here becomes bizarre after adding more than 2 pixels. Well, the EXACT 876 is possible - repeat twice the pixel at every 18 pixels, but the result is TERRIBLE. :lol: :lol: :lol:

(click to see the full image)


Attachments:
rock_ex01.png
rock_ex01.png [ 23.96 KiB | Viewed 490 times ]
Top
 Profile  
 
PostPosted: Thu Dec 29, 2016 8:46 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 4772
Location: Canada
In the general case for resizing pixel art while changing the pixel aspect ratio, my best suggestion is to use nearest-neighbour to resize to the nearest integer-multiple size, then use a bilinear filter to resize to the final target resolution. In this case:

1. Nearest Neighbour: 256 x 240 >> 768 x 720
2. Linear Filter: 768 x 720 >> 876 x 720


Alternatively, and maybe particularly suitable for the NES, which tends to have a very fuzzy horizontal picture but a very clear vertical picture due to the nature of scanline rendering, you may opt to use different horizontal and vertical filterings and treat each axis separately:

1. Linear horizontal: 256 x 240 >> 876 x 240
2. Nearest Neighbour vertical: 876 x 240 >> 876 x 720

If your implementation lets you specify different filter types for each axis, you can probably do both of these in a single GPU step.


Top
 Profile  
 
PostPosted: Thu Dec 29, 2016 9:41 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 9328
Location: Rio de Janeiro - Brazil
Zepper wrote:
From 256x240 (pixel aspect) to 292x240 (squared aspect)

The errors are so big because you're changing the aspect ratio at a low resolution, meaning that any errors that are introduced will be scaled up when you enlarge the image. To minimize errors you should scale to an integer multiple first, and then adjust the aspect ratio, because scaling artifacts are less noticeable at high resolutions.


Top
 Profile  
 
PostPosted: Fri Dec 30, 2016 4:22 am 
Offline
Formerly Fx3
User avatar

Joined: Fri Nov 12, 2004 4:59 pm
Posts: 2924
Location: Brazil
tokumaru wrote:
Zepper wrote:
From 256x240 (pixel aspect) to 292x240 (squared aspect)

The errors are so big because you're changing the aspect ratio at a low resolution, meaning that any errors that are introduced will be scaled up when you enlarge the image. To minimize errors you should scale to an integer multiple first, and then adjust the aspect ratio, because scaling artifacts are less noticeable at high resolutions.

:lol: :lol: Let me show you how I'm doing the things.

For the 2x upscaler, 1 PPU pixel becomes a 2x2 square in the bitmap.
For the 3x upscaler, 1 PPU pixel becomes a 3x3 square in the bitmap.
For the Nx upscaler, 1 PPU pixel becomes a NxN square in the bitmap. All is done in real time. I don't generate the 256x240 pic and then do a full resize, as you guessed.
The only interpolation I use is the arithmetic mean between R,G and B components (previous pixel x current pixel), or repeating pixels.

I have no problems with the 2x and 4x upscalers, the square is perfect and nice. But since I own a notebook at 720p (or a bit more), the use of a 3x scaler is a must-do right now. Any other more complicated technique would imply in rendering the full image (256x240) for a later resizing.

EDIT: for comparison... or laughs. ^_^;;


Attachments:
comp03.png
comp03.png [ 159.78 KiB | Viewed 422 times ]
Top
 Profile  
 
PostPosted: Fri Dec 30, 2016 8:53 am 
Offline
User avatar

Joined: Fri Nov 19, 2004 7:35 pm
Posts: 3735
Zepper wrote:
For the Nx upscaler, 1 PPU pixel becomes a NxN square in the bitmap. All is done in real time. I don't generate the 256x240 pic and then do a full resize, as you guessed.

Why would you do it this way, rather than generate a scanline or image and upscale that?

_________________
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!


Top
 Profile  
 
PostPosted: Fri Dec 30, 2016 9:42 am 
Online

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 17781
Location: NE Indiana, USA (NTSC)
I imagine that going 256x240 to 768x720 to 876x720 incurs more texture memory accesses than going from 256x240 to 876x720 in one step. In addition, working on individual scanlines instead of full screen images introduces less algorithmic latency.

Fractional bilinear interpolation describes how to perform this upscaling using a pixel shader.

On the other hand, an emulator may be generating scaled video directly into the back buffer because the video output API that it uses can't assume that hardware acceleration for OpenGL is present. And even if it is, Intel GMA is limited to OpenGL 1.4. Only after its name was changed to HD Graphics did it gain thorough support for OpenGL 2 or later.


Top
 Profile  
 
PostPosted: Fri Dec 30, 2016 10:36 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 4772
Location: Canada
Zepper wrote:
I don't generate the 256x240 pic and then do a full resize, as you guessed.

It's not really a "guess", it's still a fact that you're upscaling a 256x240 picture to 876x720 whether you do it in 3x3 chunks at a time or generate the whole picture at once. The overall input and output is the same either way.

The results of any given pixel only depend on a narrow window of input pixels from the source.

For linear interpolation you need to buffer 2 samples instead of just 1, (or 4 for bi-linear). If you're trying to do this immediately after generating pixels for an NES image you'll need to buffer at least a whole scanline + 2 more pixels, I suppose? (You don't need a scanline buffer if you're only doing linear interpolation horizontally, though, like my second suggestion.)

You'd still probably get much better performance for this due to caching if you just buffered the whole 256x240 image and do the operation all at once. ...or get a GPU to do it.


Top
 Profile  
 
PostPosted: Sat Dec 31, 2016 3:54 pm 
Offline
Formerly Fx3
User avatar

Joined: Fri Nov 12, 2004 4:59 pm
Posts: 2924
Location: Brazil
Allegro's stretch_blit() did it without any pixel distortion! NO CLUE how's possible... have to analyse its code to see what's done.


Attachments:
allegro_rocknes_bit.png
allegro_rocknes_bit.png [ 31.21 KiB | Viewed 328 times ]
allegro_blit.png
allegro_blit.png [ 36.09 KiB | Viewed 328 times ]
Top
 Profile  
 
PostPosted: Sat Dec 31, 2016 9:03 pm 
Offline
User avatar

Joined: Mon Feb 07, 2011 12:46 pm
Posts: 870
But it is distorted. (Look in bottom-right part to see more clearly the distortions)

_________________
.


Top
 Profile  
 
PostPosted: Sun Jan 01, 2017 6:18 am 
Offline
Formerly Fx3
User avatar

Joined: Fri Nov 12, 2004 4:59 pm
Posts: 2924
Location: Brazil
zzo38 wrote:
But it is distorted. (Look in bottom-right part to see more clearly the distortions)

Nothing perceptible to my eyes, specially on scrolling. Visually it's perfect. My stretching is terrible and you can notice the extra columns on scrolling. The most interesting is all about the absent pixel interpolation.


Top
 Profile  
 
PostPosted: Sun Jan 01, 2017 6:26 am 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3192
Location: Mountain View, CA, USA
Zepper wrote:
zzo38 wrote:
But it is distorted. (Look in bottom-right part to see more clearly the distortions)

Nothing perceptible to my eyes, specially on scrolling. Visually it's perfect.

Then your eyes are getting old. :-) Open the two links to the images in separate tabs (right-click on the image, Open link in new tab), then alternate back and forth between the tabs -- you can clearly see the scaling differences. Things I see quite clearly:

allegro_rocknes_bit.png (you meant blit) has some anti-aliasing going on; allegro_blit.png doesn't. Look at the score at the top, Megaman's head/face, as well as the centre (white area) of the far left bullet. The bottom right of the screen (re: zzo38 talking about distortions) refers to the grey dithering effect the artist used on the platforms (there's a grey cross-hatch-like pattern used), and these differ greatly between the two shots.

I don't know which of those us supposed to be correct/accurate (gut feeling is allegro_blit.png -- at least that looks more like how things do at 1:1), but the anti-aliasing (especially at native size) looks ugh (to me). It's a matter of opinion though: some people like that look (I don't, I prefer linear scaling or nearest-neighbour, e.g. 1:1, 2:1, 3:1, 4:1, etc. and not something that looks like 2.12:1 etc.).


Top
 Profile  
 
PostPosted: Sun Jan 01, 2017 7:14 am 
Offline
Formerly Fx3
User avatar

Joined: Fri Nov 12, 2004 4:59 pm
Posts: 2924
Location: Brazil
koitsu wrote:
Then your eyes are getting old. :-)

We're from 1977. 8-)
Quote:
allegro_rocknes_bit.png (you meant blit) has some anti-aliasing going on; allegro_blit.png doesn't. Look at the score at the top, Megaman's head/face, as well as the centre (white area) of the far left bullet. The bottom right of the screen (re: zzo38 talking about distortions) refers to the grey dithering effect the artist used on the platforms (there's a grey cross-hatch-like pattern used), and these differ greatly between the two shots.

Well, allegro_rocknes_blit brings a bit of pixel analysis according to the brightness, so it'll repeat either the current OR the previous pixel. Meh, each pixel from the original = 3 pixels in this scaling, and 6 when the column needs to be repeated. >_<;; This causes a bizarre distortion. The other image (allegro_blit) uses allegro's stretch blit. When scrolling, you don't notice the repeated columns (smooth); on allegro_rocknes_blit it's very noticeable.

Quote:
I don't know which of those us supposed to be correct/accurate (gut feeling is allegro_blit.png -- at least that looks more like how things do at 1:1), but the anti-aliasing (especially at native size) looks ugh (to me). It's a matter of opinion though: some people like that look (I don't, I prefer linear scaling or nearest-neighbour, e.g. 1:1, 2:1, 3:1, 4:1, etc. and not something that looks like 2.12:1 etc.).

I'm going into the accuracy - "squared" aspect. Before that, I used the 1:1 pixel perfect or VGA aspect (4:3 at 640x480). I can upload the emulator for you to try out both modes. ^_^;;


Top
 Profile  
 
PostPosted: Sun Jan 01, 2017 7:56 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 9328
Location: Rio de Janeiro - Brazil
allegro_blit.png looks to me like nearest neighbor from 256x240 to 768x720, then nearest neighbor from 768x720 to 876x720 to me. Since the stretching is done at a higher resolution, the source pixels vary between 3 and 4 target pixels wide, as opposed to varying between 3 or 6 pixels wide when you stretch at the lower resolution before scaling up.


Top
 Profile  
 
PostPosted: Wed Jan 04, 2017 9:42 am 
Offline
Formerly Fx3
User avatar

Joined: Fri Nov 12, 2004 4:59 pm
Posts: 2924
Location: Brazil
This is my best result after an hard work of testing. I tried to repeat pixels by analysing a certain pattern - it was looking good, but distortions were visible, like missing black pixels or "rounded" objects. My last try took an easy path - of interpolating the nearest neighbor. Screenshots and C code below.
Code:
#define MK3X(v) { \
   surface_00[0] = surface_00[1] = surface_00[2] = \
   surface_08[0] = surface_08[1] = surface_08[2] = \
   surface_16[0] = surface_16[1] = surface_16[2] = v; \
   surface_00 += 3; surface_08 += 3; surface_16 += 3; \
}
static void gfx_expand_3xsq(TPAL * const p)
{
   tbuffer[t_count] = p->value;
   t_count++;
   if(3 == t_count) {
      tbuffer[t_count] = p->value;
      t_count++;
   }
   if(7 == t_count)
   {
/* A few ones are still unused due to my research */
      unsigned int *PW = &tbuffer[0];
      unsigned int *PX = &tbuffer[1];
      unsigned int *PA = &tbuffer[2]; 
      unsigned int *Pi = &tbuffer[3]; 
      unsigned int *PB = &tbuffer[4];       
      unsigned int *PY = &tbuffer[5];       
      unsigned int *PZ = &tbuffer[6];     

      *PA = gfx_pixel_interpolate(*PA,*PX);
      *PX = gfx_pixel_interpolate(*PX,*PW);
      /////////////////////////////////////
      *Pi = gfx_pixel_interpolate(*Pi,*PB);
      *PB = gfx_pixel_interpolate(*PB,*PY);

      //transfer data
      unsigned int *m = tbuffer;
      while(t_count > 0)
      {
         MK3X(*m); m++;
         t_count--;
      }
   }
}

Suggestions or tips? I'm open for it!


Attachments:
rocknes_repeat_pixel.png
rocknes_repeat_pixel.png [ 33.99 KiB | Viewed 195 times ]
rocknes_interpolation.png
rocknes_interpolation.png [ 36.42 KiB | Viewed 195 times ]
Top
 Profile  
 
PostPosted: Wed Jan 04, 2017 11:35 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 9328
Location: Rio de Janeiro - Brazil
You're still repeating/interpolating at the native resolution, and that will never look anything like the examples you showed. You have to scale up first, and then repeat/interpolate. It will always look like crap if the artifacts are introduced at the native resolution.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 37 posts ]  Go to page 1, 2, 3  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group