• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

How would I do a sin city effect?

P

PhenomenalDev

Guest
Hello I was wondering how I would use a shader or script to make a sin city affect for my game where all the other colours on the screen are black and white whilst red pixels are still rendered, an example of this is russel in hotline miami:
any help would be appreciated, thanks :D.


EDIT: I have done this so for future reference use this: https://github.com/GameMakerDevsSlack/Sin_City_shader and change curve to 11
 
Last edited by a moderator:

Perseus

Not Medusa
Forum Staff
Moderator
Since this isn't a technical query, I've moved this topic to the Programming forum.

Anyways, I doubt a shader was used to do that in Hotline Miami. The simplest way (possibly used in HM as well) to achieve that would be to apply a grayscale shader on the application surface, then the coloured logo can be drawn onto the GUI layer using the Draw GUI event. Trying to desaturate the whole screen but the pixels with a specific colour would be unnecessarily complicated practice. But that depends on what you're trying to do.
 
P

PhenomenalDev

Guest
Sorry skip to 58 secs or so in the video it didn't save the time tag and that is on the psvita it wasn't on the pc as hm was made in gm8. So the goal is to replicate the effects at 58 secs not the guys overlay xD sorry about the timestamp not saving.

Edit: Changed the video to a better one :D
 
Last edited by a moderator:
P

PhenomenalDev

Guest
But the idea is the base sprites aren't black and white they are full coloured and the shader makes them black and white whilst preserving reds on the screen.
 
I would try something like this. Convert the rgb colour value of the pixel to hsv. based on the hue, you can define which range of colors you will accept as red. all other colours outside this range convert to grayscale.

You could do it in script by using get pixel functions but that would be slow. Shader is the way to go.
 
What I would do is start with a grayscale shader. Here would be a good place to start. Here is another site for more options.
Then all you need to do is compare the original color red component to the blue and green. If it is red enough, set the blue and green components to 0. If not, set the color to the grayscale color.

A couple ways to find how red the color is where 0 is red and 1 is not red:
  1. ceil((texColor.b + texColor.g) * 0.5) //texColor is the input color from the image you draw through the shader. This only accepts pure red as red.
  2. clamp(texColor.b + texColor.g - texColor.r / redWeight, 0.0, 1.0) //redWeight allows you to adjust the threshold. This allows colors close to red to be counted as partially red. redWeight of 1 would make yellow and purple red. Higher values make the colors have to be closer to red to be red.
Then all you need to do is mix your grayscale value with the red.

mix(vec4(1.0,0.0,0.0,1.0)*texColor,grayscale,redValue)

Try it out and post some code and I'll help.
 
P

PhenomenalDev

Guest
What I would do is start with a grayscale shader. Here would be a good place to start. Here is another site for more options.
Then all you need to do is compare the original color red component to the blue and green. If it is red enough, set the blue and green components to 0. If not, set the color to the grayscale color.

A couple ways to find how red the color is where 0 is red and 1 is not red:
  1. ceil((texColor.b + texColor.g) * 0.5) //texColor is the input color from the image you draw through the shader. This only accepts pure red as red.
  2. clamp(texColor.b + texColor.g - texColor.r / redWeight, 0.0, 1.0) //redWeight allows you to adjust the threshold. This allows colors close to red to be counted as partially red. redWeight of 1 would make yellow and purple red. Higher values make the colors have to be closer to red to be red.
Then all you need to do is mix your grayscale value with the red.

mix(vec4(1.0,0.0,0.0,1.0)*texColor,grayscale,redValue)

Try it out and post some code and I'll help.
I love the icon and thanks for the help and all but a guy on reddit made a thing on github for me and it seems to work perfectly.
 
Last edited by a moderator:
I love the icon and thanks for the help and all but a guy on reddit made a thing on github for me and it seems to work perfectly.
:( ok. I made the full shader though so at least you have options.
Code:
varying vec2 v_vTexcoord;
varying vec4 v_vColour;

const float redWeight = 2.0;

void main()
{
    vec4 texColor = texture2D( gm_BaseTexture, v_vTexcoord );
    vec3 lum = vec3( 0.30, 0.59, 0.11 );
    vec4 grayscale = vec4( vec3( dot( texColor.rgb, lum )), texColor.a );
    float notRed = clamp(( texColor.g + texColor.b ) - texColor.r / redWeight, 0.0, 1.0 );
    //the following removes the blend from red to grayscale
    //basically it removes pink colors
    //notRed = ceil( notRed );
    gl_FragColor = mix( vec4( texColor.r, 0.0, 0.0, texColor.a ), grayscale, notRed );
}
 
P

PhenomenalDev

Guest
:( ok. I made the full shader though so at least you have options.
Code:
varying vec2 v_vTexcoord;
varying vec4 v_vColour;

const float redWeight = 2.0;

void main()
{
    vec4 texColor = texture2D( gm_BaseTexture, v_vTexcoord );
    vec3 lum = vec3( 0.30, 0.59, 0.11 );
    vec4 grayscale = vec4( vec3( dot( texColor.rgb, lum )), texColor.a );
    float notRed = clamp(( texColor.g + texColor.b ) - texColor.r / redWeight, 0.0, 1.0 );
    //the following removes the blend from red to grayscale
    //basically it removes pink colors
    //notRed = ceil( notRed );
    gl_FragColor = mix( vec4( texColor.r, 0.0, 0.0, texColor.a ), grayscale, notRed );
}
Sorry at least anyone else who comes can use your solution. :(
 
Top