Check out a free preview of the full Creative Coding with Canvas & WebGL course

The "Shaders" Lesson is part of the full, Creative Coding with Canvas & WebGL course featured in this preview video. Here's what you'd learn in this lesson:

Matt conceptualizes shaders and GLSL, and explains how shaders work in practice using gradients.


Transcript from the "Shaders" Lesson

>> Mathew DesLauriers: So far we've got some scene. We've got everything animated. We've been talking about how to position things, how to export them as GIF, and things like that. And we could go on this sort of path a bit more. We could try and animate each individual cube. Or we could try and rotate each individual cube or something like that.

But I just wanted to move forward and pave ahead with the next kind of concept, which would be around shaders. And then after we get into shaders, we'll have a bit more time to just relax and chill. So for now I'm just going to close that.
>> Mathew DesLauriers: So what is a shader?

So a shader is a small little program that looks a bit like this and it's a small little program that is meant to do a single task and it's meant to do a single task really really well. And it's actually not even in JavaScript so when we look at this we're gonna see some things that look familiar but it's using a different language.

It's called GLSL and GLSL is a shader language for WebGL or for OpenGL and it has some different syntax. It's a bit more like C rather than JavaScript. And it is composed of these different things here. So we have some stuff at the top of the program which specifies the floating point precisions so, this is sort of something that we don't really need to deal with in JavaScript but it's basically telling us how precise our numbers are.

And we usually just wanna keep it at high p which it means high precision. That just means accurate numbers for now and then we have some inputs. These are our values. Usually numbers, sometimes like an opacity or like some sort of information that's stored in a number or a coordinate.

Those are coming from WebGL. And they're called varying. We're gonna get into what all these mean, but I'll just walk you through them. And it's using vec2, so unlike JavaScript where we just say var x is equal to 3, we actually have to say what that type is.

So it's a bit more like c or c++ or something like that, or Java maybe, something like that, where you have to say that this variable VUV is actually of type vec2. And vec2 is a type that means two dimensional vector, or coordinate. Like XY position or something like that.

And then you can also use float types. So basically, we're just going to be dealing with vec2s, and vec3s and vec4s. And float types. And then there's these other things called uniforms, and we're going to get into what all these mean. But basically, just think of shaders as little tiny programs that look like this.

And one way to try and visualize and conceptualize what a shader is, is to consider an image, like triangle, a colored triangle. But instead of just looking at it as an image or a graphic or a vector image, try to image it as a grid of pixels. And you can zoom in on the image and start to see that it is really just made up of little tiny square pixels.

If we were to zoom in further, if we were to pixellate it further, we'd see really it is a grid. Just like the grids we've been working on so far in our workshop and it's just a grid of different squares that are different colors. And what the shader is doing, is the shader is this piece of code that just says, how does each individual pixel get colored.

And so the shader is getting run, potentially millions of times because the shader is getting run. It's like a function that gets run on every single pixel in a shape. And so in this image, it's a very chunky pixellated grid, and there's maybe like a 100 or 200 or 300 of these little colored squares.

And so that shader would have to get run 300 times. But in real, like in real images, we're not dealing with these big chunky pixels. We're dealing with pixels that are so small that we can hardly even see them. And so really in this kind of an image, you might have a shader that's run a million times, because it's being run for each individual pixel.

And the shader is just running on those pixels, and it doesn't know anything about the other pixels this is something that's one of the core concepts of working with shaders. And it really changes the way you have to think about writing your code. You can no longer say that I have a pixel that's blue because the pixel next to it is red.

You have to only say that this pixel is blue because. It doesn't know anything about its neighbors. So a shader, it has to be the same code that runs across all these different pixels, and it's just simply there's too many pixels for it to have information about what's happening in the pixel up here or the pixel down here.

Just all it knows is, okay, I'm gonna make something blue, I'm gonna make something red, pink, yellow, that's all. And so this is kind of what we're gonna try and pick apart and dissect today. And there's some helper functions as well that we're gonna work with, just like we did with three.js.

So it'll hopefully make it easier, but yeah, we're gonna work on that. And so let's say we're, for example, gonna try and reproduce this image, this exact image with a shader. This is a very simple image to start with. And it's just a grayed-in from gray to white.

And it might look like this if we were to recreate this exact image with a shader. And the way it's going to work is that we specify a precision at the top. This is kind of you copy paste it, you always use the same one, precision hype float.

And then we have some variables. So in JavaScript we just have our x is equal to five or whatever. Whereas in shaders we have two different types of variables that we can think of in a fragment shader. And sorry we're talking about specific type of shader that renders pixels.

That's just kind of something that we're talking about today. There's other types of shaders in more complex applications that render different types of functions, sometimes you'll actually use shaders in like machine learning or competition that doesn't actually render anything, it's used for really fast programming and really fast competition.

We're just talking about creating color with shaders, and so back to this thing here. So there's two types. There's something called varying and a varying is a, usually a coordinate or a number or a value that's coming from WebGL, and the reason it's called varying is because if you're to imagine the surface, the value changes, it varies across the surface.

As it moves across the surface the value is not the same. So up here the value might be one or two or three or whatever down here might be a different value that's why it's called varying. This other type of data we're working with. And shaders is called uniform.

So those are the two types varyings and uniforms and uniform, it's called uniform because the value is the same across all of the different pixels in our surface. It's uniformly constant. It's uniform. So those are the two types of data that we have to try and remember to try and wrap our heads around.

>> Mathew DesLauriers: So back to this image, we have a varying which is called vec2, VUV which is basically a 2D coordinate called VUV, which is a UV coordinate and if you remember back to earlier in the workshop. We we're dealing with the grid and we had the top left to the grid was zero zero, and the bottom right of the grid was one one, and I was referring to that as UNV, this is very similar.

This is using UNV coordinate just to classify where in the surface of the image we are. And so in this image, the UNV coordinate might be similar where you know, on one end is zero zero and on the other end is one one. Something a bit like that.

It's a very similar concept. And then, what we can do is we can create a color and an opacity, using this varying value. We can create the color, using the X property of the vector, and that's basically just saying I want the U coordinate, and we can then wrap that with this structure called a vec3, and then we can create an opacity which is just gonna be one for now, which means 100% opacity.

And when we wrap that structure, because we have a three component structure here, and a one component here, that ends up being four different components. That's why we call it vec4. So it's some new concepts, and we're gonna try and work our way through it. But this goes back to this UV coordinate grid that I was chatting about before.

So let's say you were to create this gradient ramp. You might end up with this thing, where gl_FragColor is sort of the output color, that's what your pixel is gonna end up being. And you would assign that to an RGB value. And in this case, if you just specify a single value for vec3, you end up with the same red, green, and blue.

So you create a grayscale image. So this is using the U coordinate, which goes from zero to one. We can also use the V coordinate, or if you do dot Y to access the second coordinate of our UV coordinates. You can end up with a gradient that goes from one to zero.

One little gotcha is that in the morning we were talking about top left being zero zero and bottom right being one one. It's a, in WebGL quite often things are flipped, inverted on the y axis. And so in this case, the top of the image is actually gonna be white or one, and the bottom is gonna be black.

Just something to note that if sometimes you're creating something that looks upside down, it's because WebGL just sometimes will use a different coordinate system, where the Y axis is flipped sort of upside down.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now