Skip to content
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

Locomotive games (SD Gundam, V-Tetris, Virtual Fishing) have incorrect colors #55

Open
vaguerant opened this issue Mar 27, 2024 · 4 comments

Comments

@vaguerant
Copy link
Contributor

vaguerant commented Mar 27, 2024

Issue

It looks like the shades of red are not displaying correctly in Virtual Fishing (EDIT: and other Locomotive games). Everything with the "one above black" shade, used in Virtual Fishing for highlights and backgrounds is an extremely dim shade of red somewhere in the decimal 16-17/255 range, making it difficult to tell which menu item is selected, etc.

image image

In these screenshots, FEMALE and START are (barely) highlighted on the respective images. These screenshots come from the English translation patch available on virtual-boy.com for ease of navigation, but the same issue occurs in a clean Japanese ROM.

Expected behavior

Here's some Mednafen shots:

Virtual Fishing (Japan)  English Translation -240327-221925 Virtual Fishing (Japan)  English Translation -240327-221936

You can see here that I have MALE and START highlighted on the respective screens, the name plate and RECORDS section have a visible background color, etc.

Versions tested

  • Red Viper 407550d (current HEAD)
  • Red Viper ffacc69 (v0.9.1)
  • Red Viper 81ee977 (v0.9.0)
  • Virtual Fishing (Japan).vb (matches No-Intro CRC32: 526CC969)
  • Virtual Fishing (Japan) [English Translation].vb (above ROM with English patch applied)

Same behavior on all versions of Red Viper I've tested; this does not appear to be a regression.

Edit

I just noticed that V-Tetris also displays dimly and has barely-visible "one-shade-above-black" display. The Mode Select menu in V-Tetris has a BPS (Bullet Proof Software) logo scrolling across the background which is mostly obscured in Red Viper. Comparison shot from Mednafen.

image V-Tetris (Japan)-240327-233349

Locomotive was also responsible for SD Gundam: Dimension War, so that's another case that may be (EDIT: definitely is) impacted by the same issue.

@vaguerant vaguerant changed the title Virtual Fishing has incorrect colors Locomotive games (V-Tetris, Virtual Fishing) have incorrect colors Mar 27, 2024
@vaguerant vaguerant changed the title Locomotive games (V-Tetris, Virtual Fishing) have incorrect colors Locomotive games (SD Gundam, V-Tetris, Virtual Fishing) have incorrect colors Mar 27, 2024
@skyfloogle
Copy link
Owner

skyfloogle commented Mar 27, 2024

I noticed previously that the back of the ship in Vertical Force looked a bit dim.
The Virtual Boy's display works through effectively PWM, and the brightness values indicate how long the LEDs should be turned on. Modern screens don't work in the same way. I directly pass the Virtual Boy's brightness values to the 3DS display, and that doesn't line up with the expected appearance. This is probably what gamma correction is for, but I haven't figured out how to set that up yet (or how to prevent it from messing with Luma's built-in stuff).
Also worth noting: the image looks different in Citra on my laptop screen, iirc closer to what you'd see in Mednafen.

@vaguerant
Copy link
Contributor Author

Such a weird system.

So would you say this isn't a Locomotive issue specifically, beyond that maybe they liked to use dimmer colors than other developers? It doesn't really harm V-Tetris or SD Gundam as much from my testing because the darker tones are just used for background elements, but Virtual Fishing is pretty hard to navigate on 3DS because it uses them in the menus.

@vaguerant
Copy link
Contributor Author

vaguerant commented Mar 28, 2024

Here's what I'm doing on my end currently:

diff --git a/source/3ds/video.c b/source/3ds/video.c
index 374dcb4..daf793d 100644
--- a/source/3ds/video.c
+++ b/source/3ds/video.c
@@ -197,6 +197,13 @@ void video_init() {
        C3D_RenderTargetSetOutput(finalScreen[1], GFX_TOP, GFX_RIGHT, DISPLAY_TRANSFER_FLAGS);
 }

+
+#define BRIGHTFLOOR 20
+u8 brightnessFloor (u8 brightness) {
+    if (brightness > 0) return clamp127(BRIGHTFLOOR + (127-BRIGHTFLOOR) * brightness / 127);
+    else return 0;
+}
+
 void video_render(int alt_buf) {
        if (tDSPCACHE.ColumnTableInvalid)
                processColumnTable();
@@ -207,9 +214,9 @@ void video_render(int alt_buf) {
        int col_scale = maxRepeat;
        #endif
        brightness[0] = 0;
-       brightness[1] = clamp127(tVIPREG.BRTA * col_scale);
-       brightness[2] = clamp127(tVIPREG.BRTB * col_scale);
-       brightness[3] = clamp127((tVIPREG.BRTA + tVIPREG.BRTB + tVIPREG.BRTC) * col_scale);
+       brightness[1] = brightnessFloor(tVIPREG.BRTA * col_scale);
+       brightness[2] = brightnessFloor(tVIPREG.BRTB * col_scale);
+       brightness[3] = brightnessFloor((tVIPREG.BRTA + tVIPREG.BRTB + tVIPREG.BRTC) * col_scale);
 
 	C3D_AttrInfo *attrInfo = C3D_GetAttrInfo();
 	AttrInfo_Init(attrInfo);

I'm making a big assumption here: that the brightness of a lit LED can't go below a certain level, basically theorizing that there's a gap in brightness between 0/off (black) and the minimum (red) brightness achievable. I absolutely do not know this, I have zero experience with the real Virtual Boy and no anecdotal evidence that suggests this approach is right.

This approach necessarily reduces the dynamic range somewhat, since it now goes from 20-127 instead of 1-127 for lit pixels. BRIGHTFLOOR is a define for ease of adjustment, in production this could be inlined, converted to a user-configurable option, etc. but right now I'm doing this based on nothing, so that's not a concern currently.

I'm also only using integer math, because this is all back of the envelope stuff anyway. Precision is kind of pointless when I'm just eyeballing whatever looks OK.

Here's a couple of screenshots demoing Virtual Fishing and V-Tetris with a brightness floor of 20:

image image

EDIT: And a test build for anybody who wants one:
red-viper_brightness-floor.zip

@vaguerant
Copy link
Contributor Author

I can say with some confidence that my approach above is wrong. The game Virtual Boy Wario Land seems to use screen transitions which never set the brightness to 0; my approach above means that graphics that are still on-screen during transitions sit at that (arbitrary) floor of 20, when it seems likely that it's supposed to be completely dark even though they are technically not at 0 brightness.

Doing some more arbitrary nonsense, I'm running with this now:

diff --git a/source/3ds/video.c b/source/3ds/video.c
index 9481e39..cc32262 100644
--- a/source/3ds/video.c
+++ b/source/3ds/video.c
@@ -209,6 +209,12 @@ void video_init() {
        GSPGPU_WriteHWRegs(0x400424, &vtotal, 4);
 }

+#define BRIGHTFLOOR 20
+u8 brightnessFloor (u8 brightness) {
+       if (brightness > 6) return clamp127(BRIGHTFLOOR + (127-BRIGHTFLOOR) * brightness / 127);
+       else return 0;
+}
+
 void video_render(int alt_buf) {
        if (tDSPCACHE.ColumnTableInvalid)
                processColumnTable();
@@ -219,9 +225,9 @@ void video_render(int alt_buf) {
        int col_scale = maxRepeat;
        #endif
        brightness[0] = 0;
-       brightness[1] = clamp127(tVIPREG.BRTA * col_scale);
-       brightness[2] = clamp127(tVIPREG.BRTB * col_scale);
-       brightness[3] = clamp127((tVIPREG.BRTA + tVIPREG.BRTB + tVIPREG.BRTC) * col_scale);
+       brightness[1] = brightnessFloor(tVIPREG.BRTA * col_scale);
+       brightness[2] = brightnessFloor(tVIPREG.BRTB * col_scale);
+       brightness[3] = brightnessFloor((tVIPREG.BRTA + tVIPREG.BRTB + tVIPREG.BRTC) * col_scale);

        C3D_AttrInfo *attrInfo = C3D_GetAttrInfo();
        C3D_AttrInfo *attrInfo = C3D_GetAttrInfo();
        AttrInfo_Init(attrInfo);

The only change is I'm now doing if (brightness > 6) ... else return 0; instead of if (brightness > 0). 6 is just another arbitrary choice to get black during Wario Land fades since that seems to be as low as Wario goes. Obviously this is a total mess and nonsensical as far as real-world accuracy.

Really it comes down to the gamma ramp as you've said. It presumably isn't linear: it must ramp up quite slowly at the bottom-end (which suits the behavior of Wario Land), then a bit faster (so that the colors in Virtual Fishing are visible), before presumably stabilizing into something more linear after that? But I'll quit speculating since it really doesn't matter until you can test on real hardware.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants