-
-
Notifications
You must be signed in to change notification settings - Fork 697
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
Non-Linear Stretch (to Full Screen) #639
Comments
Ideally both linear and non linear strech are supported. For example I play f-zero many times full fullscreen stretch and yes there is distortion but for this game i dont care because its a car. same for many other games. a custom aspect ration pixel value can be used too. this would accomodate both options. |
video reference: |
Best example I know of of Non-Linear Stretch is from Plex Media Player. Here are some snaps to illustrate what I mean. Fatness effect of linear stretch is worse with human characters, of course, but Teenage Mutant Ninja Turtles will be good enough to illustrate the point. The linear stretch tends to be more offensive when starting from something more narrow (like the SNES ratio of 8:7 versus the 4:3 ratio of the show below)… |
I see what you mean, I'm just thinking out loud bit how will this look in a game like mario, were the screen scrolls all the time from right to left, won't the enemies be warped/distored when moving closer to the middle of the screen? I can image this to be a bit strange. Personally I prefer fixed linear scaled aspect ratio, but choice is a good thing! |
@larssteenhoff: True, you might see it, but it's variable / gradual - you'll see it if you're looking for it - I could do a quick mockup of that later. Either way, I think an option for either would be great. |
If someone can track down the math that is typically used for this non-linear warp or the corresponding code in another project I might have time to add this. |
@braindx, funny, I was just thinking i might ask you if you would be the one that could solve this considering your great work on the renderer recently and when i finally got home from work here you are posting on my issue! That would be great. To this day I have never seen an emulator of any sort offer this, and have always wondered why, however it isn't entirely foreign to video players. Would be super cool if Provenance becomes the first to apply it. I don't know the math off hand, but I can try to sort it out with you, principally it's like applying the view to a mesh that stretches it variably increasing more the further from center, like a ramp - would be like a curve of values. In my mockups above I was simulating it by warping a frame in Photoshop screens of that warp here:
|
libretro already has a ton of shaders, including some different scalers. Not sure if any are non-linear or if they're iOS compatible but maybe something for reference at least. Check out, https://github.com/libretro/glsl-shaders Another GLES 2.0 shader resource, No idea about shaders myself but something I'm looking to try out. |
@braindx : found something else that might be of help. Kodi (media player…xbmc) seems to have implemented non-linear stretch in their app, so i was sifting through their github code: gl_stretch.glsl which might shine some light on the math if I have targeted the right thing, but it's gotta be in their code somewhere. |
Some redditing and I have discovered this is also referred to as Dynamic Stretching or Anamorphic Dynamic Stretch, as discussed on this GoPro related article, so perhaps some searching around this might turn up something… video of after effects method described |
I have a basic idea of how you'd do this, you just need to choose the transform function. Let's say you choose sin() so you have sin() 0 -> 90 -> 180 => 0...1...0 on a curve. So now you're looking to map the new pixels to the old. Lets imagine it horizontal line by horizontal line, where the old width was 640 and you're new width is 1080, and you're at pixel X.
So basically each new pixel is a weighted average of where the mapping function (this case sin()) lands in the middle of an old pixel, choose the closest neighbor pixel. Now my linear algebra is pretty terrible, this can probably be done with a matrix function and no have to do it line by linen would scale in both directions, but this is where someone with OpenGL experience and better math would come in, though you could do something like above. If the vertical scaling is just linear it's pretty easy to figure out which line you should be sampling from, just do the linear ration and floor it. Or, do the same idea where you grab two lines at do a weighted average of the 4 pixels it's nearest to by the x and y mappedIndex floats you'd get by doing the conversion. Each line you'd grab the 2nd horizontal line and do an average on top of the old average, repeat with the next line, so each line gets 2 passes basically. Though since you're mapping a larger space to a smaller space I think that's not necessary, if you were shrinking the image you'd want to do a 2D weighted average of all the pixels you're combining into the smaller space which sounds more complicated. I haven't been in a math room in over a decade dno't beat me up that this is far from efficient and I didn't use radians Also rather than sin you'd probably want some kind of inverse log function so the drop off isn't so quick at the edges. sin would stretch more in the middle and less on the edges, I think...or maybe it'd be relatively even proportionately, my head hurts thinking about it too much. Also, you can probably half the iterations by not doing the middle point hack, and instead work on two pixels at a time from opposite edges of the input buffer but just subtracting the mapped value from the input length and only looping to length/2 |
Yes, that's essentially right, but you don't need to make it that complex because you're not transforming it vertically at all. You just need to define a 1D function for transforming it horizontally. So, if the transform for a given pixel is usually In Kodi's repo, they have this shader: That seems wrong to me because they're just squaring the x which seems like it would result in more distortion at the center and less at the edges... I've mocked up an example, where I've implemented a sample shader that demonstrates what XBMC's function does as well as a function I wrote that warps the opposite way. By default it shows a checkerboard and my function (alternates between nonlinear and linear stretching), but you can switch it to preview Super Mario Bros 3 or Super Metroid by changing the numbers for TEST_IMAGE at the top or change it to XBMC's function by changing USE_XBMC_NONLINEAR to 1. I think you'd want something a bit more complicated than either of those, probably one with control for how much of the screen is undistorted and then some sort of ramp that controls how quickly that escalates to more extreme distortion at the edges. |
How is work on this going :)? |
Any updates? |
Anyone working on this? |
Any potential development on this, as more people are curious about this, with the recent test flight and soon App Store submission? |
Full Request (with non-linear addition):
Setting option stretch to full screen (as in fitting to full width of a display). Highly recommend doing this as non-linear stretch so it maintains the integrity of the art on screen, centrally, maximizing the screen equity without making all the characters look fat/wide as linear stretch would do uniformly.
The text was updated successfully, but these errors were encountered: