This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

Gradual color change

Gradual color change
by on (#205119)
In my next game, there are several sections that might use different colors, but where you can still move from one section to the other without a hard cut.

For this, I'd like to do a color change like in "Link's Awakening DX":
www.youtube.com/watch?v=YzEU19PUVuE&t=13m19s

Do you know of any automatic algorithm with which I can do the color change for an NES palette?

I'm not talking about how to put this into PPU. I'm talking about a good way to transform one color into another one.

Let's say I have color $05 in one screen and color $2A in the next. And I want to change one color into the other one with two steps in between. But I don't want to build a manual lookup array of every single color tranforming into every other color.

Is there some automatism I can apply here?
Re: Gradual color change
by on (#205120)
Grays excluded, the NES palette is circular, like a color wheel, so you can generally, "slide" left or right (whichever way is shorter) to morph one hue into another. The brightness can also be animated like this, as we usually do when fading.

If you want a fixed number of steps between 2 states, you'll have to calculate the difference between the source and target values (hue and brightness separately) and then find N intermediary points in those ranges.
Re: Gradual color change
by on (#205140)
tokumaru wrote:
Grays excluded, the NES palette is circular, like a color wheel, so you can generally, "slide" left or right (whichever way is shorter) to morph one hue into another.

Yeah, that might be a way. Thanks for the tip.I never noticed that the palette actually equals a color wheel.

tokumaru wrote:
The brightness can also be animated like this, as we usually do when fading.

When I want to fade into black, I would do the following:
Subtract each color value with 16. If the upper four bits are 0, then the next step will be that the color becomes black.
Is this how you would do it as well?

tokumaru wrote:
If you want a fixed number of steps between 2 states, you'll have to calculate the difference between the source and target values (hue and brightness separately) and then find N intermediary points in those ranges.

Could be a bit difficult with 48 usable colors. I guess it's not really necessary that every color has the same number of steps.
Re: Gradual color change
by on (#205143)
It would be easiest to precompute the palettes needed rather than computing them on the fly. Do what Tokumaru said, just don't try doing it in assembly.
Re: Gradual color change
by on (#205145)
pubby wrote:
It would be easiest to precompute the palettes needed rather than computing them on the fly.

That's exactly what I want to avoid: Manually designing each color change for each section combination.

pubby wrote:
just don't try doing it in assembly.

What does the programming language have to do with it?
Re: Gradual color change
by on (#205147)
You should separate the palette entry between the "hue" (low nybble) and "luminousity" (high nybble) components, and slide those separatedly. Luminosity is the easier, since you just add or substract one, until you get to the number you want.

Hue is a bit more complicated, but doable. The "lazy" way is to simply add one until you get the number you want, if you attain the $0d column reset to the $01 column. But this can take a long time as you might rotate through the entiere palette doing this. So in order to be quicker, you'd want to compute the "distance" between the hues, and go either left (+$01) or right (-$01) through the palette columns, taking the shorter path. In the worst case where the hue difference is 6, the distance is the same in either direction. In the case either the start or target column is $00 it's a special case because you have to jump in our out of this column at some point. Similarly, the colour black $0f is a special case.

The effect as a whole can have a duration up to 6 steps, but you'll have to find a way to stay at the target hue and/or saturation once you reach them, just like you can stay to black when fading the palette out.
Re: Gradual color change
by on (#205148)
Yeah, the description that you gave is what I would have done anyway after the above hints:
Distinction between horizontal and vertical values.
And finding the shortest path.

I just don't know yet how I should decide which way to take when the other color is six steps away.
And what to do if a color has to move into white, gray or black.

Also, I'm not sure whether I should update each color whenever possible (so that a color that goes through six steps actually shows all six steps) or whether I should declare some fixed points in the transition where all colors are updated (so that the six steps are only done in memory, but only three of them are shown on the screen).
Re: Gradual color change
by on (#205149)
DRW wrote:
Also, I'm not sure whether I should update each color whenever possible (so that a color that goes through six steps actually shows all six steps) or whether I should declare some fixed points in the transition where all colors are updated (so that the six steps are only done in memory, but only three of them are shown on the screen).

Unless the effect needs to be faster than 6 frames, I do not see the point of not showing all steps.

Quote:
I just don't know yet how I should decide which way to take when the other color is six steps away.

It really doesn't matter, you just have to pick one "preferred" direction that applies in those cases.

Quote:
And what to do if a color has to move into white, gray or black.

The simplest is to avoid using those colours in the palettes that will go through your effect ^^. If you still need those colours with the effect, you'll have to handle the cases individually and code them as special cases.

Another way would be to have your own luminosity that extends the NES luminosity, for example it accepts values 0 to 5, but when display it you convert it to NES palette, for instance $0x always convert to $0f, $1x converts to $0x, and so forth until $4x which converts to $3x, and eventually $5x which converts to $30.

That still doesn't solve the problem of jumping to and from the gray column, but at least it allows easily fading to and from black, as well as to and from white, using the normal algorithm, but you just have to convert your palette colour before displaying it.

Using the same concept you could extend this and have a full HSL palette internally, do a gradual change there and "convert" it to NES palette before displaying, however I do think it's overkill. But if you really need smooth transitions and you need support for all colours including black, whites and grays, this is the most appropriate solution.
Something like this pseudocode to convert from HSL to nearest NES palette colour.
Code:

convert_HSL_to_NES(H, S, L):
   if L == L_MAX :
      return $30

   elseif L == 0 :
      return $0f

   else if S < SAT_MAX :
      return (L*3/L_MAX) << 4

   else :
      hue = 12*H/H_MAX + 1
      lum = 4*l/L_MAX
      return lum << 4 + hue     
Re: Gradual color change
by on (#205150)
Bregalad wrote:
Unless the effect needs to be faster than 6 frames, I do not see the point of not showing all steps.

Because in this case, the colors will switch at many different times. Not only will there be more or less steps. But colors that are six steps away will be updated at other times from colors that are four steps away.

Bregalad wrote:
The simplest is to avoid using those colours in the palettes that will go through your effect ^^.

Unlikely.

Bregalad wrote:
If you still need those colours with the effect, you'll have to handle the cases individually and code them as special cases.

Well, yes, but making something up for handling these cases is the interesting part. Actually programming it is not an issue.

I don't think I'll have some kind of internal abstract palette. I'll simply invent something for the grey values.
Re: Gradual color change
by on (#205161)
Image
Why yes, NES palette, I see you clearly in the YIQ color plane. Divide it into twelfths.
Re: Gradual color change
by on (#205162)
Since the grayscale columns in the NES palette are all fucked up, I usually give hue 0 special treatment, and I use a look-up table to convert brightness values into the final hardware colors.
Re: Gradual color change
by on (#205163)
Of course column 0 needs special treatment. The color change algorithm will only work on column 1 to C. This one is pretty obvious.

The question is just: How could we treat transformation from and to the grayscale values?

Brightness is simple: Conversion from one brightness to another is done as if black was actually color $00, white $30 and gray $10 and $20 instead of the constellation that they have in the moment.

But how do I gradually transform black into a specific green? Or gray into orange?
Re: Gradual color change
by on (#205165)
In my opinion, transitioning from black/grays/white to any color:
1 - jump to the correct hue instantly
2 - add/subtract brightness untill you get the final color
Re: Gradual color change
by on (#205167)
Sounds like a plan.
Re: Gradual color change
by on (#205168)
DRW wrote:
I just don't know yet how I should decide which way to take when the other color is six steps away.

When the colors are five or six hues apart, you're cutting through the middle of the YUV/YIQ plane. This means you can go through the gray of roughly the same brightness. For example, for $22 to $28, use $22, $10, $28.
Re: Gradual color change
by on (#205182)
DRW wrote:
What does the programming language have to do with it?

The math involved is kinda hairy. Though, I guess it comes down to personal opinion and the amount of transitions you're gonna have.

Anyway, here's some example code: http://coliru.stacked-crooked.com/a/7c14ab65a73ca806

If I had to do it in assembly, I would just do a 5-step lerp between the values and call it a day:
Code:
frame1:
lda pal1, y
sta pal_buffer, y
rts

frame2:
lda pal1, y
asl
adc pal1, y
adc pal2, y
ror
lsr
sta pal_buffer, y
rts

frame3:
clc
lda pal1, y
adc pal2, y
ror
sta pal_buffer, y
rts

frame4:
lda pal2, y
asl
adc pal2, y
adc pal1, y
ror
lsr
sta pal_buffer, y
rts

frame5:
lda pal2, y
sta pal_buffer, y
rts
Re: Gradual color change
by on (#205188)
pubby wrote:
If I had to do it in assembly, I would just do a 5-step lerp between the values and call it a day:

But this won't work at all, because the palette is not "linear".
Re: Gradual color change
by on (#205193)
Oh! You're right. This is why I wouldn't do it in assembly :P

The hue would have to be separated from the value and both interploated (which was said in this thread I'm just dumb)
Re: Gradual color change
by on (#205199)
In my opinion, the programming itself is really secondary here. Once I get to actually implementing it, I will write the algorithm and then try to optimize it.

The code that you linked to is not really something that seems to apply to my own attempt. I would not work with any kind of RGB color at all and I wouldn't have any lookup tables.

I would simply do the following:

For all 16 (or 13, depending on what's faster) colors, do the following:

Take the current value.
Take the new value.
Take the high nibble of each.
Calculate the way that has to be walked from one color to the other to reach the other one.
The values in this case go from high nibble 1 to high nibble C (not 0 to F). This has to be taken care of.
Calculate how many scrolling frames we will have and calculate scrollingFrames / colorSteps to find out after how many frames the color needs to be adjusted.

Do a similar thing with low nibble, i.e. brightness.

And then apply the colors during the correct scrolling frames.

This takes 16 x 6 bytes in RAM that have to exist beyond function calls:
For each color:
The current value.
The destination value.
Indicator whether the high nibble is incremented or decremented.
Indicator whether the low nibble is incremented or decremented.
The number of frames until the high nibble is incremented or decremented.
The number of frames until the low nibble is incremented or decremented.

Maybe some more values for special treatment of gray to color and color to gray.
Re: Gradual color change
by on (#205241)
Quote:
The question is just: How could we treat transformation from and to the grayscale values?

Quote:
Maybe some more values for special treatment of gray to color and color to gray.

My $2 : If you want to do it "the NES way", usually simplicity is to be preferred over exactitude. Not that this is a technical limitation or anything, but if there's a way to do something in a simpler way even though it's technically "false", but still does the job, it's common for 80's games to take this path.

This is why I first mentioned cycling hues in a fixed direction, regardless of the values. This also has the advantage of handling the special case of hue=0, as it'll jump to either hue=C or hue=1 without any additional code. The disadvantage is that this does not take the shortest parth, for instance if your preferred direction is increasing hues, and you want to switch from hue=6 to hue=5, you'll rotate through the whole palette instead of going there directly. It's still a gradual change of colour, but the effect is different. Final Fantasy 1 does exactly that when entering in the menu or a shop.

If the shortest path is calculated I'd suggest treat "0" as a normal hue and make it jump to "1" or "C" column, to eventually reach the destination column, is probably closer to the "NES" way to do it, rather than having extra code for that particular case. Extra code does not seem to be avoidable if gray column or black is the target colour, though, but I believe the normal algorithm can extend rather naturally to those colours if used as start colours.
Re: Gradual color change
by on (#205242)
Yeah, I really have to see which version I take. I don't know if taking a fixed direction is really something I want to have, even if it's shorter. I'll ave to see what it looks like, which won't happen in the next weeks since I first have to program more important stuff.

I also thought about treating column 0 as a regular column, in the way that the color gray moves through blue and cyan, which might look pretty natural.
Although, in this case, I would still skip column 0 whenever non-gray colors are concerned.


Are there any other NES games that use gradual color changes to switch from one region to another? (Games that simply use this technique to fade out an image to a solid black don't count.)
Re: Gradual color change
by on (#205244)
DRW wrote:
I also thought about treating column 0 as a regular column, in the way that the color gray moves through blue and cyan, which might look pretty natural.
Although, in this case, I would still skip column 0 whenever non-gray colors are concerned.

Yeah, exactly what I meant. Sorry if that wasn't clear.

Quote:
Are there any other NES games that use gradual color changes to switch from one region to another? (Games that simply use this technique to fade out an image to a solid black don't count.)

Ninja Gaiden 3 have this effect on the status bar when you loose a life.

Mega Man's ending have an animation with the sun setting down, however I doubt the palette values are not simply pre-calculated.
Re: Gradual color change
by on (#205409)
Bregalad wrote:
Ninja Gaiden 3 have this effect on the status bar when you loose a life.

But the status bar is just an abstract collection of letters and numbers. You can apply any color to it anyway.
But in "Ninja Gaiden", you don't have a source palette and a destination palette and then a bunch of in-between palettes that you can analyze to find out what algorithm they used to turn the dark yellow of screen 1 into light pink of screen 2.

Bregalad wrote:
Mega Man's ending have an animation with the sun setting down, however I doubt the palette values are not simply pre-calculated.

Yeah, that's not the same situation anyway: You have a single scene and this scene is shown in different colors that are all valid scene colors: Every shade really represents a certain time frame of the scene.

But my situation is: You have two different actual color palettes. And now the game gradually switches between both palettes by shortly showing intermediate virtual versions.
These intermediate versions are not supposed to indicate that the scene ever looked like that.
(If I have a green plant in one screen and a purple roof in another screen that simply occupies the same index in the palette, this doesn't mean that the roof was ever brown, just because my scene switch effect cycles through these colors.)

That's not the same as in the "Mega Man" ending where every shade is supposed to be a full representation of the scene at a specific point in time.
So, there's nothing to analyze here, from an algorithm point of view.


And the color change in "Final Fantasy" when entering the menu or a shop is just some general color cycling before the screen completely fades to black. The source colors don't transform into destination colors, they simply blink a bit before everything goes dark and a completely new, unrelated scene is created from scratch.


I would need something like this, but in an NES game:
http://www.youtube.com/watch?v=YzEU19PUVuE&t=13m19s