How Shaders Work in OpenGL

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey what's up guys my name is a chana welcome back to my OpenGL Series today I'm gonna be talking all about shaders finally and we're finally going to be able to actually see something on the screen so last time we talked about vertex attributes and before that we talked about vertex objects that takes buffers and all that stuff so definitely check out those videos if you haven't by just clicking on a link over there and from last episode some of you were actually able to already see something on the screen and if we dive into the code that we wrote last time over here we've basically just wrote these two lines if I try and run this application as is without any any kind of shade of stuff at all then you'll see on my computer it actually works and we do seem to see a triangle a white triangle at our correct vertex positions which we actually specified over here so how come that's happening and the answer to that is that some GPU drivers will actually provide you with the default shader if you haven't if you haven't actually provided your own shader and that was that's what seems to be happening here but this is something that is really really based on your drivers right now I'm just running this with the Intel drivers on this actual laptop however I do also have an NVIDIA GPU which will probably have to switch to later for more serious things but that being said it's completely driver dependent so I don't really want to kind of that that's why I didn't run it last time and I really want to encourage that because it'll probably end up working for some of you but not for all of you and then some of you will be like my code doesn't work it's not really meant to there's nothing in the OpenGL standard that actually says you should see a triangle if you write the code that we've done right now that's actually just up to the GPU manufacturers to say hey you know what if you don't provide shader we'll just write a basic one for you so that you can at least debug your code a bit easier or something like that today we're actually going to write our own shaders so the first question on everyone's mind is what is a shader a shader is basically just a program that runs on your GPU that's all that's all you should be thinking of when you think about a shader by program I just mean a like a block of code it's code that we can write as text as a string on our actual computer then we can give it to OpenGL we can send it to the graphics card compile it like any other program link it like any other program and then run it like any other program but the difference is that it actually is run on our GPU on our graphics card and not on our CPU like this C++ program yes so why do we need program to actually run on the GPU at all why why do we have to write code and then run it on the GPU well obviously we're learning about graphics programming so the graphics card does play a major role in that but specifically the reason that we want to be able to program the GPU is because well we want to be able to tell the GPU what to do we want to utilize the power of the GPU to actually draw graphics on the screen now that doesn't mean that everything we do we need to do on the GPU or should be doing on the GPU in the form of a shader there are some things that the CPU is still faster at and as we kind of progress through this series we'll probably find some things that we will prefer to do on the CPU and then maybe just send the result the resulting data to the GPU while still doing that processing on the CPU but that being said there are undeniably things a lot of things to do with graphics that the GPU is simply going to be way faster at and that's where shaders come in handy now not just when we want to defer things now now not just where we want to kind of take things from the CP or put them on the GPU but fundamentally we need to be able to program the GPU because even in the form of drawing of drawing this really simple triangle I still want to be able to tell the GPU how to draw that triangle right like where do the vertex positions go what color should the triangle be how should it be drawn all that kind of stuff right when we actually get into more complex 3d scenes as well lighting is a great example of why we want to be able to program the GPU how does lighting work all of that stuff I don't want to get too ahead of myself but all of that stuff needs to be programmed somehow the GPU doesn't just know how to do this right we need to tell the GPU what to do with the data that we've sent it and that is what a shader fundamentally is now in this episode and for most of OpenGL and for most of graphics programming we're and you as well probably going to be focused on two types of shaders vertex shaders and fragment shaders fragment shaders are also known as pixel shaders by the way so these two shader types are by far the two kind of most popular ones and probably the ones that you will be using 90% of the time there are other shader types tessellation Jada's geometry shaders that kind of stuff compute shaders if you're doing like completely kind of other stuff as well there are many different types of shaders don't think that there are just vertex and fragment shaders that's it you're done that's all that's all there is there's much more and they do come in handy when you kind of start getting to the more advanced stuff and we will certainly be covering those types of shedders further down in this series when we get to more complex graphics but for now and for like 90% of shader programming you're probably going to be dealing with vertex and fragment or pixel shaders right that's it so first of all let's talk about what that actually means what is the vertex shader what does a fragment shader what are they why are there two types how do we use them so now I haven't really covered the OpenGL pipeline or the just a standard kind of graphics rendering pipeline yet but how does roughly work so really the picture that you should be having in your head is that we've written a bunch of data on the CPU we've sent some data to the GPU we've issued something called a draw call we've bound certain states as well before you showing that draw call and finally we kind of get to the shader stage of things or rather the GPU gets to actually processing the draw call and drawing something on the screen and we get to see a triangle on the screen that specific process is basically the rendering pipeline right how do we go from having data to actually having a result on our screen now shaders come in handy when the GPU actually starts drawing its triangle and vertex and fragment shaders are two different shadow types that are along that pipeline so when we actually issue a draw call what happens is the vertex shader will get called and then the fragment shader will get cold and then we'll see a result on the screen now there are many things in between that I have skipped over for simplicity's sake there are many stages before the vertex shader at many stages in between the vertex and fragment shader as well as between the fragment shader and the restoration stage and all of that stuff I don't want to really code out right now I'm trying to keep this simple so just keep that in mind all of you advanced people who know exactly what's going on but it's still watching these videos for some reason I'm kidding by the way pretty much everyone should watch these videos because it might feel in a lot of gaps that you're missing but anyway the point being I am I am simplifying this so we go from the draw pole to the vertex shader to the fragment shader to being able to see on our screen now what the vertex shader does or specifically the code that is our vertex shader that gets that gets called for each vertex that we're trying to render so in this case we have a triangle we have three vertices okay that means that the vertex shader will get called three times okay one for each of vertex and the primary purpose of a vertex shader is to tell OpenGL where you want that vertex to be in your screen space right so where is so again simplifying is where in your window you have a you have a window open on your computer where you're rendering these graphics where would you like that don't that vertex to be right that is what the primary purpose of the vertex shader is just again it has the word shader in there so some people like to think of it like having something to do with lighting or shadows or no it's a program that's all it is so this doesn't even have anything to do with actual graphics traditional graphics in terms of like color or whatever all the vertex shader does is it specifies the way you want the positions to be now that being said it's also used to parse data kind of from attributes into the next stage and and also in law in in our case the next stage is the fragment shader so it's also used for that because of course the vertex shader will actually take in all of the vertex attributes that we've specified in our buffer in this case we only have a position these positions that we specified if we look back to our code these kind of negative 0.5 and all the all this actual this stuff we can access this in our actual vertex shader because we actually specify them as vertex attribute pointers and you would have noted that we also wrote the index 0 and this index 0 is actually going to correspond to an index 0 which will define in our actual vertex shader and through that kind of format we'll be able to actually access the data or access this specific vertex attribute on this specific virtual vertex attribute being the position so once we've accessed that position in a vertex shader we can basically tell up in jail I want you to position this vertex at the position that we specified in the attribute now you might be thinking this is a very very trivial task like why does this even need to exist obviously we want to position our triangle or our vertex position where we've actually specified in our buffer well yes but think about this what if you have a camera you have a camera in a 3d world and it's its position is somewhere suddenly this these vertex position with these vertex positions that we've specified for our triangle aren't really going to translate directly to being on the screen at those same positions because if the cameras moved then well the triangle should move as well and that kind of transformation of that triangle needs to happen somewhere now this comes down to certain things being faster to do on the CPU and the GPU and that's there's a lot of a lot of thought that goes into this and we'll certainly cover when we get off to writing cameras and stuff like that but all I'm saying is that don't think of it it's not actually that trivial is just saying that the dot of the dot the data that is in my buffer that is where I want my vertices to be because there are certain transformations they actually need to happen based on certain states and just a lot of other stuff as well but again the primary purpose of a vertex shader is to provide those vertex positions we need to be able to provide some kind of transformation if if nests if necessary so that OpenGL can actually transform these numbers into actual on-screen coordinates so that we see our graphics in our window at the right location in the right place so once the vertex shader has run I remember it runs for each vertex once that's happened and we've run it in our case three times it gets called three times one for each vertex we move on to the next stage in our pipeline in our pipeline for this application which is the fragment shader or the pixel shader now fragment shape fragments and pixels are a little bit different kind of terminology we're not going to get into that now but for now you can kind of think of pixels as for our rather you can think of fragments as pixels because the fragment shader is going to run once for each pixel that needs to get rasterized what I mean by rasterized is actually drawn on our screen so our window is just basically made up of pixels right it's like a pixel array what needs to happen is those three vertices that we've kind of specified that make up our triangle now need to get filled in with our or pixels that's what the rasterization stage is doing and the fragment shader or the pixel shader is going to get called one once for each pixel in our triangle that needs to get filled in and the primary purpose of our fragment shader or our pixel shader is to actually decide which color that pixel is supposed to be that's all it does it just it determines a color and output color for our pixel so that we can get so that so the people can get shaded with the right color think of this as like a coloring book where you've kind of got an outline of things but you need to actually color it any meter shade it in with a certain color that's what the fragment shader is responsible for now straightaway you might have noticed a difference between the two first half shadow gets called three times fragment shader might get called tens of thousands of times right depending on how much room our actual triangle occupies on our screen if you have a tiny triangle a really small triangle in your window that might only get called like another 50 times or something maybe if it ends up taking up 50 pixels if you have a giant triangle just filling your window you know if in jail or something like that you might it might be taking up like I don't know a million pixels or 500,000 pixels or something like that which means that that fragment shader is actually going to get called 500,000 times immediately and I want you guys to realize this even from the very beginning that if I do something like five times five equal like III calculate what five times five is in the vertex shader and the value of that is 25 that calculation is going to happen three times in in the process of rendering this triangle because the vertex shader gets called three times if we have a huge triangle and we do five times five that calculation in the fragment shader and that fragment shader gets called five hundred thousand times suddenly we're doing five hundred thousand multiplications and not three there's a bit of a difference here right which is why when it comes to when it comes time to kind of optimize and think about performance which is really all the time you can probably start to notice that hey it might be worth me doing some of these critical operations in the vertex shader rather than the fragment shader and you can pass data from the vertex shader to the fragment shader as well so yeah just something to think about just wanted to mention that keep that in mind fragment shader things in there tend to be a lot more expensive because that fragment shader is going to run for each pixel that being said some things obviously need to get calculated per pixel so a great example I love bringing up this example with fragment shader specifically is lighting right if you're calculating lighting each pixel is going to have a color value that is determined by a number of things for example the lighting the environment potentially the texture the material that's supplied to the surface all of this stuff right comes together to determine what the correct color is for a specific pixel obviously this is going to depend on a number of inputs such as where the camera position is and as I said all the surface properties the environment properties all of our stuff is going to come together but in the end of the day at the end of the day all you're determining in the fragment shader is the color of a single pixel that is what the fragment shader does it's a program that runs to determine which color this pixel should be that's it once that happens once that fragment shader gets calculated your color will basically make it to the to the screen okay to the actual to your actual window that is open on your computer and we'll see an actual triangle why it's triangle in this case because the the default shader in this exam in our example in our driver seems to be just a falling to white which is probably the reasonable default but anyway when we start writing these shaders it will be whatever color we specify so as an easy test we could just say that hey every single pixel for this triangle should be red and we should just see a completely red triangle okay raccoon chatters wrong for each pixel I may determine the color output vertex shaders run for each vertex and they determine the position on our screen that is basically how shaders work now you may think that I'm really over simplifying things because I am making a lot of the stages of the actual pipeline and a lot of other shaders that we actually have available to us but I don't think I'm actually ever simplifying at all that is actually as simple as it is that being said you can do some really cool stuff with this right you have something that runs for each vertex and fresh pixel width that you can do like 90 percent of graphics program everything you see in games nowadays has probably done 80 to 90 percent in those shaders okay and believe me once you get to some really nice looking graphics those shaders could be thousands of lines of code so they definitely get very complicated not to mention that a lot of game engines and certainly every kind of serious big game engine actually generates shaders on the fly based on what's going on in your game and based on what graphics settings you've selected all that stuff so having like kind of live shader generation and compilation is very very common in game engines so there's definitely a lot of really cool things you can actually end up doing with shaders anyway that's a gentle introduction to shaders we didn't get to writing any code this time but we definitely will next time I just wanted to give you guys a basic overview finally kind of a bit of a technical overview of what writers are how they work what they used for and all of that and hopefully we'll see them in practice in the next episode one more thing I want to mention is that like everything else in OpenGL shaders work based on the state machine which means that when you want to enable a shader you're about to draw a triangle and you want it to use a certain shader to draw that triangle you enable that shader you might also send some data to the shader so much like we're sending this vertex data from the CPU to the GPU in the form of a vertex buffer we can also send data to our shader in the form of something called a uniform and that comes from the CPU as well so we kind of set up all of that state enable the shader and then we draw a triangle that's how it works in OpenGL again like the rest of OpenGL is pretty much just a state machine you guys enjoy this video you can hit that like button you can also help support this series by going over to patreon Ocampo slash to Cherno really does help me make more of these episodes and more quickly and all that fun stuff you also get some pretty cool rewards such as access to all the source code for all of these videos kind of episode by episode on github and plenty of other awards as well just check out that link if you want to discuss this episode further I've got an OpenGL channel on my discord you can head on over and join that discord by going to the channel calm slash discord is basically just a nice community of people where you can talk about C++ open shell games all that kind of fun stuff so definitely join by that link there I will see you guys next time goodbye [Music]
Info
Channel: The Cherno
Views: 167,983
Rating: undefined out of 5
Keywords: thecherno, thechernoproject, cherno, c++, programming, gamedev, game development, learn c++, c++ tutorial, shader, opengl, vertex shader, fragment shader, pixel shader, graphics programming, GPU
Id: 5W7JLgFCkwI
Channel Id: undefined
Length: 17min 37sec (1057 seconds)
Published: Sun Oct 22 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.