41
\$\begingroup\$

I am creating a 2.5D isometric (2D images) game.

I want the players to be able to "dye" their armor, clothes, and other things. I find that grayscaling everything leads to losing out on some of the more "natural" coloring. For example, making a dragon with red/yellow grayscale and then applied overlay color, leads to a purely red dragon with all loss of the secondary color. Meanwhile, dying that same Red/Yellow Dragon Blue, makes it Blue/White which looks great.

The characters in my game do not have a huge variety of equipment. The game is closer to how League of Legends handles characters and graphics. There may be a single type of character (ex. Hero) but multiple outfits for the character as options. I want players to be able to have as much customization as possible in the limited number of "outfits" and characters.

So I want players to be able to select a character, pick one of several weapons, alternate between a handful of costume choices (layered onto the characters), and then dye most of everything (pick a specific metallic color for weapons, pick any color at all for clothing).

If I were to be able to use grayscale without losing some of the coloring, I would.

Here is an example as to what I am talking about:

enter image description here enter image description here enter image description here

It is not even possible to "dye" the Dragon, no matter what I use in Photoshop's "Color Overlay" options. Obviously, I am doing it wrong if it is possible to color it better using grayscale. For a color like Green or Yellow or a Lighter Purple, this works okay. For any other color, it destroys the art.

However, it looks much better if I do NOT grayscale it, and instead simply apply a "HUE" color to the original (Red/Yellow) image.

Red Hue enter image description here

Purple Hue enter image description here

Now the players could dye the Dragon ANY color, and it would still look great! (The yellow turns white, while the red turns to any color of the rainbow.)

Does this mean that WHITE or BLACK is the best secondary color? Then use some sort of method or shader to change white (secondary) color to something different?

Are there any articles on this complex method of taking a single image, and based on the coloring, dye different pixels?

For the Dragon, would it work best as a White/Black high Contrast Dragon? Is Red/Yellow the best color set? It turns out a lot better for images than a few other colors I tried. Is Grayscale still superior?

Does Photoshop's "Blending Options" not include the method to appropriately "dye" images for a video game? Are there complex Shaders/Methods that allow me to achieve better success?

Note: If GrayScale allows me to reduce the RAM consumption of my images, or prevent loss of quality during lossy compression, that would be something I would seriously consider.

\$\endgroup\$
3
  • 5
    \$\begingroup\$ This is legitimately interesting. I'm gonna vote this up and hope it gets some more attention. Color theory is a great topic. \$\endgroup\$
    – Evan
    Sep 22, 2013 at 0:46
  • \$\begingroup\$ Hue rotation is a possibility, albeit to 2D -- I don't know if it would work in 3D. \$\endgroup\$
    – ashes999
    Sep 22, 2013 at 1:47
  • \$\begingroup\$ Thank you Evan. I also agree this is extremely interesting, and one of the best topics I've researched on my own time. This type of work, I imagine, is rarely done because it would require an artist- but not just any artist- an artist who deals with color theory, video game shaders, or even an artist-coder. I can only imagine that finding someone who excels at both art and programming is rare. There is next to no findable research about this subject on google. Especially for someone who has no idea what key terms to research. I am certainly not an artist. \$\endgroup\$
    – Carter81
    Sep 23, 2013 at 0:59

5 Answers 5

19
\$\begingroup\$

Hue-shifting is one possibility that would let you get a range of colors without losing the color details. The basic idea is to convert each pixel from RGB to HSV space, then offset the hue by a user-defined amount, then convert back to RGB. Actually, this can be done more efficiently by applying a rotation matrix to the RGB values: create a matrix that rotates around the (1, 1, 1) axis by the user-defined hue angle. Then you can simply apply this matrix to each RGB value in your pixel shader.

This will allow the red-yellow dragon to be shifted to a yellow-green, green-cyan, cyan-blue, blue-magenta, or magenta-red dragon. This is a bit restrictive as the two colors can't be altered independently with this approach. They'll maintain their relative hue as they're authored in the original image. There are also some colors you can't access, e.g. you can't make a black-and-white dragon this way. Still, this may be enough for your purposes.

Another possibility, if your models have two or three "key colors" (red and yellow here), you could author the original image using a separate color channel for each key color. In this case, you'd author the dragon red and green instead of red and yellow, and if you had a third color you could author it in blue. Then, you can let the user pick the three key colors, and use the red, green, and blue values in the image as amounts of those user-defined colors to blend in. So your pixel shader would look something like

finalColor = image.r * userColor1 + image.g * userColor2 + image.b * userColor3;

This would let the users pick any color combination they wanted, including things like black and white, or purple and gold.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ +1 for not only a good answer, but an answer on a rare topic (color theory). I have no idea why I didn't think of that before. RGB. Red Green Blue. DUH! The best colors to use are probably those exact three colors, due to the ability to manipulate the individual channels. \$\endgroup\$
    – Carter81
    Sep 23, 2013 at 1:13
  • 1
    \$\begingroup\$ Color shifting is a very difficult topic because most of the image information is conveyed by brightness, and simple hue shifting doesn't preserve the brightness. Different hues have different inherent brightness values. You need more sophisticated algorithms for that. \$\endgroup\$
    – API-Beast
    Feb 12, 2014 at 0:04
10
+50
\$\begingroup\$

The problem you are facing is that you can't simply "tint" the whole image, the appearance you see is more than just a base color. For one you have fine gradients from one one material to the other, but more importantly you also have reflections,highlights and shadows, which are not influenced by the base color. (Those are basically added on top of it.)

So you need to decompose the image into it's components.

A Example:

Deconstructed Image Tinted Examples

In this case we have 3 layers:

  • The Background - This contains the elements that shouldn't be colored.
  • The Part that should be colored - This is multiplied by the color. (OpenGL multiplies by default if you specify a color other than white.)
  • The Highlights - These are drawn additive over the whole image. It's not white but yellow, to emulate a hue change. Objects aren't just a single color, the hue changes together with the brightness.
\$\endgroup\$
5
  • \$\begingroup\$ +1. But "you can't simply "tint" the whole image" is a bit misleading. It all depends on OP choice for his game. For example Diablo II did it. So I would not be so strict with "you can't / you need" parts. This is just one of the options, better looking but more RAM consuming. \$\endgroup\$
    – Kromster
    Feb 12, 2014 at 4:19
  • 1
    \$\begingroup\$ @KromStern Point is, it looks awful if you do it, always. \$\endgroup\$
    – API-Beast
    Feb 12, 2014 at 12:33
  • \$\begingroup\$ Great answer. Is this possible though, through automation? If one has thousands of animated images for a single character, it is not feasible to hand-select each layer or decompose the image one at a time. Still, I can see some workarounds to help automate it if rendering from 3D using masking, but regardless it is still a great answer. \$\endgroup\$
    – Carter81
    Feb 16, 2014 at 17:52
  • \$\begingroup\$ It is potentially possible to automate it by finding the dominant colors and extracting them (by using a reverse "color to alpha" algorithm). But it's not like I've ever implemented something like that. \$\endgroup\$
    – API-Beast
    Feb 16, 2014 at 18:10
  • \$\begingroup\$ For extracting the highlights you can take the mask you previously extracted and filter it so you are only left with the brightest components. \$\endgroup\$
    – API-Beast
    Feb 16, 2014 at 18:12
3
\$\begingroup\$

I would personally recommend using mask textures to achieve what you want. This will ensure that your main RGB24 texture will keep its original colour quality without sacrificing any precision. It will also give you plenty of artistic freedom to blend your main texture with your colour mask.

Each mask can be viewed as a grayscale texture that will linearly interpolate the colour of your base texture with your custom color. Let's say you are letting your users customize two colors, the pixel shader will look something like this:

inputs: MainTexture(RGB), Mask1Texture(A), Mask2Texture(A), Colour1(float), Colour2(float)

output: (Mask1Texture+Mask2Texture)-MainTexture + Colour1*Mask1Texture + Colour2*Mask2Texture

This suggests that the more a mask has a large value for one pixel, the less your main texture will impact the output colour of that pixel. For example you could convert your dragon texture into a grayscale and erase everything but your wings to have custom wing colour no matter the body colour.

I have used this technique for mobile games and it won't affect your overall performance. It might cost you a 33% more RAM per texture but the trade-off for quality is probably worth it.

P.s. If you're only thinking of using one custom colour, you can simply use the Alpha channel of your RGBA32 main texture as your mask.

\$\endgroup\$
2
\$\begingroup\$

you can use color palettes and let the player edit the palette. If you use shaders, you can use 1D textures as palettes and the original gray value as index.

To convert a true color image into an image with 255 colors, you can take all your pixel color values and group them in the 3d color space to 255 clusters with the k-means algorithm. Or use generic image conversion tools. Then it is important to mention that interpolation of the gray value texture will be an interpolation of indices in your color textures. This can lead to a result where you have several colors between two texels of your original image. This can be solved by disabling interpolation (I think you do not want to do that) or by sorting your color palette in a useful manner.

\$\endgroup\$
2
  • \$\begingroup\$ This is basically what I was thinking as well. You may want to expand the answer to something a bit more complete. Basically, you let the user pick two colors (or more or fewer, but for the sake of argument, 2), make a 1D texture that fades from the two colors. Use a grayscale texture on the geometry that serves as a reference in the 1D texture. It's still possible for the user to set ugly colors, but they at least have the option of making nice contrasting color choices. \$\endgroup\$
    – wrosecrans
    Feb 9, 2014 at 17:51
  • \$\begingroup\$ yes you can also derive the full 255 color palette from fewer user colors, but here I can only advise you to try it out. I know that a lot of old games used changes in the color palette to make variations in their monsters. I think Diablo did that. \$\endgroup\$
    – Arne
    Feb 14, 2014 at 14:37
1
\$\begingroup\$

you could convert the image to a gray image but still save it with rgba channels. store a weight value in the alpha channel then use this to decide how much dye to apply to a pixel. this weight can be calculated based on how close the color is to white. the closer to white the pixel is, the less dye should apply or the lower the weight should be. this will leave most highlights with their original lightness, but apply a bit of the dye to it. the final pixel color can then be found by calculating each channel Ro + (Rd * Ao) where Ro is the original red, Rd is the red in the dye color and Ao is the Alpha of the original. so all white pixels get no new color and all black pixels assume the dye color.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .