Jump to content


Photo

Need help duplicating an advanced color effect:


  • Please log in to reply
14 replies to this topic

#1 esco

esco

    GMC Member

  • GMC Member
  • 209 posts

Posted 01 April 2012 - 06:32 AM

First off, sorry if this is in the wrong forum, but from what I was told this would be a pretty advanced question that I have.

I basically want to duplicate the color effect found in this video (NOTE: this video is slowed to 10 fps to make it easier to see the effect):

The effect is a triangle, that has a gradient in the middle of it, some transparency along the edges, and a waviness effect applied to it. I managed to duplicate all of this, but I have NO IDEA how to duplicate the color effect. It basically goes from blue, to purple, to yellow, green, light blue, then blue and repeats. It seems to have a color scroll that it just moves over the object in a set pattern, or changes the color one line of pixels vertically at a time.

I was trying to come up with a way to use d3d fog to do it on a surface but I couldn't think of one. Can someone please assist me with accomplishing this color effect? It would be very much appreciated. I will gladly rep the person, and also credit them for the code.

Here is my version of the effect so far (the code is below: it is all done in the draw event): http://youtu.be/7g8XR73wIWc

//makes the wave go in 1 direction first (CREDIT GOES TO XOT FOR GIVING ME THE ORIGINAL VERSION OF THIS CODE)
if (wave_size_increase ==0)
{
   if (phase < 999999) {phase+=1/22;}
   
   else {wave_size_increase = 1;}
}

//then once it goes over enough it goes in other direction
else
{
   if (phase > 0) {phase-=1/22;}
   
   else {wave_size_increase = 0;}
}


//next 4 vars are needed for script to work & MUST be done like this
var size,shift,sx,sy;
width = sprite_get_width(sprite_index);
height = sprite_get_height(sprite_index);
xoff   = sprite_get_xoffset(sprite_index);
yoff   = sprite_get_yoffset(sprite_index);

//here it sets wether to do a wave horizontally
if (axis == 1) {size = height;} 

//or vertically
else {size = width;}


//makes it appear to glow more
draw_set_blend_mode(bm_add);

//runs loops once for each pixel based on whether we use height or width
for (count=0; count<size; count+=1) 
{
   //this controls the wave effect
   shift = amplitude*sin(3.6*pi*((count/wavelength)+phase));
   
   //hozintal wave first
   if (axis == 1) 
   {
      sx = image_xscale*(shift-xoff)+x;
      sy = image_yscale*(count-yoff)+y;
      draw_sprite_part_ext(sprite_index,image_index,0,count,width,1,sx,sy,image_xscale,image_yscale,c_white,image_alpha);
   }
   
   //now vertical wave
   else
   {
      sx = image_xscale*(count-xoff)+x;
      sy = image_yscale*(shift-yoff)+y;
      draw_sprite_part_ext(sprite_index,image_index,count,0,1,height,sx,sy,image_xscale,image_yscale,c_white,image_alpha);
   }
}

//sets draw back to normal so things drawn after won't glow
draw_set_blend_mode(bm_normal);

Edited by esco, 01 April 2012 - 05:18 PM.

  • 0

#2 Gamer3D

Gamer3D

    Human* me = this;

  • GMC Member
  • 1590 posts
  • Version:GM8.1

Posted 01 April 2012 - 09:07 PM

Easy ways:
  • Make a sprite for the whole animation. (Recommended)
  • Use d3d to set normals on a model (the normals should occupy a 90-degree curve) then set up 2 or 3 lights (using directional lighting) and rotate them around the model.

  • 0

#3 Follomania

Follomania

    Unum Mirabile

  • GMC Member
  • 445 posts
  • Version:GM:Studio

Posted 01 April 2012 - 10:29 PM

Changing colors can be visualized and implemented as a vector between two points in three-dimensional space, and a point traveling along that vector from point A to point B. I shall explain further.
Each color has three components: red, green, and blue. So we have two sets (red1, green1, blue1) and (red2, green2, blue2) and want to get from one to another. What do we do? We get the unit vector that defines the direction between the two points.
vectorred = red2 - red1
vectorgreen = green2 - green1
vectorblue = blue2 - blue1

Now we get the length of that vector:
vectormagnitude = sqrt((redvector)2 + (greenvector)2 + (bluevector)2)

Divide each vector component by the magnitude:
vectorred /= vectormagnitude
vectorgreen /= vectormagnitude
vectorblue /= vectormagnitude

Now we want a speed variable, which we can attain by dividing the magnitude by the number of steps we want it to take to get from one color to the next. We'll say 60:
vectorspeed = vectormagnitude/60

Every step we increase our color components by the corresponding vector component times the vector speed.
Every 60 steps, we need to refresh our vector to the next color and current color.

Here's code to get you started:
target_color[0] = c_red
target_color[1] = c_yellow
target_color[2] = c_blue
current_color = 0
r1 = color_get_red(target_color[current_color])
g1 = color_get_green(target_color[current_color])
b1 = color_get_blue(target_color[current_color])
r2 = color_get_red(target_color[(current_color + 1) mod 3])
g2 = color_get_green(target_color[(current_color + 1) mod 3])
b2 = color_get_blue(target_color[(current_color + 1) mod 3])
rv = r2 - r1
gv = g2 - g1
bv = b2 - b1
mag = sqrt(sqr(rv) + sqr(gv) + sqr(bv))
if (mag != 0){
    rv /= mag
    gv /= mag
    bv /= mag
}
spd = mag / 60
rc = r1
gc = g1
bc = b1
color = make_color_rgb(rc, gc, bc)

rc += rv*spd
gc += gv*spd
bc += bv*spd
color = make_color_rgb(rc, gc, bc)

current_color = (current_color + 1) mod 3
r1 = color_get_red(target_color[current_color])
g1 = color_get_green(target_color[current_color])
b1 = color_get_blue(target_color[current_color])
r2 = color_get_red(target_color[(current_color + 1) mod 3])
g2 = color_get_green(target_color[(current_color + 1) mod 3])
b2 = color_get_blue(target_color[(current_color + 1) mod 3])
rv = r2 - r1
gv = g2 - g1
bv = b2 - b1
mag = sqrt(sqr(rv) + sqr(gv) + sqr(bv))
if (mag != 0){
    rv /= mag
    gv /= mag
    bv /= mag
}
spd = mag / 60
rc = r1
gc = g1
bc = b1
color = make_color_rgb(rc, gc, bc)


I think that should work.1 I haven't tested it though, and just came up with the whole using a vector to change colors thing as I was typing a response.


Edit: typo
1 It works; I've tested the code. Whoever says linear algebra isn't useful in the real world knows nothing!

Edited by Follomania, 02 April 2012 - 01:21 AM.

  • 1

#4 esco

esco

    GMC Member

  • GMC Member
  • 209 posts

Posted 02 April 2012 - 04:27 AM

Hey Follomania, thanks so much for taking the time to try and explain the logic, AS WELL as actually coding all of that for me. That is very generous of you and you obviously know your stuff. :D UNFORTUNATELY, this does not seem to do what I am asking for. It just cycles between colors, which I could actually do already in a much simpler manner. I assume that I use COLOR for my image blend, right? What I am trying to do is getting the sprite to fade into the next color from left to right on the sprite (basically a few columns at a time)

So basically it would start as red, then fade into yellow from the tail side, then fade into blue. I do not mean have the WHOLE SPRITE change colors. Check my video again above to see what I mean please. :) If your code already does this somehow, please explain what I am doing wrong? All I do in my code is change the image_blend in the draw_sprite_ext statement to color.

BTW: I repped you anyway just because you took so much time out to try and help. :thumbsup:

Gamer3d: that is actually an EXCELLENT idea too. But I know nothing about 3d, and I am making a 2d game. So unless you have the time to jump on something like msn messenger and tutor me on it some (which I wouldn't expect anyone to do for me, since I would be asking for a lot there) I would have no idea how to do it, and keep all the 2d stuff in game working. I would love to learn some, but sadly I have no one to teach me, and my attempts so far to enlighten myself have accomplished nothing (I spent several days trying once).

Edited by esco, 02 April 2012 - 04:28 AM.

  • 0

#5 Follomania

Follomania

    Unum Mirabile

  • GMC Member
  • 445 posts
  • Version:GM:Studio

Posted 02 April 2012 - 05:07 PM

The first method that comes to mind then, is to "pre-bake" the colors into an array. This will significantly reduce processing needed during the game, but will mean you can only have the colors you chose at the beginning. More specifically, you won't be able to change your base color whenever you want.
Here's how I would do it in pseudo-code.
Have an array named "baked_color[]".
Run the first code chunk I gave you.
Set "ii" to 0
Loop 3 times{
    Loop 60 times{
        Run second code chunk I gave to you.
        Set baked_color[ii] to color
        Add 1 to ii
    }
    Run third code chunk I gave to you.
}

Now every color will be in an array from [0] to [179]
Now set a variable such as "current_hue" to 0

When you draw the sprite, increase current_hue by 1, using "mod 180" to set it to 0 when it's out of range.
Each segment, use baked_color[(current_hue + count) mod 180] to give you your image_blend variable.

If I come up with a better method, I'll let you know, but this should work.

Edited by Follomania, 02 April 2012 - 05:08 PM.

  • 0

#6 IceMetalPunk

IceMetalPunk

    InfiniteIMPerfection

  • Retired Staff
  • 9259 posts
  • Version:Unknown

Posted 02 April 2012 - 08:10 PM

All you need here is an array of color values, some linear interpolation between them, an offset variable, and modular arithmetic (oh, yeah, and a white sprite).

I'll be assuming the following things:

1) The sprite is called spr_blast.
2) The colors are stored in an array called "colors".
3) The total number of colors is in a variable called called "colnum".
4) The left of your sprite should be drawn at x.
5) The max amplitude of the wave is AMP, and the frequency of the wave is proportional to FREQ.

Then this should work:

/* CREATE */
offset=0;

/* DRAW */
w = sprite_get_width(spr_blast);
h = sprite_get_height(spr_blast);

offset+=0.1; /* Change to any number to adjust color cycle speed */
for (xx=0; xx<w; xx+=1) {
  perc=1-(xx/(w-1));
  colamt = perc * (colnum-1);
  colamt = (colamt + offset) mod colnum;
  col1 = colors[floor(colamt)];
  col2 = colors[ceil(colamt) mod colnum];
  col = merge_color(col1, col2, frac(colamt)); // LERP

  draw_sprite_part_ext(spr_blast, 0, xx, 0, 1, h, x+xx, y + AMP * sin((1 - perc) * FREQ + offset), 1, 1, col, 1);
}

So, for example, if I added this to the CREATE code:

colors[0] = c_red;
colors[1] = c_blue;
colors[2] = c_green;
colnum = 3;

AMP = 5;
FREQ = 10;

It would cycle between reed, then blue, then green, then back to red. The wave would have an amplitude of 5px and a frequency proportional to 10Hz.

-IMP

*EDIT* I realized the original code I had was actually completely reversed from what you wanted. It's been fixed above so that it goes the right way now (just a matter of inverting the percentage). Also, an offset speed of 1/step is extremely fast, so I've changed the default above to 0.1 so you can see the effect better. You can change that to whatever you like, of course.

Edited by IceMetalPunk, 02 April 2012 - 10:28 PM.

  • 1

#7 Follomania

Follomania

    Unum Mirabile

  • GMC Member
  • 445 posts
  • Version:GM:Studio

Posted 02 April 2012 - 08:30 PM

  merge_color()

Now that just made my life easier. :blink:
I knew that function existed, but I never thought to actually use it. :tongue:
  • 0

#8 esco

esco

    GMC Member

  • GMC Member
  • 209 posts

Posted 02 April 2012 - 11:15 PM

Hey IMP, great to hear from you. :)

modular arithmetic & linear interpolation


I need what, and what!? O_o Lol

Well I tried your code and it works excellently without the waviness part of the code included. But the minute I put in image_xscale and image_yscale, it gets all messed up. And then when I put it into the wave effect part of the code it totally comes out messed up. I tried a ton of things (one of which is listed below) and I couldn't get it to work. Any ideas here?

Also I am not sure what you mean by frequency variable? Are you talking about the scale, phase or wavelength var in my code?

//draw event.
//makes the wave go in 1 direction first
if (wave_size_increase ==0)
{
   if (phase < 30) {phase+=1/22;}
   
   else {wave_size_increase = 1;}
}

//then once it goes over enough it goes in other direction
else
{
   if (phase > 0) {phase-=1/22;}
   
   else {wave_size_increase = 0;}
}


//next 4 vars are needed for script to work & MUST be done like this
var size,shift,sx,sy;
width = sprite_get_width(sprite_index);
height = sprite_get_height(sprite_index);
xoff   = sprite_get_xoffset(sprite_index);
yoff   = sprite_get_yoffset(sprite_index);

//here it sets wether to do a wave horizontally
if (axis == 1) {size = height;} 

//or vertically (NOTE: I have it set to this)
else {size = width;}

offset+=0.01; /* Change to any number to adjust color cycle speed */
  

//makes it appear to glow more
draw_set_blend_mode(bm_normal);

//runs loops once for each pixel based on whether we use height or width
for (count=0; count<size; count+=1) 
{
   //this controls the wave effect
   shift = amplitude*sin(3.6*pi*((count/wavelength)+phase));
  
  colamt = count/(width-1) * (colnum-1);
  colamt = (colamt + offset) mod colnum;
  col1 = colors[floor(colamt)];
  col2 = colors[ceil(colamt) mod colnum];
  col = merge_color(col1, col2, frac(colamt)); // LERP
  
   //vertical wave

   sx = image_xscale*(count-xoff)+x;
   sy = image_yscale*(shift-yoff)+y;
   draw_sprite_part_ext(sprite_index, image_index, count, 0, 1, height, sx+count, sy + amplitude * sin(count/(width-1) * shift), image_xscale, image_yscale, col, image_alpha);

}

//sets draw back to normal so things drawn after won't glow
draw_set_blend_mode(bm_normal);

Edited by esco, 02 April 2012 - 11:15 PM.

  • 0

#9 IceMetalPunk

IceMetalPunk

    InfiniteIMPerfection

  • Retired Staff
  • 9259 posts
  • Version:Unknown

Posted 03 April 2012 - 12:22 AM

Hey IMP, great to hear from you. :)

modular arithmetic & linear interpolation


I need what, and what!? O_o Lol

Modular arithmetic simply means "wrap the values around a certain maximum", in this case the number of colors. This way, you can keep adding to the offset, and when you get past the last color you can just wrap around to the first color again. For example, (5 + 6) = 11, but (5 + 6) mod 7 = 4 because 11 wrapped around 7 is 4.

Linear interpolation just means that when you're given two values, you can find some intermediate value that scales linearly. So linearly interpolating, for example, the values 5 and 7 with an intermediate value of 0.5 will give you the number exactly 0.5 of the way between the two values: 6.

Well I tried your code and it works excellently without the waviness part of the code included. But the minute I put in image_xscale and image_yscale, it gets all messed up. And then when I put it into the wave effect part of the code it totally comes out messed up. I tried a ton of things (one of which is listed below) and I couldn't get it to work. Any ideas here?

If you need scaling, the code needs to be modified a tiny bit to account for that scaling.

Also I am not sure what you mean by frequency variable? Are you talking about the scale, phase or wavelength var in my code?

Like any wave, frequency = speed/wavelength. So it's inversely proportional to your wavelength.

Here's the code (untested) modified to allow for xscale and yscale:

/* DRAW */
w = sprite_get_width(spr_blast);
h = sprite_get_height(spr_blast);

offset+=0.1; /* Change to any number to adjust color cycle speed */
for (xx=0; xx < w; xx+=1) {
  perc=1-(xx/(w-1));
  colamt = perc * (colnum-1);
  colamt = (colamt + offset) mod colnum;
  col1 = colors[floor(colamt)];
  col2 = colors[ceil(colamt) mod colnum];
  col = merge_color(col1, col2, frac(colamt)); // LERP

  draw_sprite_part_ext(spr_blast, 0, xx, 0, 1, h, x + xx * image_xscale, y + AMP * sin((1 - perc) * FREQ + offset) * image_yscale, image_xscale, image_yscale, col, 1);
}

Note the only modifications are that the offsets to the x and y are multiplied by the scaling, and the drawn slices are also expanded to the scale factors. That should fix it to work with image_xscale/image_yscale, though again, I haven't tested it.

-IMP
  • 0

#10 esco

esco

    GMC Member

  • GMC Member
  • 209 posts

Posted 03 April 2012 - 03:34 AM

Sorry IMP, but that doesn't seem to work either. I don't see anything at all now when I used that code.

Isn't there an easier way to do this by drawing the sprite in a surface instead, then just drawing another square sprite that is a color strip over it anyway? I was trying to understand your code, but I have no clue why it would work even after messing with it for 2 hours.

EDIT: Here is the sprite for the cone: Posted Image

And now for the color pattern itself: Posted Image

Now my question would be is there a way to just use this cone sprite, somehow put this color pattern sprite over it, and move the color sprite left or right in a way that the colors would transfer to the cone sprite, thus creating the same effect as in the video above? Basically cutting out the colors into the shape of the cone, and sticking them onto it, then making the colors scroll over left or right?

I was messing around with surfaces some with this, but whatever I try I can't get it to work no matter what blend modes I use. But I was pretty sure I had seen someone do this before.

Edited by esco, 03 April 2012 - 04:05 AM.

  • 0

#11 IceMetalPunk

IceMetalPunk

    InfiniteIMPerfection

  • Retired Staff
  • 9259 posts
  • Version:Unknown

Posted 03 April 2012 - 01:22 PM

Strange...I just tested my code, and it works perfectly for me. I can't understand why it wouldn't work for you...

Yes, you can easily draw the sprite to a surface and use it as an alpha mask for your colors, but then it would cause issues when creating the wave effect (you'd have to make the surface large enough to fit the offset pieces, which means you'd need to (1) use a large surface which takes up VRAM and (2) destroy and re-create the surface whenever the scaling or amplitude change.

Considering my code worked in my own tests, I can't imagine why it wouldn't for you.

Here's the test file I'm using: http://dl.dropbox.co...orwave_test.gmk

You can use the arrow keys to change the x and y scales independently (up to 10x on each axis). Maybe look through there and figure out what you're doing differently that's breaking it? It's mostly just a direct copy of my code above, but still...

-IMP
  • 1

#12 brac37

brac37

    GMC Member

  • GMC Member
  • 765 posts
  • Version:GM7

Posted 03 April 2012 - 10:20 PM

In the first link of my signature you will find a method to cycle the hue of a sprite. The first part of it is a HSV-decomposition of the sprite, which is slow but only required once. The second part is making the hue-shifted sprite from the HSV-decomposition, which is fast enough to do in real-time, I guess. Maybe that can help.
  • 0

#13 esco

esco

    GMC Member

  • GMC Member
  • 209 posts

Posted 04 April 2012 - 07:23 PM

Imp: I don't know what the issue was with the original, but your gm file above worked fine. I even managed to implement code to affect the wave speed. But for some reason the original code from before just didn't work. It's odd.

Well anyway, thank you so much. You have once again helped me solve my issue and I repped you for it of course. :biggrin: Are you some kind of gfx effects specialist by any chance? I ask because while I have had many people that were nice enough to TRY and help me, you always seem to know how to accomplish exactly what I am looking for. And for the most part all I am really going to ask for help with on the forums will in fact be things like unique gfx effects and 3d things.

BTW, have you seen this: http://gmc.yoyogames...howtopic=512187

That thing is an absolute stroke of genius and has been extremely helpful to me since I found it a day ago. :cool:

Brac37: why would you send me a link to an old file that doesn't even work in 8.1? :confused:

Edited by esco, 04 April 2012 - 07:25 PM.

  • 0

#14 IceMetalPunk

IceMetalPunk

    InfiniteIMPerfection

  • Retired Staff
  • 9259 posts
  • Version:Unknown

Posted 05 April 2012 - 09:11 PM

Imp: I don't know what the issue was with the original, but your gm file above worked fine. I even managed to implement code to affect the wave speed. But for some reason the original code from before just didn't work. It's odd.

Well anyway, thank you so much. You have once again helped me solve my issue and I repped you for it of course. :biggrin: Are you some kind of gfx effects specialist by any chance? I ask because while I have had many people that were nice enough to TRY and help me, you always seem to know how to accomplish exactly what I am looking for. And for the most part all I am really going to ask for help with on the forums will in fact be things like unique gfx effects and 3d things.

BTW, have you seen this: http://gmc.yoyogames...howtopic=512187

That thing is an absolute stroke of genius and has been extremely helpful to me since I found it a day ago. :cool:

Brac37: why would you send me a link to an old file that doesn't even work in 8.1? :confused:

No problem :) . Glad it finally worked out in the end. No, I'm not a graphics specialist :P . I'm just a very visual thinker, so graphical work comes naturally to me. I'd be glad to help you out with graphics questions in the future (when I have time; I'm technically quite busy until mid-May), but I have very little knowledge of 3D work, so for that you'd have to ask around for help from other people.

And yeah, I've seen that. I don't use it much anymore, but it certainly was very helpful when I was first learning how blend modes worked. I still think it's an essential teaching tool for GM programmers who are starting out with blending modes.

-IMP
  • 0

#15 brac37

brac37

    GMC Member

  • GMC Member
  • 765 posts
  • Version:GM7

Posted 11 April 2012 - 06:19 PM

Brac37: why would you send me a link to an old file that doesn't even work in 8.1? :confused:


The hsv-decomposition does not work in versions above 7, I think. But I guess the hsv-composition with hue shift still works. You can do the hsv-decomposition with gimp or something.

EDIT: I uploaded a gm8 version which took me 15 minutes to make. So the "old file" was not such a big problem after all: you could have done the conversion yourself when you were interested, too.

Edited by brac37, 03 June 2012 - 09:41 PM.

  • 0




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users