Jump to content
IGNORED

Gopher2600 (continuing development on Github)


JetSetIlly

Recommended Posts

3 minutes ago, Thomas Jentzsch said:

Ah, missed that. But why would they use score mode here? It would work well without it too.

 

Very good question. I don't know. I'm only surmising that it is set based on the bug that I fixed. My bug might have been more subtle, but I don't think so. Either way, it would be interesting to dig into the ROM.

Link to comment
Share on other sites

  • 4 weeks later...

I've been working to improve the CRT shaders in Gopher2600 and was quite frustrated at how difficult is is to capture good screenshots, particularly of games that employ flicker-kernels. So I've added a screenshot facility that tries to capture what the eye sees on the screen.

 

Could still do with some improvement but I'm pleased with this as a first attempt.

 

Games are, of course, Galagon and Robotron from ChampGames.

 

 

galaga_title.thumb.jpg.8493b20508b0c7d159a43190ec6c7ac6.jpg

 

galaga.thumb.jpg.661763e9b531f048bd344e2b0a1b6f9b.jpg 

 

robotron_title.thumb.jpg.e8781578826de09f42fd27e83b3a8597.jpg

 

robotron.thumb.jpg.2b0efda09f231a6b22da08bc2cbb8e1e.jpg

Edited by JetSetIlly
  • Like 4
Link to comment
Share on other sites

17 minutes ago, JetSetIlly said:

capture good screenshots, particularly of games that employ flicker-kernels. So I've added a screenshot facility that tries to capture what the eye sees on the screen.

 

Try Stay Frosty, you should end up transparent fire that lets you clearly see the ice blocks through their bodies.

 

Stay Frosty 44271.png

 

The ROMs available here - ignore the "unavailable", I think it refers to the download count.

 

 

 

 

Link to comment
Share on other sites

8 minutes ago, SpiceWare said:

Try Stay Frosty, you should end up transparent fire that lets you clearly see the ice blocks through their bodies.

 

Stay Frosty 44271.png

 

The ROMs available here - ignore the "unavailable", I think it refers to the download count.

 

photo_StayFrosty_20210503_225110.thumb.jpg.5e010fa66395d70ad24e9bded376782f.jpg

  • Like 1
Link to comment
Share on other sites

1 hour ago, JetSetIlly said:

I've been working to improve the CRT shaders in Gopher2600 and was quite frustrated at how difficult is is to capture good screenshots, particularly of games that employ flicker-kernels. So I've added a screenshot facility that tries to capture what the eye sees on the screen.

This is great, thanks so much! I'm constantly taking screenshots of games for the show and it's so hard to capture both 30Hz screens to merge them in Photoshop. It's also impossible to take screencaps of anything 'in action' either. This is super helpful!

 

- James

  • Like 2
Link to comment
Share on other sites

5 hours ago, JetSetIlly said:

photo_CDFJChess_20210503_225301.thumb.jpg.8efa09f23440c2f821ad6249798f34b0.jpg

 

Thanks for testing. Still not quite there for this particular ROM/method.  I would have liked to see the blue squares "solid".

In particular, I would hope to see no distinct "every 3 line" horizontal banding.

Would it be possible to "average" over three consecutive frames?  That would do the trick for this method, I think.

There would still be the issue of *which* three frames ABCDE (i.e., ABC or BCD or CDE) because of movement of objects...

I realise this ROM isn't one that lends itself well to screenshots :)

 

 

Link to comment
Share on other sites

4 hours ago, Andrew Davie said:

 

Thanks for testing. Still not quite there for this particular ROM/method.  I would have liked to see the blue squares "solid".

In particular, I would hope to see no distinct "every 3 line" horizontal banding.

Would it be possible to "average" over three consecutive frames?  That would do the trick for this method, I think.

There would still be the issue of *which* three frames ABCDE (i.e., ABC or BCD or CDE) because of movement of objects...

I realise this ROM isn't one that lends itself well to screenshots :)

 

 

 

I know what you mean.  A quick hack around with the code I can produce this.

 

photo_CDFJChess_20210504_094142.thumb.jpg.bfda16ab29fe910de5a725d2442dfdd7.jpg

 

I'll have a play around with it some more this week. I think what I'll end up doing is having separate settings for "photo-mode". So in addition to the CRT settings (phosphor speed, bloom etc.) there would be settings for exposure time and all that kind of stuff. I'll see what I can come up with.

 

What I don't want to do is to produce overly artificial screenshots. As much as I can, I want it to capture what the eye sees on the screen.

  • Like 1
Link to comment
Share on other sites

1 hour ago, JetSetIlly said:

 

I know what you mean.  A quick hack around with the code I can produce this.

 

photo_CDFJChess_20210504_094142.thumb.jpg.bfda16ab29fe910de5a725d2442dfdd7.jpg

 

What I don't want to do is to produce overly artificial screenshots. As much as I can, I want it to capture what the eye sees on the screen.

 

I understand; having said that, this version is excellent and IMHO gives a much better impression of what you see on the screen. This particular display technology is a bit of a difficult one to find a screenshot of that's truly represenative. What you need is to output animating GIFs, with inbuilt flicker ;). That is, GIF images with each frame 1 frame in duration and then viewing them (at the right speed) would be truly representative of what the eye sees.

 

 

 

  • Like 1
Link to comment
Share on other sites

Perhaps Gopher could capture frames until it finds a frame identical to one that it has captured before (with an upper limit of say 10 if the screens have objects that move around a lot).
Then the user could choose to save that as either:

  • a single frame formed by averaging all the frames
  • an animated GIF showing each captured frame in turn, using the same timing as they were captured.

Should work for ABABAB (captures AB), ABCABCABC (captures ABC), ABCDABCD (captures ABCD).

 

Would still fail for ABCBABCB but do many games do that?
If need, could make it stop when it sees 2 successive frames that are identical to 2 previous success frames.
Ie, ABCBABCB would capture ABCB because AB was seen before.

Link to comment
Share on other sites

2 hours ago, stepho said:

Perhaps Gopher could capture frames until it finds a frame identical to one that it has captured before (with an upper limit of say 10 if the screens have objects that move around a lot).

 

I think these are good ideas, I'll address the points with my thoughts:

 

2 hours ago, stepho said:

Then the user could choose to save that as either:

  • a single frame formed by averaging all the frames

 

If we put to one side for the moment, the idea of analysing frame content, averaging frames already happens, in a way, with the phosphor emulation. At the moment I've implemented phosphor to have linear decay but it would be better for screenshot purposes if it starts off slow and then fades off relatively rapidly.


Alternatively, we should perhaps thing of screenshotting taking an short average of the future in addition to the past. If we think of the following as a sequence of frames with C being the current frame.

 

-5  -4  -3  -2  -1  C  1  2

 

The negative numbered frames represent the immediate past and will still partially be visible in the C frame as phosphor fade. The positive numbered frames are in the future.


A screenshot of frame C with an exposure time of 1 will capture an average of C and the 5 immediately preceding frames. An exposure time of two on the other hand will capture C, the 5 preceding frames and also frame 1. Similarly for an exposure time of three, would include frame 2.

 

The advantage of this is that we can treat exposure differently to phosphor fade. I'll experiment with this later this week.

 


That said, your idea of looking for an identical frame is a good one. It would be incredibly useful if the emulator knew what type of kernel it was dealing with for several reasons. For screenshot purposes, we could use it to control how long the "exposure" is (ie. one, two or three frames). It could also be used to control how emulation buffer underruns are handled but that's a very different conversation ?

 

2 hours ago, stepho said:
  • an animated GIF showing each captured frame in turn, using the same timing as they were captured.

 

Not a bad idea. It shouldn't be too difficult to do.

 

2 hours ago, stepho said:

Should work for ABABAB (captures AB), ABCABCABC (captures ABC), ABCDABCD (captures ABCD).

 

Would still fail for ABCBABCB but do many games do that?
If need, could make it stop when it sees 2 successive frames that are identical to 2 previous success frames.
Ie, ABCBABCB would capture ABCB because AB was seen before.

 

It would be worth experimenting with. One other problem might be the exsitance of "hybrid" kernels. In other words, some elements are drawn on a period of two frames and other elements on a period of three frames. I know my own game (a flappy bird clone) changes some things every third frame and other things on the second frame and yet others on every frame. But this might be a pathologicial example.

 

  • Like 2
Link to comment
Share on other sites

I'm curious about how you do the phosphor fade.

 

Do you store n raw frames in a sliding window and then do a weighted average for the display?


Or do you use a simple signal filter?
eg:

frame=getNewFrame();
frame = ( (oldFrame * k) + frame) / 2;     // where 0<k<1
display( frame );
oldFrame = frame;

  • Like 1
Link to comment
Share on other sites

8 hours ago, stepho said:

I'm curious about how you do the phosphor fade.

 

Do you store n raw frames in a sliding window and then do a weighted average for the display?


Or do you use a simple signal filter?
eg:

frame=getNewFrame();
frame = ( (oldFrame * k) + frame) / 2;     // where 0<k<1
display( frame );
oldFrame = frame;

 

The current version takes the strongest value from the current phosphor or the new frame information, working on each pixel one at a time. It's all done in GLSL https://github.com/JetSetIlly/Gopher2600/blob/master/gui/sdlimgui/shaders/crt_phosphor.frag

 

image.png.1a7b50d56bfdff24d319ebbfd9c9baf2.png

 

This new CRT shader (which this is only a part of of course) is based on the work on recent CRT work by Mattias Gustavsson https://github.com/mattiasgustavsson/crtview/ . If you dig around on his github you'll find patched versions of DOSBox and a couple of other emulators.

 

My CRT implementation isn't exactly the same as Mattias' but there are substantial ideas taken from his work. Phosphor being one of them (the 0.96 multiplier is his) and how to curve the screen effectively, being the other. The main takeaway though is the use of framebuffers. My previous OpenGL experience is very, very old and predates modern shaders by a long time so framebuffers had completely passed me by.

 

If you look at the old phosphor implementation therefore, it was a lot less pretty and only worked against video-black. Still, it worked in a fashion but it is less versatile and probably a little slower.

 

https://github.com/JetSetIlly/Gopher2600/blob/v0.10.3/gui/sdlimgui/screen.go

 

image.thumb.png.615b0e295dca1dce316cf7eb4f53ed88.png

 

on edit: forgot about the other half of the fragment work. see next post.

 

 

 

Edited by JetSetIlly
  • Like 1
Link to comment
Share on other sites

And this is the part of the old fragment shader that did the other half of the phosphor work. https://github.com/JetSetIlly/Gopher2600/blob/v0.10.3/gui/crt/shaders/generator/fragment.frag

 

image.png.1f2522751699bb312c441ff701cc2fa4.png

 

I was experimenting with fading the different phosphor elements at different speeds. I'm not sure that's necessary.

  • Like 1
Link to comment
Share on other sites

10 minutes ago, JetSetIlly said:

The current version takes the strongest value from the current phosphor or the new frame information, working on each pixel one at a time.

Interesting, thanks for providing the info.

 

For Stella we use the same approach:

  auto getPhosphor = [&] (const uInt8 c1, uInt8 c2) -> uInt8 {
    // Use maximum of current and decayed previous values
    c2 = static_cast<uInt8>(c2 * myPhosphorPercent);
    if(c1 > c2)  return c1; // raise (assumed immediate)
    else         return c2; // decay
  };

This is also done per pixel and per color, where c1 is the current frame's value and c2 the previously resulting (displayed) value.

 

So the multiplier 0.96 seems to be the only difference. Is that explained somewhere?

Link to comment
Share on other sites

1 minute ago, Thomas Jentzsch said:

Interesting, thanks for providing the info.

 

For Stella we use the same approach:


  auto getPhosphor = [&] (const uInt8 c1, uInt8 c2) -> uInt8 {
    // Use maximum of current and decayed previous values
    c2 = static_cast<uInt8>(c2 * myPhosphorPercent);
    if(c1 > c2)  return c1; // raise (assumed immediate)
    else         return c2; // decay
  };

This is also done per pixel and per color, where c1 is the current frame's value and c2 the previously resulting (displayed) value.

 

So the multiplier 0.96 seems to be the only difference. Is that explained somewhere?

 

Not that I can tell, I'll ask him.

 

The other important factor is that the previous phosphor is blurred before blending with the new frame. So while the phosphor is pixel-by-pixel, the previous phosphor has "bloomed" a little. If you look at the current source version of Gopher2600 you can adjust the bloom. Not sure if it's visible but the the Activision logo has a "glow" after I've adjusted the bloom slider to 'very high'

 

image.thumb.png.6bd23cf8168338a98c9255a8b57724af.png

 

  • Like 1
Link to comment
Share on other sites

2 minutes ago, JetSetIlly said:

The other important factor is that the previous phosphor is blurred before blending with the new frame.

Isn't that a general CRT effect which is applied to the output independently of phosphor? 

7 minutes ago, JetSetIlly said:

Not sure if it's visible but the the Activision logo has a "glow" after I've adjusted the bloom slider to 'very high'

I cannot tell. :) 

Link to comment
Share on other sites

13 minutes ago, JetSetIlly said:

Not that I can tell, I'll ask him.

It seems like a magic number. Probably the raise should be adjustable like the decay. But it deviates so little from immediate raise, that this would probably be an overkill.

 

BTW: It is possible to add a quote to an edited post? With BBcode this was easy.

Edited by Thomas Jentzsch
Link to comment
Share on other sites

Just now, Thomas Jentzsch said:

Isn't that a general CRT effect which is applied to the output independently of phosphor? 

 

There is a small amount of blur/softening applied independently of the phosphor pipeline. There is also some chromatic aberration (color fringing) which would has a similar effect of blurring the image. In fact, in the old code I referred to this as blurring, but that was a poor description.

 

Just now, Thomas Jentzsch said:

I cannot tell. :) 

 

Fair enough. It shouldn't be too obvious anyway ?

Link to comment
Share on other sites

6 minutes ago, Thomas Jentzsch said:

It seems like a magic number. Probably the raise should be adjustable like the decay. But it deviates so little from immediate raise, that this would probably be an overkill.

 

It does seem like a magic number. It has the effect of dimming the picture but I'm not sure why that specific value. Note that all channels including the alpha channel are dimmed but later on I fix the alpha channel at 1.0 so it is effectively only the RGB channels that are touched here. It might be a useful place to attach an overall brightness control.

Edited by JetSetIlly
  • Like 1
Link to comment
Share on other sites

2 minutes ago, JetSetIlly said:

It might be a useful place to attach an overall brightness control.

You are right, effectively it only affects the brightness and slightly modifies the phosphor effect. So it can be emulated by changing these two settings instead.

Link to comment
Share on other sites

Just now, Thomas Jentzsch said:

You are right, effectively it only affects the brightness and slightly modifies the phosphor effect. So it can be emulated by changing these two settings instead.

 

Thinking about it some more, it also provides some headroom for the phosphor effect to be visible against non-black backgrounds. You need to correct for brightness in another way, but adjusting that value down causes the phosphor fade to be more obvious against dark-but-not-black colors.

 

For example, a very rough hack with more extreme values:

 

image.png.ccaecba67fba0ec8998a6a724ae122a1.png

 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...