Shaders are amazing. You can do really pretty stuff very easily. So in the long run, it makes your game prettier
and saves time making resources. We will cover what a shader is real quick,
and then teach you how to write what you need for your game. We compress hours worth of information in
here, so your head might hurt a bit. You can imagine a shader as a way to teach
a GPU how to draw a certain object. This was originally meant for lighting etc,
but quickly became used for all types of visual effects. This origin shines through at many points,
and explains why some things don't work on the GPU or are slower than you would expect. The important takeaway is the mindset. GPUs are massively parallelized. So we don't think in the scope of a whole
picture, we only describe what to do with a single pixel. And this is done for every pixel. Let us start with the examples. We will be using Godot, but it works similarly
for other tools. We open it, add a texture and give it a material,
which holds parameters for our shader. Then we add the shader itself. To make a 2D shader, we write shader_type
canvas_item. We type out a fragment function, this means
we manipulate the image pixel-wise. This function has no return, but we will compute
the COLOR variable which holds the color of the pixel. For a shader that just reads an image and
draws it, we use the texture method. It takes two arguments: the texture to sample,
and where to sample it. We can use the texture that is assigned with
TEXTURE, and for the place… Well, we have to meditate on this. We remember that the code is run for every
pixel, in order to tell them apart we have their coordinates within the texture. This is called the UV coordinates, and always
is between 0 and 1 in both x and y dimension. We can reference our UV, with the “UV”
keyword. The texture call needs such a coordinate to
sample the texture. So let's just give it our coordinate first. We just get the color value we expect and
can assign it to the COLOR variable. The shader will draw whatever is assigned
to the COLOR variable. So now we can see what our changes do. Understanding UVs is the most important thing,
and a bit of a headache at first. So let's play around. When we multiply the UV by 2, the texture
is sampled more densely, so it will be smaller. The center of the sprite that is at UV 0.5,0.5
is mapped to 1,1, the bottom right corner of the texture. We can interchange this 2 with a constant,
defined above here. Or with a uniform. A uniform is a kind of variable and how we
get information from the CPU to the shader. It can not be manipulated in the shader and
always is the same for every pixel. Let's make the 2 into a uniform. We can give it an editor hint so we get a
nice slider. When we now slide it around we see that we
learned to scale images dynamically. We can also do distortion-like effects. For example, by adding a part of the Y coordinate
to the X coordinate. Try to play around for yourself to get the
hang of it! If we ever want the graphic to repeat, we
can change this in the import settings. We can also manipulate the color. Let us try and make the image greyscale. For this, we need to reference the different
color channels separately. We can access the components of a vector with
.r for red, .g for green .b for blue and .a for its alpha value. Alpha is the transparency of that pixel. So when we average the r,g and b value, we
have a greyscale value. We can write the same value on all of the
channels, and there it is! I read some people had problems with the builtin
modulate, because it can not add color that is not there. It’s only multiplicative. So let's fix that. We can simply multiply the greyscale value
with a color, to color that image. For that we add a color uniform, with the
following line. We now can choose a nice color, and the image
will change in real time. Many games also need a flash effect. For this, we just need one more uniform to
decide how flashed the graphic is. Get rid of the greyscale. Instead, we now interpolate between the flash
color and the current color. Luckily, this is very easy with the mix function. You say what color is interpolated towards
what color, and how to mix between those two. Use the original image color, the flash color
and our interpolation uniform. We can play with this slider and see the magic! The cool thing is, each uniform can be animated
by an animation player or tween. So maybe add an animation player and change
the flash state. It can even animate the color! When you have multiple sprites with the same
material, this will change all of them, so we need to make the material local to scene. There is one more trick you need to know before
we get into the crazy stuff. Swizzeling. With swizzeling we can use vectors very creatively
and access the components in a really cool way. Let's say we want to swap colors around. You can write COLOR.rgb = COLOR.gbr and they
will swap around. You can even use the same color multiple times,
like COLOR.rgb = COLOR.rrr to write the same one everywhere. Keep in mind, this is not the same as the
greyscale shader, it just switches the red channel to grey and throws away the rest! We will now do a bit of advanced magic! Fancy distortion effects such as the typical
explosion shockwave are made by reading the screen itself. You can access the screen texture by writing
SCREEN_TEXTURE. If we use the UV to sample it, we see the
screen in small and flipped. This is because the 0,0 of the sprite is up
here, and it is mapped to the 0,0 of the screen. We usually want to sample the point directly
below the image, so we use the SCREEN_UV. This shader now does absolutely nothing. But we can make the same adjustments and even
the same distortions. So let us distort the screen uv, for example,
based on our image. If we use different images, we can get different
results. We can also generate a texture for this purpose. To give it to the shader, add a sampler2D
uniform. There are really helpful texture types that
you can generate inside the engine. We can add a noise texture filled with simplex
noise, which is basically cleaner perlin noise, with a lot of parameters. With a bit of playing around you can do cool
effects with that. But it's a little complicated, so we linked
a video about that in the description. There are also gradients, which are great,
for example, to overlay them onto the sprite or to map the sprite’s colors to the gradient. For this, simply use the brightness and use
it to address the gradient for this nice effect! And lastly, curves are great too. When given to a curve texture, they are translated
into a grayscale image. We used them for this black hole for example! There are other types of shaders as well. Vertex shaders can manipulate the position
of objects. They are most impressive in 3D, so we will
switch to 3D. We can use the vertex function and now have
access to the VERTEX keyword. With this we can take this boring plane, and
add a sinus wave to the Y coordinates. We can make this shift if we want, either
using a uniform or based on the time. To make it time-based we use the TIME builtin,
and add it to the sinus. Now you can see fancy wave effects. You can also sample a texture in the vertex
function. The fragment function still exists here. Let's add a part of the UV to the color, just
to see a little gradient to visualize UVs once more! But instead of COLOR, we set ALBEDO and ALPHA. By the way, when you create a SpatialMaterial,
you can convert it to a shader material. This way, you can add your own shader code
to existing materials.. Shaders can be used for many many more things. You can control particles with them, because
particles are more or less just vertices. We made a whole video about that. You can also apply it to the visuals of a
particle and do the wildest stuff with that! We have a whole playlist down in the description
with different effects you can do. If this was helpful to you, please give the
video a like! If enough people are interested, we can go
for a part 2!