Generating noise/static?

Kyon

Member
Hi everyone

I'm working on how to visualize "health" or being dmg in my game
and I thought of this little noise-gradient:


So on the top you're damaged.
But I would like the noise to be animated like static noise.
Right now it's just a large png I made in photoshop, but it costs around 2mb.
Is there a way to do this in code, and is there a way to generate noise on a gradient?
(I looked online but couldn't really find anything about static noise)

17:15 update:
Ok so new question really, how do I remove these lines in my old shader:
Heya, thanks for this, very interesting and helpful!
Are you a bit of an expert with shaders?
Because I kind of have a noise shader for a while that I really like, but it gives these "lines":


Do you maybe have a way for my shader to remove those? I really like the look/feel on the shader (next to those annoying lines...)
I believe this is the important code within fshader:
Code:
varying vec2        v_vTexcoord;
varying vec2 var_texcoord;

float random(vec3 _scale, float _seed)
    {
        return fract(sin(dot(vec3(var_texcoord, 4.0)+_seed, _scale))*u_noise+_seed);
    }

void main() {

    vec4 base = texture2D( gm_BaseTexture, var_texcoord );

        float noise_strength = u_settings.z;
        float noise = noise_strength * ( 0.5 - random( vec3( 1.0 ), length(vec2(var_texcoord.x/var_texcoord.y, var_texcoord.x)) ) );
        if (u_settings.w == 1.0) {vignette_colour += noise;}

///

gl_FragColor    = vec4((v_vColour * vec4(base.rgb * vignette_colour, base.a)) + out_col) ;

Yeah I removed a lot of code. I made multiple shaders into one shader. This one does shockwave effect, vignette and noise.
Something I collected(from a few posts and tutorials)/wrote over the years and now always use. But those lines in the noise part are very annoying.
(it's not a part of the vignette or other stuff, I had this when I first started)

I has something to do with
Code:
length(vec2(var_texcoord.x/var_texcoord.y, var_texcoord.x))
Because that is sort of how it "wraps" around or something.
Here is a picture of the u_noise on a very low number:


So you can see the actual effect. But I'm just not sure why those lines exist. It should just like cover it up with more noise because the u_noise is such a large number?

In the first gif, the noise is moving because I'm randomly generating the variable "u_noise" to somewhere between 30k-50k I believe.

Idk, I just can't wrap my head around shaders even though I tried for a long while.



Kyon.
(if you need more info I'm here)

Kyon.
 
Last edited:

NightFrost

Member
Could be done with a shader that uses pseudorandom number generation and manipulates alpha for the gradient. Are you familiar with writing shaders?
 

Kyon

Member
Could be done with a shader that uses pseudorandom number generation and manipulates alpha for the gradient. Are you familiar with writing shaders?
Not really, right now I made some shaders by mostly following tutorials. But I must say, I don't reeeeaally get it yet.
I just stumbled upon bktGlitch shader which has a noise-thingy in it which I'm looking it.
But anyone who can push me in the right direction would be amazing.
 

NightFrost

Member
Ok, I lack the time to experiment myself (I haven't done a shader exactly like that myself) but here's the basic idea: you grab a function for your shader that generates pseudorandom numbers. You seed the function call with screen coordinate position (gl_FragCoord) plus a time variable. The time variable you send in from GameMaker as a uniform, and increase it every step. You do this because there is no true random in shaders, only math that makes it look like random. When those formulas receive a specific seed value, they always return a same "random" result. So just using coordinates for noise creates a noisy pattern, but it will not change from step to step. You must add a different value to it each step to make the noise change. Easiest is a value that increases by one every step.

To make actual noise from the pseudorandom number, you scale it to 0-1 range (in case the function doesn't do that already) and set it as alpha value for pure white. Or whichever color the noise has to be. To create a gradient, you subtract a value from the alpha. That value is calculated from y-position, so that final alpha decreases towards the middle from top and bottom. You'll have to bring in screen height as another uniform into the shader so you can set the gradient values to correct height.
 

Yal

šŸ§ *penguin noises*
GMC Elder
I'd say the PNG method still has a lot of merit... by drawing the sprite at a random position each frame, you get additional randomness proportional to its area. This quickly makes it impossible for the naked eye to see that you're using the same image each frame. (Randomly rotating or flipping it adds additional randomness to this, since the image will feel distinct when mirrored or rotated) This means you can draw a smaller image tiled to fit the entire screen instead of having a screen-sized one, too! (Also, if you overlay two independently moved/rotated/flipped copies of the image instead of drawing just one, you get polynomial randomness growth)
 
I want to show you just how easy it can be to do this with a shader. See spoilers if interested.
draw event
Code:
    shader_set(sh_noise);
    shader_set_uniform_f( shader_get_uniform( sh_noise, "time" ), random(pi*2) );
    draw_rectangle(-1,-1,1,1,0);
    shader_reset();
vertex shader
Code:
    attribute vec3 in_Position;
    varying vec2 v_vPosition;
    void main() {
        gl_Position = vec4( in_Position.xy, 0.0, 1.0 );  //rectangle from -1 to 1 will exactly fit view
        v_vPosition = in_Position.xy / vec2( gm_Matrices[MATRIX_PROJECTION][0][0], gm_Matrices[MATRIX_PROJECTION][1][1] );  //dividing by size of view (orthographic only), so effect is consistent with different view sizes.
    }
fragment shader
Code:
    varying vec2 v_vPosition;
    uniform float time;
    float random (vec2 st) { return fract( sin(time + dot(st.xy,vec2(49.4536,43.8641)))*398645.7252); }
    void main() { gl_FragColor = vec4( random( v_vPosition ) ); }  //just setting all channels rgba to the same random value.

You can use any random function that gives results that you like.

The real question is what do you want to do with the random value. You've got four colour channels, rgba, and you can decide how the random value affects each one of those channels in any way that you'd like. Additionally, you can explore how different blending modes will affect it.
 

Dr_Nomz

Member
Well you can make an image file smaller (well, in some cases anyway) by making sure to check the correct compression settings. Play around with it a bit in Photoshop and see what you can do. PNG is great for this cuz there are a lot of options to select, and I use the max compression to get the smallest files. It doesn't even seem to mess with the image's quality. :D

Hey how big is the PNG? Just curious. (I'd like to compare to some of my images)
 

Kyon

Member
I want to show you just how easy it can be to do this with a shader. See spoilers if interested.
draw event
Code:
    shader_set(sh_noise);
    shader_set_uniform_f( shader_get_uniform( sh_noise, "time" ), random(pi*2) );
    draw_rectangle(-1,-1,1,1,0);
    shader_reset();
vertex shader
Code:
    attribute vec3 in_Position;
    varying vec2 v_vPosition;
    void main() {
        gl_Position = vec4( in_Position.xy, 0.0, 1.0 );  //rectangle from -1 to 1 will exactly fit view
        v_vPosition = in_Position.xy / vec2( gm_Matrices[MATRIX_PROJECTION][0][0], gm_Matrices[MATRIX_PROJECTION][1][1] );  //dividing by size of view (orthographic only), so effect is consistent with different view sizes.
    }
fragment shader
Code:
    varying vec2 v_vPosition;
    uniform float time;
    float random (vec2 st) { return fract( sin(time + dot(st.xy,vec2(49.4536,43.8641)))*398645.7252); }
    void main() { gl_FragColor = vec4( random( v_vPosition ) ); }  //just setting all channels rgba to the same random value.

You can use any random function that gives results that you like.

The real question is what do you want to do with the random value. You've got four colour channels, rgba, and you can decide how the random value affects each one of those channels in any way that you'd like. Additionally, you can explore how different blending modes will affect it.
Heya, thanks for this, very interesting and helpful!
Are you a bit of an expert with shaders?
Because I kind of have a noise shader for a while that I really like, but it gives these "lines":


Do you maybe have a way for my shader to remove those? I really like the look/feel on the shader (next to those annoying lines...)
I believe this is the important code within fshader:
Code:
varying vec2        v_vTexcoord;
varying vec2 var_texcoord;

float random(vec3 _scale, float _seed)
    {
        return fract(sin(dot(vec3(var_texcoord, 4.0)+_seed, _scale))*u_noise+_seed);
    }
 
void main() {
 
    vec4 base = texture2D( gm_BaseTexture, var_texcoord );

        float noise_strength = u_settings.z;
        float noise = noise_strength * ( 0.5 - random( vec3( 1.0 ), length(vec2(var_texcoord.x/var_texcoord.y, var_texcoord.x)) ) );
        if (u_settings.w == 1.0) {vignette_colour += noise;}

///

gl_FragColor    = vec4((v_vColour * vec4(base.rgb * vignette_colour, base.a)) + out_col) ;

Yeah I removed a lot of code. I made multiple shaders into one shader. This one does shockwave effect, vignette and noise.
Something I collected(from a few posts and tutorials)/wrote over the years and now always use. But those lines in the noise part are very annoying.
(it's not a part of the vignette or other stuff, I had this when I first started)

I has something to do with
Code:
length(vec2(var_texcoord.x/var_texcoord.y, var_texcoord.x))
Because that is sort of how it "wraps" around or something.
Here is a picture of the u_noise on a very low number:


So you can see the actual effect. But I'm just not sure why those lines exist. It should just like cover it up with more noise because the u_noise is such a large number?

In the first gif, the noise is moving because I'm randomly generating the variable "u_noise" to somewhere between 30k-50k I believe.

Idk, I just can't wrap my head around shaders even though I tried for a long while.



Kyon.
(if you need more info I'm here)
 

Kyon

Member
Well, what is it about that noise pattern that you want to preserve?
idk, the colors? Can you get the same effect with yours?


EDIT:
implemented your noise shader, and it kind of looks ok. But ofc its just black/white noise now. How do I get this to be more film-grainy. Like in my gif.
 
Last edited:

Ido-f

Member
What you're after should be a "post processing" effect. Look up how to implement those (basically, turn off automatic drawing of the application surface, then set the post-process shader before drawing the application surface manually in the appropriate draw event (I don't remember which one)).
That way, the shader will apply to the whole image at once.

Then you'd have to combine the original image's pixel (commonly referred to as a "fragment" in shaders), which you sample using the texture2D found in the default shaders, with the noise's pixel, produced using a random function such as flyingsaucerinvasion's.
That combination could be done either by multiplication of the two pixels' vec4 values, addition of them (which you would probably want to offset by reducing the resulting brightness), or some other method.

Finally, you could probably use a fade-in uniform to control the effect. Determine by the gameplay to what extent you want the effect to apply at any given time: It would look good transitioning smoothly from 0 to 1. Set a uniform to it, then set gl_FragColor to mix(original_image_pixel, combined_image_and_noise_pixel, uniform_fade_in).

I read the comments again and figured you probably already know all that stuff.

I don't know about the film-grainy look, but I would like to add, on the gradient that you were looking for in the opening post, that what I would do is to pass a normalised uniform of what height the player is on the screen, produce a fade value from the height difference of each fragment and that uniform (something like smoothstep(no_effect_screen_percentage, partial_effect_screen_percentage, abs(player_height-v_vTexcoord.y)), then set gl_FragColor to mix(original_image_pixel, combined_image_and_noise_pixel, height_difference_fade_in * other_fade_in_factors).
You can replace player_height with 0.5 to keep the effect centred at the screen, rather than following the player.
 
Last edited:
The visual appearance is going to mostly be based on what you're drawing, how you use the random value, and which blending modes you use (if applicable). I would just try to use different arguments to your random function, until I find one that doesn't produce obvious artifacts.
 

Kyon

Member
The visual appearance is going to mostly be based on what you're drawing, how you use the random value, and which blending modes you use (if applicable). I would just try to use different arguments to your random function, until I find one that doesn't produce obvious artifacts.
I tried but nothing really gets me a clear grainy/noise filter.
It just doesn't make sense to me :(
 

Kyon

Member
OK SOLVED IT.
I put the
Code:
float noise = noise_strength * ( 0.5 - random( vec3( 1.0 ), length(vec2(var_texcoord.x/var_texcoord.y, var_texcoord.x)) ) );
On a higher random value. So the 1.0 is now 20.0 and it solved it so I'm just not touching this ever again.


Thanks for all the help.
 
Top