GLSL & Shaders Tutorial - Understanding The Syntax And The Idea Behind The Vertex & Fragment Shaders

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey in this tutorial we are going to learn the syntax of the glsl es language and then wrap up with some examples to better understand some of the concepts that we are going to see so what is glsl glsl stands for opengl shading language in a nutshell it is a programming language through which a web application can communicate with the graphics card in order to display 2d in 3d stuff on the browser that being said if you want to learn more about the subject i made a video in which i explained the relationship between opengl glsl webgl and 3d libraries like 3gs and babylon gs link in the description comments in this language work the exact same way they do in many other programming languages so if the comment doesn't exceed one line we type double slash and then the comment on the other hand if it's a multi-line comment we open the space dedicated to the comment with a slash asterisk and close it with the opposite again same as javascript for instance we have variables in which we store information notice though that in glsl we have to specify the type of the data to be stored in the variable beforehand we also have constants which are variables that need to be initialized and can't be updated and again the only difference between constants in glsl in javascript is the data type in glsl we have three basic types which are end float and boolean and yes i didn't mention characters in strings because they don't exist in this language having said that once we assign a type to a variable we can set a value of another type to it for example we can't put a float in a net variable and vice versa the only way to do that is to convert that value using a conversion function and here are some examples in true returns 1 float2 returns 2.0 float false returns 0.0 then of course we have a variety of operators to apply different kinds of operations like addition subtraction division etc the other types we have are vectors and matrices these two types are similar to objects in object-oriented languages so they are basically entities that are composed of basic types something i need to mention here is that in this tutorial i'll be focusing only on the programming side however it is highly recommended to have some basic knowledge of maths to understand the mathematical representation of what we are going to see speaking of maths a great resource i suggest you to look at is the math for game devs playlist at freya hallmark's channel she did an amazing job to cover some themes such as the definition of vectors in matrices vector normalization interpolation busier curves and many other topics now let's start with vectors we actually have three categories of victors if you will float vectors label it as vac and vectors label it as ivec and volume vectors label it as b vac so basically each one of these categories is composed of a certain number of elements that belong to one of the basic types that we've seen earlier the digit refers to the number of elements each vector is composed of for instance a vector variable represents a vector composed of two floats b vect4 is a vector composed of four booleans another way of creating a vector when its components have the same value is to specify that value only once we can also set the values from another vector as long as it is equal or bigger than the one we are creating so here wig d will have the two first values from vict furthermore we can use more than one vector values to create a new one now that we learned how to create vector variables let's see how to set and get data from their components to access the first component we need to specify the variable name followed by a period then x r or s to access the second component we need to do the same but with y g or t to access the third component we need to type z b or p to access the fourth component we need to type w a or q as you see we have a variety of notations that do the same thing but you should use the right one depending on the context so if you're dealing with vertices positions it would make more sense to use x y and z if you're working with colors it would make more sense to use r g and b and s t and p if you're working with textures keep in mind that we can access more than one component at the same time so here we can create a new vector which components are taken from the first and third components of another vector another thing to mention is that we have the ability to repeat and take values in a different order so repeating r here will create a vect3 which components are all taken from the first component of vict a and here vic d components are three in one which represent the third and first components of vect a quite similar to vectors a matrix is composed of a certain number of floats so we can have mat 2 mat 3 and mat 4 matrices that said unlike vectors a matrix can have different types of values at the same time so we can have a mat 2 with 2 ins and 2 booleans for instance however those values will end up automatically converted into floats the elements of a matrix are set in a column major order so to create this matrix with code it must be typed this way to access a matrix components we can use the brackets notation m0 for example creates a vector out of the first column of the matrix m22 sets the last value of the matrix to 100 furthermore we can combine the dot notation that we use to get and set the values of a vector with the brackets notation and here is an example math b 0 y gives us access to the second component of the first column we can apply a variety of operations on vectors and matrices like we do with the other types so we can add subtract multiply etc another type of variables we have in glsl is sampler there is sampler2d which is what you are going to use most of the time in sampler cube in short a sampler is a variable in which we store an image data same as other programming languages we have arrays to store collections of data to create an array we need to specify the type and also the number of its element beforehand so 7 here doesn't mean we are initializing the array with 7 it rather means that the array could take 7 elements that said to access an element we need to use the bracket notation pretty much the same way we do in c or javascript along with the different types of variables that we've seen we have the ability to create our own types to do that we need to use the keyword struct followed by a name and a couple of opening and closing curly braces then we have to specify what are the components of our structure aka our custom type so that's like the skeleton of our type to create a variable of that type we simply do it the same way we do with other variable types now to access the components of the variable whether to get or set their values we have to use the dot notation like we did with vectors again same as the other languages glsl has control flow statements so we have if else statements and for loops then we have functions a function in glsl like a variable should have a type so if it returns something the type of the function should have the same type of the returned value on the other hand if it doesn't return anything the function's type must be set to void we also have to specify the type of the parameters of a function now if you want to create and call a function the order matters which means that we need to create the function first then call it however we can do something to break this rule to do that we need to set what it's called the prototype of the function at the top a prototype is basically the definition of the function without its body then we can call the function and set its full definition right at the bottom of the codebase glsl is actually rich with built-in functions so make sure to have a look at shaderrific.com where you will find a brief documentation of any built-in function that you might need i'll leave you the link in the description below there are four storage qualifiers we've seen one of them earlier which is const then we have attribute uniform and varying i actually think it would be easier for you to fully understand the purpose of these qualifiers if you learn a little bit of pure webgl but still here is a brief explanation attribute and uniform variables are variables that receive data from the outside of the glsr code from the javascript side of the application code more precisely now the difference between the two is that an attribute variable holds data that is different from a vertex to another one a vertex is a point by the way so positions of vertices that form a triangle for example should be passed as attributes because each vertex has a different pair of coordinates time on the other hand should be passed as a uniform variable because a set of placed vertices share at the same time five seconds passed for a vertex for example are five seconds for the other vertices as well they can't be less or more another difference between attributes and uniforms is that the number of attribute variables allowed is less than the number of uniforms also attribute variables can be used only in the vertex shader while uniforms are allowed in both the vertex and fragment shaders will get into shaders later on so don't worry varying variables on the other hand are used to transfer data from the vertex shader to the fragment shader that's it precision qualifiers represent a way to optimize the resource consumption the memory usage more precisely there are three precision qualifiers which are low p medium p and high p low p being the less consuming however you need to carefully choose the right precision since the lower one sometimes can give and correct results to set the precision of a variable we just need to start the declaration with the wanted precision qualifier or we can set a precision for an entire type of variables by typing the keyword precision followed by the precision qualifier then the type now that we've seen the syntax of glsl let's talk about shaders simply put a shader is a small program that is written in glsl having said that we have two types of shaders the vertex shader and the fragment shader as you may know every object in 3d whether it's a point a text a shape or a 3d model is composed of a number of vertices that said the role of a vertex shader is to take care of positioning every vertex composing that mesh in the scene the code of a vertex shader must be typed within a function named main furthermore this function will be executed as many times as the number of vertices that compose the mesh so for example if you create an object that has 500 vertex the vertex shader will execute this main function 500 times to put every one of them at the right position now within this main function we must set a value to the built-in variable gl position and as its name suggests it's where each vertex coordinates are stored the value of gl position here depends on the 3d library you are using in 3gs you'll have this line where projection matrix model view matrix and position are 3gs built-in variables the first two are related to the camera view in the overall calculation of the final value passed to gl position and the third one is where the initial coordinates of one vertex are stored again this may be different if you're working with a different library like babylon gs or pxegas now that we have at least an idea of what a vertex shader is let's talk about the fragment shader essentially the role of a fragment shader is to colorize vertices in the meshes they form after they get positioned by the vertex shader that said the colorization process goes through a handful of steps but as a beginner all you need to know is that the fragment shader decomposes the mesh that is created out of the vertices placed by the vertex shader into small fragments then proceeds to colorize them now same as the vertex shader a fragment shader code must be set within the body of a main function in which we also need to specify a special built-in variable which name is gl frag color and as its name suggests gl frag color is where the color of one fragment is stored each one of this veg4 variable represents a color channel so the first value represents the red channel the second represents the green channel the third one represents the blue channel and the fourth represents the alpha channel bear in mind though that the values of these components start from zero and end at one so every negative value is same as 0 and every value that exceeds 1 is equivalent to 1. let's practice with some examples as you can see i have a very basic template in which i loaded an image that i'll need for the last example and a 10 by 10 plane with 30 width and height segments we also have the shader material with the wireframe mode set to true to display the vertices that compose the plane and i also have the vertex and fragment shaders already linked to the material if you don't understand something from this template you need to watch my 3gs guide where i have explained every single part of it link in the description now let's use the vertex shader to update the positions of the vertices that compose this plane to do that we simply need to update the value of this position variable which is of type vec3 since it has the x y and z positions of a vertex so i'll just apply the math function sign here as you see the vertices of the plane now form a sort of a pattern let's try to apply another math function and i'll use 10 this time which stands for tangent we can also animate the shape by changing the coordinates of the vertices over time the problem though is that unlike javascript we don't have something like the date class to get the current time so what we are going to do is to pass the time from javascript to the vertex shader using a uniform variable which is one of the four storage qualifiers that i explained earlier if you remember back to the javascript code let's create an object in which we will set every uniform variable that we are going to use in the vertex and fragment shaders every property that belongs to this object represents a uniform variable that said the properties are objects and each one of them can have a type property which is optional but it is recommended so here i'll pass the time as a float variable thus the f as type in addition to that it must have a value property that contains the value we want to pass to the shader next we need to pass the uniforms object to the uniforms property in the shader material configuration object and here we can go the es6 way since our object has the same name as the key of the property then we need to update the value the elapsed time from the moment the page was loaded in the browser to do that we need to create a clock and then update your time with the value returned by get elapsed time from the clock instance and that's it for the javascript part now in the vertex shader we need to create the uniform variable which name must be the same as the property name in the uniforms object that done we can use the u time variable to change the x position of the vertices for example so here i create a variable which value is calculated out of the time the x position and the y position of each of the plane vertices then i'll create another wek3 variable that represents the new coordinates of a vertex so here we are basically changing the x position while preserving the y and z positions and then i'll replace the initial position with the new position and there we go you can see how a short random math equation applied to the x position of a plane vertices made such a big change to its shape let's bring back the plane to its original form and turn off the wireframe mode that done let's change the color of the plane using the fragment shader so as i said earlier the gl fret color value is a vec4 which components represent the rgba values of one fragment's color therefore altering these values will result in a color changement we can also create a color animation and again talking about animation means time so same as we did in the vertex shader we are going to create a uniform variable to get the time and use it to make the animation furthermore we can change the color of an object depending on where it stands in the scene for example we can make the plane look greenier the more it is close to the right edge of the screen and darker the closer it is to the left edge the problem is that in glsl we don't have a way to get the resolution of the screen in order to achieve the gradient color that said the solution is to provide the information through a uniform variable from javascript so let's do that we'll call this variable u resolution and the value is going to be a vector 2 holding the width and height of the window something to mention here is that i have seen some examples on the internet where developers add this optional portion of code looking at the mdn it seems that it is useful to show better results on high dpi screens next we are going to create the uniform variable and use the x component to create the green gradient nothing has changed and that's because we are passing values that exceeds 1. at the middle of the screen for example u resolution x is equal to 960 which is equivalent to 1. in other words the g component of the gl frag color will be set to one no matter where the plane is since we are passing values that are bigger than one the solution to that is to convert the values from the document coordinate system to the fragment shader coordinate system which starts from 0 and ends at 1 and to do that we simply need to divide the later by the formal one so here gl frag chord is a glsl built in variable that holds the position of the current fragment in the fragment shader coordinate system then we need to replace the u resolution variable with st now as you can see the closer the plane is to the right side the greenier it gets and the closer it is to the left the darker it gets that done we can also create another color animation not with time but with the mouse position this time again glsl doesn't have a function to track the mouse position so we are going to pass this information through a vector from the javascript site to the fragment shader with a uniform variable we'll convert the mouse position coordinates in javascript this time and now we'll use the data from the umos uniform to set the green and blue channels same as colorization dealing with images and texture mapping is done using the fragment shader because images are nothing but a collection of colors after all once again we need to use a uniform variable to pass an image data to the fragment shader here we have the image set as a sampler 2d variable to use it to colorize the plane we need to convert it into a texture first by calling the texture 2d function the first argument must be the sampler2d variable and the second one represents the space where the image should appear i'll use st here which means the entire scene now we can colorize the plane by taking the colors from the image so here for instance we can take the green color from the image we can also take the other two channels which will end up displaying the image as it is on the plane and again we can create some effect using time for example now as you can see the plane is taking only a part of the image and that's because we set the entire scene as the dedicated surface on which the image should be mapped that being said what if we want the image to be fully displayed on the plane to do that we need to set the surface of the plane as the second argument of the texture 2d function the bad news though is that we don't have such a built-in variable that contains that information in the fragment shader the good news however is that 3js provides an attribute to the vertex shader under the name of uv which means that all we need to do is to pass the value of that variable from the vertex shader to the fragment shader if you remember i've talked about such a variable type earlier in the video yes you're right we need to use the variance storage qualifier so first we need to create a variant vic 2 variable in the vertex shader next we need to create another variant variable that must have the same identifier as the one created in the vertex shader and finally we need to pass that variable as the second argument of the texture 2d function and that's it last words before i finish the video i know that some parts seem a bit complicated but trust me once you practice a little bit all will fall into place i actually think that the real struggle with glsl is how to transform an idea into an applicable set of math functions in order to apply a certain effect or animation which i think requires a good ability to visualize mathematical formulas and a lot of practice of course with that being said make sure to like and subscribe and i will see you in the next video
Info
Channel: Wael Yasmina
Views: 51,053
Rating: undefined out of 5
Keywords: glsl es, glsl webgl, glsl three.js, glsl tutorial, glsl es tutorial, fragment shader, vertex shader
Id: xZM8UJqN1eY
Channel Id: undefined
Length: 26min 10sec (1570 seconds)
Published: Sun Jan 02 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.