Xor Shaders
  • Tutorials
  • About

#8 wind shader

5/30/2016

20 Comments

 
Hey, I'm back! I haven't been very active on here for months because I was trying to make a new shader tutorial site. I still might on occasion, post a tutorial here (just simple ones for now), but I'm planning on making a more organized format on the new site. Anyway, by request I decided to make a tutorial about a 2D wind effect.

Ripple effect

To make our wind, we need a simple ripple effect. We will create a vec2 and call it "Coord". This will be used for the wind distortion. We will set it to "v_vTexcoord". Now you need to change the texture2D coordinates to our Coord vector. It should look like this:
 
​   
vec2 Coord = v_vTexcoord;
    gl_FragColor = v_vColour * texture2D( gm_BaseTexture, Coord);


This​ is a pass-through shader. If you run the shader applied to a sprite, it shouldn't change anything. To make a ripple we will use a cosine wave. We could do it like this:
​
    vec2 Coord = v_vTexcoord + vec2(cos(v_vTexcoord.y*30.0)/30.0,0);

If you run this you will see this (before and after):
Picture
 This is closer to what we want, but we need it to be animated. This works by shifting the X coordinates in a cosine wave shape. This makes a nice ripple. You can adjust the frequency by changing the first 30.0 and adjust the amplitude by changing the second 30.0. We'll come back to this at the end of the tutorial

Animating

​We need to add a uniform float and we'll call it "Time" and we'll add it in the cosine like this:

​    vec2 Coord = v_vTexcoord + vec2(cos(v_vTexcoord.y*30.0+Time*6.2831)/30.0,0);
​
It is multiplied by 6.2831 (pi times two) because cosine uses angles in radians so that it does a full cycle every second.
We'll set the time uniform in GameMaker like this in the draw event:

    var uTime = shader_get_uniform(shdr_wind,"Time");

    shader_set(shdr_wind)
    shader_set_uniform_f(uTime,current_time/1000)
    draw_self()

    shader_reset()

This will set "Time" to the number of seconds since the game started.
If you run this it will be animated. You may notice a problem where the base of the grass moves​
just as much as the top.

Fixing and perfecting

So to fix the problem I just mentioned we will multiply cosine by the" (1.0-v_vTexcoord.y)". This will make the distortion decrease as it approaches the bottom of the sprite. Here is what I did:

​    vec2 Coord = v_vTexcoord + vec2(cos(v_vTexcoord.y*30.0 + Time*6.2831)/30.0,0)*(1.0-v_vTexcoord.y);

It looks much better now. This shader is currently proportion to the sprite size. If it's long it will stretch the waves, but if it's short it will shrink the waves. That is because the waves are based off of the v_vTexcoord which is between 0 and 1. To fix this we can use the position attribute from the vertex shader because it uses the vertex coordinates rather than the texture coordinates.
 We need to add a varying vec2 called "v_vPosition" in the vertex and fragment shaders by the other varying vectors. Set it in the vertex shader like this:

    v_vPosition = in_Position.xy;

And back to the fragment shader use this for the coordinates:

    vec2 Size = vec2(256,128);
    vec2 Wave = vec2(48,5);

    vec2 Coord = v_vTexcoord +
vec2(cos((v_vPosition.y/Wave.x+Time)*6.2831)*Wave.y,0)/Size*(1.0-v_vTexcoord.y)​;

​
"Size" should be the size of the texture. Use a uniform if necessary. "Wave" is for adjusting the wave shape. The first number is the height of the wave and the second number is the amplitude.
This works by making a cosine wave of the chosen size with time added (multiplied by 6.2831 because of radians) and then multiplied by the amplitude; then divided by "Size" to get it between 0 and 1 and finally multiplied by the code I mentioned above to fix the bottom.

Thanks for reading.
DOWNLOAD EXAMPLE
​
Basic tutorials used:
#4 Vertex Shader
#2 Black and White Shader
#1 Shader Basics​​​​
20 Comments
Hilmi_A
8/17/2016 08:35:53 am

please do the scanline shader tutorial

Reply
Xor link
8/17/2016 10:06:08 am

Thanks for the suggestion. I will keep this in mind and may get to it when I finish my other projects.

Reply
Smartkin
8/26/2016 03:12:39 am

Hello, there is a problem I am experiencing using this or any other kind of such shader. For some reason no matter what size or wave I am passing sometimes game maker will start showing all the other sprites(in the atlas) that attached to the one I am using (like fonts, backgrounds and all the other sprites) as shown here. http://i.imgur.com/MigbYq6.png Is there any way to fix that?

Reply
Xor link
8/26/2016 04:48:15 am

The simple solution is to tick "Used for 3D". This will put the sprite or background on it's own texture page. I think there is a way you can use sprite_get_uvs to fix this, but I haven't been able to get it to work.
Hopefully that helped!
-Xor

Reply
Shaun Spalding link
9/24/2016 01:49:29 pm

You can also make sure there is an appropriate amount of transparent space on the sprite and have the "not cropped" setting ticked for the texture page.

Xor link
9/25/2016 06:56:20 am

Thanks for visiting Shaun! I'll look in to the method you described.

fernando
9/29/2016 03:13:35 pm

hi xor, have you any simple morphing 2 images shader? :)

Reply
Xor link
9/29/2016 06:58:33 pm

I don't have any examples that I can think of. It should be rather straight forward to make however. You would need a uniform sampler2D. The do something like this:

vec4 Tex1 = texture2D(gm_BaseTexture,v_vTexcoord);
vec4 Tex2 = texture2D(SamplerTexture,v_vTexcoord);
vec4 TexFinal = mix(Tex1,Tex2,Fade);

In this case "Fade" is a float to control the blend. 0 meaning completely Tex1 (the default texture) and 1 meaning Tex2 (the uniform texture).

Reply
fernando
10/6/2016 07:23:30 pm

thanks, i will try with that :)

nook
12/23/2016 05:13:25 pm

can you give me an example please Im confused

Xor link
12/23/2016 07:49:34 pm

I don't have the time to put together an example, but what are you confused about? Maybe I can still help!

Nook
12/24/2016 08:53:29 am

I ment like where do I put this code?:

vec4 Tex1 = texture2D(gm_BaseTexture,v_vTexcoord);
vec4 Tex2 = texture2D(SamplerTexture,v_vTexcoord);
vec4 TexFinal = mix(Tex1,Tex2,Fade);

With this code:

//fragment
varying vec2 v_vPosition;
varying vec4 v_vColour;
varying vec2 v_vTexcoord;

uniform float Time;
vec2 Size = vec2(,128);
vec2 Wave = vec2(48,5);

void main()
{
vec2 Coord = v_vTexcoord + vec2(cos((v_vPosition.y/Wave.x+Time)*6.2831)*Wave.y,0)/Size*(1.0-v_vTexcoord.y);
gl_FragColor = v_vColour * texture2D( gm_BaseTexture, Coord);
}

//vertex
attribute vec3 in_Position; // (x,y,z)
attribute vec4 in_Colour; // (r,g,b,a)
attribute vec2 in_TextureCoord; // (u,v)

varying vec2 v_vPosition;
varying vec4 v_vColour;
varying vec2 v_vTexcoord;

void main()
{
vec4 object_space_pos = vec4( in_Position, 1);
gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * object_space_pos;

v_vPosition = in_Position.xy;
v_vColour = in_Colour;
v_vTexcoord = in_TextureCoord;
}

Of course the wind shader code works nicely:)

I just went to try morphing 2 images while destorted.

I try to put this code like this:

varying vec2 v_vPosition;
varying vec4 v_vColour;
varying vec2 v_vTexcoord;

uniform float Time;
vec2 Size = vec2(16,128);
vec2 Wave = vec2(12,5);
vec4 TexFinal = mix(Tex1,Tex2,Fade);

void main()
{
vec2 Coord = v_vTexcoord + vec2(cos((v_vPosition.y/Wave.x+Time)*6.2831)*Wave.y,0)/Size* (1.0-v_vTexcoord.y);
gl_FragColor = v_vColour * texture2D( gm_BaseTexture, Coord);
vec4 Tex1 = texture2D(gm_BaseTexture,v_vTexcoord);
vec4 Tex2 = texture2D(SamplerTexture,v_vTexcoord);
}

or like this

varying vec2 v_vPosition;
varying vec4 v_vColour;
varying vec2 v_vTexcoord;

uniform float Time;
vec2 Size = vec2(256,128);
vec2 Wave = vec2(48,5);
vec4 TexFinal = mix(spr_1,spr_2,0);

void main()
{
vec2 Coord = v_vTexcoord + vec2(cos((v_vPosition.y/Wave.x+Time)*6.2831)*Wave.y,0)/Size*(1.0-v_vTexcoord.y);
gl_FragColor = v_vColour * texture2D( gm_BaseTexture, Coord);
vec4 Tex1 = texture2D(gm_BaseTexture,v_vTexcoord);
vec4 Tex2 = texture2D(SamplerTexture,v_vTexcoord);
vec4 TexFinal = mix(Tex1,Tex2,Fade);
}
Or tried on vertex but all of them didn't work I keep getting errors

right now Im out of ideas how to do this.

If you don't have time or don't know as well thats okey.

Xor link
12/25/2016 12:04:53 pm

Something like this should work (make sure the uniforms in GML):
varying vec2 v_vPosition;
varying vec4 v_vColour;
varying vec2 v_vTexcoord;

//It's important that these get set in GML
uniform sampler2D SamplerTexture;//Secondary texture
uniform float Fade;//Should be between 0 and 1 where 1 is the second texture.

uniform float Time;
vec2 Size = vec2(16,128);
vec2 Wave = vec2(12,5);
vec4 TexFinal = mix(Tex1,Tex2,Fade);

void main()
{
vec2 Coord = v_vTexcoord + vec2(cos((v_vPosition.y/Wave.x+Time)*6.2831)*Wave.y,0)/Size* (1.0-v_vTexcoord.y);
vec4 Tex1 = texture2D(gm_BaseTexture,v_vTexcoord);
vec4 Tex2 = texture2D(SamplerTexture,v_vTexcoord);
gl_FragColor = v_vColour * mix(Tex,Tex2,Fade);
}

Nook
12/26/2016 07:22:05 am

Like this:)

//create
SamplerTexture = shader_get_uniform(shd_distort_2,SamplerTexture);
Fade = shader_get_uniform(shd_distort_2,Fade);

If I got it right what do I do with the draw event.

Xor link
12/26/2016 08:23:29 am

Try it like this (untested):
//Create event
uSampler = shader_get_sampler_index(shd_distort_2,"SamplerTexture");
Sampler = -1;//Set to a texture id!
uFade = shader_get_uniform(shd_distort_2,"Fade");
Fade = 0.5;//Variable for fading (between 0 and 1).
//Draw event
shader_set(shd_distort_2);
texture_set_stage(uSampler,Sampler);
shader_set_uniform_f(uFade,Fade);
//Draw stuff
shader_reset();

If this doesn't work, or you don't understand, please read my tutorial on uniforms (http://xorshaders.weebly.com/tutorials/blur-shaders-5-part-2).

Nook
12/26/2016 10:35:01 am

I'm still keep these errors like:

//fragment
if I set it like this: vec4 TexFinal = mix(spr_ghost,spr_ghost_2,1);

I get this:

In shader shd_distort_2 at line 13: 'spr_ghost' :undeclared identifier.
In shader shd_distort_2 at line 13: 'spr_ghost_2' :undeclared
identifier.
In shader shd_distort_2 at line 13: 'mix' :no matching overloaded function found.
In shader shd_distort_2 at line 13: '=' :connot convert from 'const mediump float' to '4-component vecter of float'
In shader shd_distort_2 at line 20: 'Tex' :undeclared identifier.
In shader shd_distort_2 at line 20: 'mix' :no matching overloaded function found.

or

//fragment
If leave it like this: vec4 TexFinal = mix(Tex,Tex2,Fade);

I get also this:

In shader shd_distort_2 at line 13: 'Tex' :undeclared identifier.
In shader shd_distort_2 at line 13: 'Tex2' :undeclared identifier.
In shader shd_distort_2 at line 13: '=' :connot convert from 'const mediump float' to '4-component vecter of float'
In shader shd_distort_2 at line 20: 'mix' :no matching overloaded function found.

Any ideas. ¯\_(ツ)_/¯

It tells me that theres something wrong with the shader.
It didn't tell me anything wrong about the create or draw event.

Chrys
10/24/2017 12:39:58 am

Hi. Very good tutorial. Looks very good. But I have a problem. I have made several plants, which turn and move with the land on which they are. After a while, the plant movement is jammed (jerky), although the game is cursive. I can not get it. Please help. Thanks.

Reply
Xor link
11/7/2017 11:38:33 am

Hello! Sorry for the delayed response! If you want help please use the contact page and describe the issue in good detail.

Reply
jonPS
12/13/2018 09:15:22 am

hello xor
a question a bit off topic.
do you know how to set multiple shaders working together ?
In photoshop multiple effects can be applied to an image. In GM the way i see it you ll need an 3 applications surfaces for 3 shaders, i think this can only be done with 3 cameras.

Ive been trying to merge your blur shader with your pixelate shader, but they dont mix only seem to work 1 of time and i get a weird effect... whats the trick to merge all shaders into one ?

blur + pexelate

varying vec2 v_vTexcoord;
varying vec4 v_vColour;

uniform vec4 pixel;//background width, background height, pixel width, pixel height // - pixelate
uniform vec3 size;//width,height,radius - BLUR

const int Quality = 8; // - BLUR
const int Directions = 16; // - BLUR
const float Pi = 6.28318530718;//pi * 2 // - BLUR


void main()
{
vec2 ts = vec2(pixel.z*(1./pixel.x),pixel.w*(1./pixel.y)); // - pixelate
vec2 coord = vec2(ts.x*floor(v_vTexcoord.x/ts.x), ts.y*floor(v_vTexcoord.y/ts.y)); // - pixelate

vec2 radius = size.z/size.xy; // - BLUR
vec4 Color = texture2D( gm_BaseTexture, v_vTexcoord); // - BLUR


for( float d=0.0;d<Pi;d+=Pi/float(Directions) ) // - BLUR
{
for( float i=1.0/float(Quality);i<=1.0;i+=1.0/float(Quality) ) // - BLUR
{
Color += texture2D( gm_BaseTexture, v_vTexcoord+vec2(cos(d),sin(d))*radius*i); // - BLUR
}
}
Color /= float(Quality)*float(Directions)+1.0; // - BLUR

gl_FragColor = (Color * v_vColour) + texture2D(gm_BaseTexture, coord);
}

Reply
Ito
2/21/2021 10:48:53 am

Thank you very much for this. Great work!

Reply



Leave a Reply.

    Tutorials

    New tutorial at GMshaders.com!

    Categories

    All
    3D
    Beginner
    Intermediate

    Archives

    October 2021
    November 2019
    January 2019
    May 2016
    August 2015
    June 2015
    March 2015
    February 2015
    January 2015
    December 2014

    RSS Feed

Powered by Create your own unique website with customizable templates.