Shader Abstraction in OpenGL

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey what is up guys my name is Chen and welcome back to my OpenGL series so last time and in fact over the last few episodes we've been abstracting all of our opengl cart away into classes so that we can deal with them a little bit more easily when it actually comes time to writing real graphics code and today we're gonna continue that journey with shaders so we've done like vertex buffers and vertex arrays and like vertex buffer lay house and all that kind of stuff and now the next step is gonna be shaders so let's take a look at how we can actually extract shaders well first of all let's just talk about so the way that I'm gonna structure this right now is in in a simple way because shaders when it comes to shaders and if you guys don't know what a shader is by the way definitely check out the video that I made about shaders but shaders are an incredibly complex topic and shaders are very very important to graphics programming or any kind of rendering right and so because of that there tends to be like a tendency to actually make a rather complicated system that is both easy to use but also extremely powerful so in games and game engines typically it's very common to have a custom shading language which then compiles into shader code for the each kind of rendering API or platform and also is somewhat kind of controllable and extensible through the code at runtime meaning that it's very common for games to actually generate new shaders and then have them be used for rendering while the game is actually running or while the game is loading or something like that right shader generation and shader kind of creation on the fly is something that's very very common and in general the whole like rabbit hole of let's write a shader system for our engine like tends to be such a massive topic and we're definitely not even going to scratch the surface today or in distance higher OpenGL series because it's irrelevant quite frankly to actual OpenGL rendering when we think about shaders in OpenGL like there's nothing to it you just write text in a file or as a string and you're done like there's no real need for any of this stuff that I just described that kind of stuff is typically used by an actual game engine right and we're not talking about about that in this series this is just about OpenGL later on when I make the game engine series we will definitely talk about that stuff and will definitely implement a way more complicated shader and material system and all that in that serious so I just want to just want to be on the same page as everyone else here as to what we're actually trying to achieve today because by no means am I trying to rate trying to write a crazy shader system or anything like that right here all I'm doing today is I want to basically take all of the OpenGL shader code that we've written abstract that out behind an API that's really easy to use understand and keeps our actual kind of user or client side code really concise so that when it comes time to do really crazy things with graphics in this series it's actually going to be easy for you to read the actual code because it's gonna be high-level and Telly conceptual and not like you know GL uniform matrix 4fv or whatever the function names might be right it's gonna be very simple to deal with hopefully at least that's my goal for this so that being said what is it that we actually need to need to abstract in our shader code right well what it is it whoa we haven't done anything particularly too crazy I mean we're loading a shader from a file based on the source code that I'm reading right now and we're rendering it and I think it might have a uniform or something like that that we're setting yeah what's that we're setting a uniform for EFI with the color I think of our yes with the color of our rectangle or out square that's all that we're doing but in general what does the shaders need well first of all we want to be able to basically pass in a file or a string and have that be compiled as a shader so that's kind of step one we want to be able to bind and unbind the shader step two and step three is we want to be able to set the uniforms all the different uniforms for the shader okay that's probably if for something that we're looking at right now I mean like reading back attributes reading back like being able to load in a shader and then ask the shader hey what uniforms do you actually have and what types are they all that is something that would be incredibly useful but again that probably falls more into a shader system for a game engine and that would actually require pausing the shader source code and all that this way out of scope is going to take way too long not something I'm interested in right now anyway for this series I mean but yeah I mean we just want to be able to create shaders bind them for use and also set uniforms that's it so let's dive in and start refactoring the source code so that we can do all of that okay so step one under source let's add a new item I'm going to add a header file it's going to be called shader and I'm going to do the same thing for a CPP file okay CPP file of course is going to include the header file and let's go back to the header file and start editing out so class shader we're just going to have a few Pub private and public things here we'll have a constructor which takes in a Const SCD string file name now what I did do last time or not last time but in the actual shared or a episode what I actually ended up doing was inside application I did actually make it so that we could write our shader in one file because I find it very annoying having to deal with two files so you can see that over here we actually have a single file with two kind of sections shader fragment and shadow vertex so that's very important that also means that we don't need to be able to take in kind of well we don't need to take in two different files we can just take in the file path and everything will be ok so file path we're going to have a destructor we're going to have bind which is going to be a constant function and unbind now of course with opengl we kind of bind vertex arrays and we bind vertex buffers and we bind like index buffers or textures or all that stuff but with shader programs is called gel use program now since we're making this API though we're going to be consistent and we're gonna call bind for everything so this is gonna be bind and then we're gonna have unbind and that is of course consistent with things like our vertex buffer which also have a bind and unbind all right and then finally I want to be able to also set uniforms now there are so many of these and again I'm not trying to create a complicated shader system so I don't want to make something that's templated and then has to actually deal with its own kind of things if I was writing this as actual kind of code for my engine or something like that I would definitely just probably have a set value or set uniform function that was kind of 10 flattered and not even templated but also like in a more complicated shader system you would actually just pause the trader source code to work out what type a uniform variable was and by doing that you can validate kind of what goes into it and also design an API so that you can just have set value and all you need is the name and the value and it will automatically set the right data and all of that something whether I think I talked about in the game engine series but for now we're just trying to keep this code really straightforward really simple so that again when we actually get into making things in OpenGL it's gonna be easy to read and people won't have to be like wow this is such a complicated system why I built something like that or I doesn't make any sense for this so just keep that in mind so we're gonna have like a set uniform for F or something like that which is just gonna mimic our initial line and then we'll add the other ones as we go along so this of course is going to have to have a Const STD string name and then a float and then for floats for the value so we'll just have v-0 v1 if I had a maths library which we need to get on to I would probably just use that here like I mean like a vector for okay so there we go and then I think that's pretty much it so in terms of private what we need here is an unsigned int renderer ID and then we're also going to have some kind of caching system in a minute for uniforms and you'll find out why in a minute I'll also have a private function down here which is going to be an unsigned and I think get uniform location and it's going to take in a contest city string name as well that's going to be used to retrieve our OpenGL uniform locations all right right click here and then using using visual cysts I'll create method implementations I'm also going to include string here because it's getting a little bit sad at me okay cool so back over here let's start filling all this stuff out now for debug purposes I'm at I am actually going to also save that file path just so that we can print out like what share what file the shader belongs to if we need to so I'll just set that up here and file path file path I'm also going to assign renderer ID to 0 and then I'm going to write a function called compile a shader and this is just going to be another private function here void compile shada we might want to return false or something I might actually make this a boolean and then compile shader let's go over here make it just beneath the destructor and then I'll call it compiled shader of course shader and it's actually a board alright cool so compile shader if we go back to our application code we've already written all this code we're just refactoring it so here's the Creator code that already has a better shader a fragment shader which is cool I'm just going to take all this and in fact this entire function copy it I'm going to put it here now I could leave it as static because it's really not going to change but I will reflect vary in a minute to be an actual member and then this I think is our compiled shader function which I tried to make here but obviously I forgot that I actually had it here and then this is our path shader which just figures that stuff out so really I'm just copying everything from our actual main application file into here and then Wayne shader program sources well trainer program source might just be a struct that I actually make up here in this class rather than have it in the header file because I only want it accessible here if we scroll up we're going to have to steal some of these includes as well so let's pop them down here and we definitely need the renderer as well ok cool so that looks pretty good of course let's clean this up now so we have PA's Raider which I'm gonna make all of these methods so we're gonna have passed shader we don't need the file path because we have that on the member although I might pass that in anyway compile so this doesn't all need to be static anymore that looks good career chatter as well will not make - static and we'll make it a member shada like that and this is probably a bit big let me make it a bit smaller hopefully you guys can still see compiled shader becomes a member as well ok that looks pretty good sorry let's stop making these actual functions so we have on my Mac them up here unsigned int create shader and then we also have compiled shader which is what I tried to make but that returns an int so let's edit that yeah in fact I'm going back on my whole original plan here and actually just making it match what I had an application because I forgot I had half of that courage let's copy that as well for Paz shader just get rid of the shader thing all right cool so that's what our API kind of looks like right now and let's get rid of this and this is okay so we're going to I guess because we've actually got shader program source here we kind of do need to have that there so I'm just gonna again keep it simple and move this shader program source over here cool all right that looks pretty good and of course we're getting errors here but that's fine okay so now we've actually converted this code to be inside this file instead of inside application so let's remove it from application and now all we really need to do is actually make it work so the first thing that we really need to do is inside our shader source code in the constructor we're just going to set m renderer ID to be compile or sorry what is it so the first thing we do is we've passed the trader let's go back to application actually see what it's doing so when it comes to a shader we're doing all of this basically so these two lines are what we need so we get a source and then M render ID is what this is and that becomes creator okay and then of course instead of this file path we're gonna have file path like that so we're really just maintaining this file path here as a member just for debugging purposes okay in the destructor I'm gonna call gel call Jill delete program and then this is going to be our and renderer ID now this is really only relevant if the renderer IDs isn't zero so if it's actually successful in creating it that all of this looks pretty good I think bind and unbind of course is just gonna be jail for jail bind Jill use program M renderer ID and then unbind is going to use zero youth program zero say uniform for F so if we go back to our application we can kind of well we can see what it is gel uniform for F so G L call jail uniform for F now what we actually need here is the location of the uniform so we need a way to get a location of the uniform and then V 0 V 1 V 2 V 3 ok and that's where get uniform location comes in which is just this code here so GL call get uniform location and then the name dot C string and shader is M renderer ID just like that ok cool so what we need to do here is actually assign this so unsigned int location equals G okay uniform location if locations negative one it means that we don't actually have it now we could just make this code assert here but sometimes it's quite valid for us to actually have negative one as a shader location so if we have a uniform in our shader for example and it's being used somewhere but we comment that line out or we declare a uniform but we just don't use it yet or for whatever reason then that's going to mean that it gets stripped which means that we don't have that uniform in the shader at all and this is going to give us negative one but that might like we still want our shader to work like normal because we might have intentionally just declared a uniform but not used it so that's why I don't want to assert here but we may want to print some kind of message here being like you know warning uniform and then name doesn't exist just in case we have a rendering error so that we can see that and then we'll also return that location now this is going to become get uniform location name and this is sorry get uniform location like that so our our actual function that looks pretty good just make sure you call this V 2 and V 3 and not F for some reason don't wait why did that just switched halfway through they just answer value in our in our case alright cool so that was pretty good now there is an issue that will address in a minute with the get your form location thing but otherwise I think that's pretty decent our code should work so let's actually go back to application and hopefully our code does work I'm actually gonna close everything but that okay so this stuff is going to just become let's actually include shader at the top first of all included shader H and then down down over here shader shader and we'll take in this path and then we'll do shaded up bind then we can just leave it bound probably and then instead of this location and whatever we're trying to set that variable this just kind of becomes shader dot set uniform for F the name which is u underscore color and then the actual values which is this thing yeah and then I believe so you can see how much code that reduces it's nice and simple now use program zero is just shaded or on bond with bind like the buffers we could probably address that in a minute as well we don't actually all we do have it next but we can do that in a minute Jill use program shader of course is just shaded aligned and this again which is shaded dot set uniform for F you color and then that okay so you can see how much that simplified out code and then finally we don't need to delete it at all because when we actually reach the entered the scope it will be deleted by the destructor of shader and yeah so with this I don't know why I've still got this but Sheila ray buff but that's just VB dot on bind and then that's just IB unbind so we might as well fix that now just on binding everything basically I might just leave that like that okay cool so there we go looks pretty good let's just hit at five or run this and see what happens okay so it looks pretty good we get the same kind of rectangle and the colors working so everything looks fine so let's talk about this little problem that we have so it's not really like a huge issue or anything but in shader bill cpp what we actually have here is inside this game form location every time we set this uniform we actually retrieve the location again and again and again and that's not particularly fast what we actually want to do with this is cash it so that we've only really retrieve it the first time in there that's it we're done and we can do that really really easily we just go back to shader by age I will include something called an unordered map which is really just a hash map or a hash table and then I'm just going to make STD unordered map STD string to unsigned int and call this our location cache or specifically our uniform location cache I should say and then all I'm going to do here is the first thing I want to do is check to see if the uniform location cache actually contains the name so if this doesn't equal M uniform location cache and we'll simply return and if an M uniform location cache and then the name otherwise we'll go ahead and retrieve the location and if it actually doesn't equal negative one then we can actually cache it I mean technically speaking we we don't really need like we could cache it ever even if it is negative one it's not like it's going to change whether or not uniform exists until we actually recompile the shader with potentially new code so this isn't literally need to be an allison fact i might remove this from else and just add it regardless so we're actually adding that location there and that way if i just put a breakpoint here so that we can verify that works properly the first time that i run this code we should of course result in a cache miss here and actually retrieve the location properly you can see if the location is zero which is a valid location and then the second time if I hit f5 that we actually go here we should just return this without having to do the GL gate uniform location you can see that that works okay so of course we shouldn't see any difference visually but that should provide us with a nice whole performance boost especially when we start having a lot of uniforms in our shadows anyway I hope you guys enjoyed this video that's pretty much all there is to it as always you can help support the series by going to patron or compost at the Cherno and you'll get access to all the source code that I wrote here today as well as kind of individual source code episode per episode so definitely go help out support the series they're huge thank you to everyone who does that because again we wouldn't be here without those people so thank you so much that's pretty much all there is about like that dad that is a basic shader kind of system that you can abstract out I mean you can go really really crazy really complicated with this and we will definitely do that I promise in the game engine series but for now for this series that's really it I mean if we jump back into our code really quickly another thing we could do and what we'll have to do is actually kind of extend this whole uniform thing so for example if we wanted to set a single float so a1 F then we'd have to write another function here that actually just takes in our value like this I'll go over here copy this function call us at uniform 1f remove all of these extra ones and this here as well and you can see that we basically just that that's it that's how we implement this days and I could go through an amplifier C's and integers and all that kind of stuff right now but I don't want to boy you guys you get the point anyway and as we as we require those set uniformed functions good because as we start using those actual values we'll have to kind of we'll just implement them as we go along and out that way it'll be nice and easy and we don't have to spend like half now now writing all the ones because that would be really boring anyway next time I think what else is there really too abstract we have to deal with our render at some point I don't know if that's necessarily gonna be next time but it looks like everything else is pretty much abstract away I don't see any OpenGL code here apart from our actual draw call which is which we which will be done by the renderer so let's do that next time let's go ahead and abstract our renderer and actually create a renderer class into which we can kind of parse all these objects that we've created and hopefully then it will render something for us and then I think after that we can probably move on to stuff like textures and more exciting things so anyway I hope you guys enjoyed this video if you did you can hit that like button to let me know and leave a comment being like a series is awesome or it's terrible do this instead or whatever you want to write that's what the comment section is for speak your mind I will see you next time goodbye [Music]
Info
Channel: The Cherno
Views: 47,763
Rating: undefined out of 5
Keywords: thecherno, thechernoproject, cherno, c++, programming, gamedev, game development, learn c++, c++ tutorial, opengl, shaders, shader abstraction, graphics programming, opengl API
Id: gDtHL6hy9R8
Channel Id: undefined
Length: 21min 56sec (1316 seconds)
Published: Wed Jan 10 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.