OpenGL Shaders | Game Engine series

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

That's exactly what I've been looking for for a while now, thanks for posting!

👍︎︎ 4 👤︎︎ u/Pixel_Err0r 📅︎︎ May 26 2019 🗫︎ replies

I've been watching his videos for a while, very good at explaining game making stuff, works in game industry and really knows about it, can give good advice

edit: for source code you need to drop some bucks on his patreon

👍︎︎ 3 👤︎︎ u/[deleted] 📅︎︎ May 26 2019 🗫︎ replies
Captions
hey little guys my name is trying to welcome back to my game engine series so last time we draft OS triangle definitely check out that video if you haven't already and today we're gonna talk about shaders do you guys don't know what shaders are I would recommend that you check out my opengl series it's the series all about OpenGL and I specifically focus on the graphics part of it and it is a series that is still actually going on I haven't stopped it I've just kind of thrown pause to focus on the game and in series but as the game in this series kind of gets more into graphics as it is right now I'm gonna start off that series again I'm gonna start off s tears again and actually deal with more of the kind of in-depth explanations and stuff like that to do with graphics specifically because I realized that not everyone is like obsessed with graphics and this is a game engine serious so definitely check out the video on shaders in the opengl series in fact check out that whole series because that's really going to teach you all the core kind of stuff about graphics specifically we are still gonna discuss what shaders are there because everyone needs to know what shaders are doesn't matter if you're dealing with graphics or not if you're a game engine developer you need to understand what shaders are because they're incredibly important without shaders you really wouldn't see anything on the screen at all so I mean even if you're just a game engine developer trying to debug events and then I don't know something goes wrong with something that's on the screen or something is not working correctly or you get an error or a shared a compilation failure or something you should be able to fix that so we are definitely gonna talk about what shaders are and we're actually going to write out those shaders for the hazel engine right now today but first I want to give a huge thank you to all the patrons that made this series possible patron icon for size for sure know is that link if you go over there you can help support this series you'll get really cool rewards such as access to private channels where I can discuss what I'm gonna do next and you'll also get access to behavioral development repository which contains all the code that I've actually written for hazel but haven't actually put into videos yet so what I typically tend to do is I actually work on hazel kind of spare time I kind of just develop it as an as I would an engine and then I take kind of code from that and that's what I actually end up teaching so it's like a sandbox for me to play around with but also kind of plan the engine design and architecture and all that and it's got some pretty cool stuff in it I'm not gonna lie like I mean yeah it's a lot more advanced than what you see here in fact you know what I've been saying that for like episodes and episodes you guys aren't seeing anything why don't I show you what is in that hazel development repository let's just jump in and take a look all right so check this out this is currently the hazel engine as it appears in the hazel development repository which patrons get access to so this right now is some kind of PBR demo that I've just put together it's by no means perfect I'm still working on it it's a little pretty good though here we have a bunch of balls with just varying roughnesses and the bottoms are metals the top are dielectrics which are basically plastics or any kind of a nonmetal material you can see we've got a cube map which we can do in kind of rotate and you can see that that environment map is showing up on the kind of less rough surfaces here as they would currently the I mean I've done a few things I'm not gonna discuss this too much but currently you can see that the reflection isn't a hundred percent smooth that's just because I'm relying on like a bit of a trick which is just kind of generating the cube map mitts on the fly just using OpenGL and sort of actually pre filtering them properly if I turn that on for a second you'll see that it looks a lot nicer but it runs at like one frame per second because that's supposed to be done offline and then like not done actually per pixel anyway and in fact if we take a look at the Model View we can even load a model this model is just is by um I'll leave the link to this kind of in the description below if you guys are interested to check it out but if we you can actually load any mesh you want here as well as any texture you want so if I kind of navigate to the actual textures from this I can load the albedo texture here you can see that's what it looks like I can use that I can load the normals texture I can load a metalness texture and I can load a roughness texture if I just use all of those you can see that what I have here is quite a nice-looking gun which has like all the reflections as you can see over the kind of cube map over here and yeah basically you can see that like I mean the hazel development repository is pretty cool this what you see right here on the screen is exactly where this series is headed so for those of you who are like oh you know we drew our first triangle after like eight months how long is it going to take we're gonna have this on the screen by the year by the end of the year for sure but it's just important to note that a lot of the stuff that I did in the first eight months was to make stuff like this really easy all the stuff that we covered like a vent systems layers setting up like dll and static libraries properly all of that stuff the maths library that stuff is important so that you can just like do this effectively this took me I think about two or three days to get this whole thing up and running because everything was just ready I'm GUI was ready all the maths libraries were ready just everything was just setup events were ready everything was set up and ready to kind of go which made this kind of thing really really simple let me see if I can get like some nice lighting here so yeah you can see that I mean it looks pretty cool if you want access to this card straight like right now you can just help support the stories on patron accomplice Ashley Turner and you can just literally just grab this look at all the cards look at all the shaders play around with it do whatever you want just like that and in fact if we just go back to the spheres for a second the spheres in fact will show these textures as well and you can see that that doesn't look pretty cool if I just get rid of the normals and maybe the metalness I'll keep the metalness you can see how nice and like rough certain surfaces look here I mean although it is this particular roughness map anyway oh and it's even like full HDR as well so there's exposure you can kind of like overexposed here and then dial it down you can see it's all kind of HDR which is pretty cool so there we go and finally maples let's just change the light radians get rid this stuff and you can kind of see how that works as well with all the lighting it's way too bright there and let's maybe get somewhere here cool all right so that is the awesome hazel develop and rush that's a preview for all of you guys watching this series as to where we're heading with this whole graphics component um it's gonna be pretty cool so I'm really looking forward to doing all this stuff in the future all right cool so anyway back to shaders because without shade is where understanding how shadows work we can't do anything that you saw us on the screen because shaders are extremely important because what they actually do is they're programs that run on the graphics card but you tell the graphics card what to do with the job that we actually send it so it's really important to understand the graphics pipeline as a whole I actually really want to make a video about the graphics pipeline and about how graphics are right there probably as part of the OpenGL series so keep an eye out for that this would be a good time to subscribe if you haven't already we're gonna cover kind of how that works but specifically what happens usually in a in a typical rendering scenario is we send a bunch of data to the GPU in the form of like a vertex buffer and index buffer you know send shader parameters shader uniforms that kind of thing and then what we need to do is actually kind of get that data on the GPU and then process it and the way that that's done primarily is through shaders and specifically to different types of shaders so there are a lot of different types of shaders but there are two types of shaders which are used more than like any other shaders and that is the vertex shader and the fragment shader which is also which is also known as the pixel shader in our the rendering api's OpenGL calls that the fragment shader there are reasons for that but making DirectX and then like you know most other kind of API is like the you know console API stuff like that it's known as a pixel shader which basically if we kind of break that down what that means is that a veteran that runs on the GPU for every vertex that we decide to render so a vertex is just a position in 3d space when we rendered the triangle last episode we defined three vertices right we have the top of the triangle left kind of side the right side and what happened we sent those three vertices to the GPU we executed something called a draw call in which we said draw these three vertices right and then what OpenGL actually did was it processed those three vertices in the vertex shader the vertex shader runs once per vertex so effectively the vertex shader runs three times right and the vertex shader is responsible for actually figuring out the position of those vertices in clip space so we kind of were already we didn't have a share the last time right so we were kind of already in clip space which meant that our coordinate system with it was between negative 1 and 1 on all axes and last episode we kind of discussed that so usually the message header is responsible for actually translating any kind of thing that we have any kind of vertex data that we have into that kind of negative one to one space that's effectively the output of the vertex shader we need to tell OpenGL hey this is my vertex data I'm going to process it usually by multiplying it with like a Model View projection matrix to get it into that negative one to one space and of course we'll cover that in the future and the opengl series I believe has already covered that so definitely check that out and then we move on the pipeline eventually getting to the fragment shader and the fragment shader runs per pixel okay so the veteran shader per vertex which means it runs three times now the fragment shader needs to feel in that triangle it needs to shade that triangle that's the point of the other of the pixel shader right it's called a pixel shader or fragment shader because it runs per pixel or per fragment right so what it does and what the output of that is is it figures out what color that pixel actually needs to be so runs for every pixel that open GL needs to shade it so obviously depending on how big your triangle is on your computer screen that means that's how many times it will run right so if it's really small triangle probably run like just a handful of times if it's huge it might run like a million times right and you need to shade that triangle in a particular color so what we're going to do today is we're gonna make a red triangle so we're gonna tell OpenGL that hey I want you to put these vertex positions to this particular place and I want you to shade every pixel red or something like that right now what you saw here in the kind of hazel developing branch obviously that was doing a lot more than just shading it a solid color it was doing a whole bunch of lighting calculations a whole bunch of like sampling something called a cube map which is basically the environment that we're into kind of because that actually influences the lighting via something called image based lighting it does a whole bunch of operations and texture lookups and everything to actually try and figure that out and it does that per pixel most of that lighting stuff is done in the actual pixel shader in fact there's very little stuff is actually done in the vertex shader most of the vertex what most of what the vertex shader does is actually send stuff to that fragment shader and interpolates it and does a bunch of other stuff right and that's what we'll kind of talk about here as well because another thing we can do is we can actually pass data from the vertex shader to the fragment shader anyway there's all kind of very hard to understand without diagrams of actually looking at the graphics pipeline so I don't want to spend too much on that all you need to know is that we need to tell the GPU what to do with data we send it vertex shader tells it what to do with the vertex data that it receives specifically its primary responsibility is to point out where in that window in our clip space do the vertices actually lie in right so it does that transformation and the pixel shader or the fragment shader takes in that data right from you usually you would give it data from the actual vertex shader right and it figures out what color everything needs to be when we dive into more advanced kind of shaders in the OpenGL series I'm not gonna do it in the game administer is probably but when we dive into more advanced things in the OpenGL series which actually might move on through next week or the week after we're actually going to talk about various lighting algorithms and how all the dotter kind of comes together that's gonna be a lot of fun because I love graphics and I know that a lot of you guys do as well but the primary responsibilities there is just determining what color the pixel that it's processing currently needs to be all right anyway let's just dive in take a look at how shaders work write our own shaders and see we can get that triangle read okay so if you check out this link here which I will link in the description below basically open the opengl wiki which is on kind of Chronos org it has this article about Shetty compilation because what we need to do is actually compile these shaders they're just regular programs they're specified in this OpenGL shading language instead of C++ but effectively is just like a bunch of code and what do we need to do with code we need to compile it and turn it into some kind of program so that's what this article kind of just takes you through right so it's really important that you read this you can also check out my opengl series video on shaders I think that covers quite a lot of this as well but I would recommend that you read through this whole thing because again you need to understand how it works because when things go wrong it's going to be a little bit hard to actually find out what the problem is if you have no idea what you're doing alright anyway there's a full compiled link example of a vertex and fragment shader over here we're literally going to just copy this there is absolutely no point in writing us out by yourself I really really hope that you will read through this card and in fact I'm going to explain it in this video because I can't just paste a bunch of code and not explain it I think that's really stupid but that card will compile a vertex and fragment shader you don't really need to modify it much it will pretty much remain as is in our engine apart maybe aside from like minor modifications okay so this is clearly OpenGL specific code so technically speaking this should belong in platform OpenGL but I actually want to make a video I think in the future about how we're actually good like what our platform but what our rendering API abstraction strategy is actually going to be because that's gonna be kind of the template we use for pretty much every render API specific class not render a class but render API specific class so for now we'll just stick it into hazal renderer but that's gonna definitely move and in fact we'll just call it shader but again that's going to move into an OpenGL specific file which will be called something like OpenGL shader right okay so we'll stick this into the hazal namespace and I'll just call this class shade up okay we're going to have a construct which for now we're just going to take the shader program in via just strings essentially so we'll have our vertex source and our fragment source so these are going to be the source codes for our shadows in the future we're going to take this in as a file but for now it's going to live like this we're also just gonna have a normal kind of destructor here not like a virtual one or anything in the future like we'll talk about this in the renderer API videos but in the future we kind of want to have subclasses of this essentially that will be implemented per API oh man in that case we would need to make the destructor virtual so we have bind and unbind unbind isn't really necessary I only you really use it for debugging purposes because usually you don't waste time on binding things you just bind the next thing you need but yeah we'll just keep it in here for kind of debug stuff and I'll also include string because we need that in terms of what we need here I think really the only thing that we want to maybe keep is the actual like ID of the shader so opengl gives us unsigned ends usually sometimes just normal incest ID and i like to call them renderer ID I think I may have done this before maybe not so what this is going to be is essentially just a number that uniquely identifies this object kind of inside OpenGL so that we can refer to it okay so we'll make a shader CPP file we'll just put it obviously next to the header file called shader CBP will include our PCH will include our shader header file we'll put this into the hazal namespace and we'll implement all of these if I right click here I can create the method implementations using the visual assist and finally what we're going to do now is is go back to that website copy that code because I have done a lot of copying since then and just paste it into the constructor it's gonna be nice and simple so here we go paste it into the constructor and of course we'll modify some of this stuff now first thing I'll do is include glad because we need access to OpenGL vertex sauce fragments were already taking I might just get rid of that entirely and slightly rename this to just SRC and fragment sauce I might as well rename defragment sauce okay cool and other than that you can say we get in our compiler errors or anything according to intellisense so let's take a look at this code line by line so the first thing we do is we actually create shader you'll note that a lot of the stuff that we've been doing in OpenGL right now if we go to application I think is like gen riot so GL gen buffers shield generate x-rays there's actually a bit of a newer API and I think opengl 4.5 or even 4.6 which is actually just GL create instead of jail gen they've kind of consolidated that and made it a lot more consistent and will probably switch to it when we actually make these kind of classes what I think we'll switch to it because I don't think I have much of a desire to support anything below kind of opengl 4.5 but create gen will actually this kind of gen call will only assign us with a unique ID it won't actually create the object in OpenGL like on our GPU it's it actually gets created when we do the binding for the first time with a particular target like a ray buffer here whereas create actually creates everything like actually it actually creates everything doesn't just give us an ID it actually creates everything on a GPU like allocates memory and does all that stuff and actually validates the object so create is used in this case instead of gen now you don't know why ultimately everything is going to be created in the future it is now it's just that if you look at a lot of OpenGL card prior to like opengl 4.5 it's always gen and buying gem in mind instead of just create which is kind of the new way of doing things what we're doing here is basically just getting this source we don't really need to cast this to a constant your char in fact this can just be like the C string is a is a contra this this this is I mean if you look at what GL char is they like to use their own kind of primitives IRA I don't really like this just because usually when I'm dealing with OpenGL I'm writing cross-platform on rather cross rendering API kind of code I'm not just you know implementing an engine in which with a single API and so because of that I like to just have everything the same so you can see over here I used you and 32t instead of like Glu and because first of all to get Glu and I'd have to include the up in jail header here but also why use it if like I know what it's going to be and I don't want to kind of have a different thing every time in fact this probably will end up looking something like a renderer ID with render ID being defined as a UN 32k but in this case you can see they are using the right kind of a this code out is as is and not do too much to it I think what this does is just sends our source code to OpenGL right I mean the comment even even says that sends it to our vertex shader which we generated this is the ID for our pet actuator and then it just compiles it right and now what this code does is actually checks to see if the compilation succeeded or if it failed so if the compiled status here we're checking the compiled status we're getting that kind of integer from OpenGL and we're saving it into here if that is false it means that it in compiled successfully so what our next steps are is to figure out why didn't compiled successfully so what we do here is we actually get the length of the log message which says why it didn't compile so that just gives us a length like how many characters it is and then what we're doing is we're shoving all of that information so OpenGL has a log which says why it's like an error message which basically just says why our shader doesn't compile we're getting that and we're shoving it into this vector now I think I believe in my OpenGL series I actually Alec aid so I used alakay to just allocate memory on the stack and I think it was just a gel char or like at just a cha kind of array you can do that in here they're doing a vector which is dynamically allocated but they are providing max length here anyway my point is it's not the most efficient solution but it doesn't really matter probably won't notice this this only happens when a shared compilation fails which you know in distribution builds really should this code should never ever get hit anyway so I'm not gonna be all like pedantic about how this is written but effectively all you need to know is that log that error message from OpenGL we take it and we put it into this vector of jail Charles okay and then we delete the shader because we don't need it anymore this pretty much happens anyway well we detached it we don't delete it at the end but you know there's no need for this to stay around because obviously this particular shader which we created and tried to compile didn't compiled successfully so get rid of it so we have this info log so what we need to do here if this just returns it but what we'll do is we'll just write h said probably call error which will just be like our error message so we might write something like vertex shader compilation failure and then I might do another error which is actually going to be our error message so I'll just write here what should we do probably just infer log data okay same as writing I was going to write infer log zero like that with a memory address there which just kind of gives us the first char and then obviously it's a child pointer so the logging library will know what to do with it but we can also just do it for logged up data okay and then I think I'll assert okay so H there to call assert or maybe I'll actually move this into the assert here so we're just assert here and obviously we just assert false okay cool so there we go now we basically are logging the problem that has occurred in our vertex at a compilation all right guess what the next thing is identical except we like find/replace vertex with fragment right so we make a fragment shader we get the source for actually using the source variable here I'll just clean this up we compile it we get the log length again if it failed we do all of this stuff this actually delayed both of them because that might be a problem because we've already deleted vertex shader in this case um I guess we can probably return here as in we don't need to keep compiling if if if our shader failed to compile right in this case delays both of them because even if the vertex shader worked out but the fragment shader didn't we like it's a complete failure anyway delete everything and then what we'll do here is we'll just grab this stuff and pop it here okay I'm except well obviously right fragment shader compilation failure okay cool so now now that they've been successfully compiled so that source code that we specified is obviously like valid we take this we create this program and the program is just it's like a program it's literally a program right it's going to be the combination bus both of our shaders in some kind of form that we can just run on the GPU we attach the shaders to our program because the program consists of shaders we link the program right which is kind of links to shaders together and figure stuff out and now obviously the compilation stage we know succeeded but now we do the linking okay and this just checks exactly the same as what the compilation did except it checks it for linking failures instead of compilation failures so again we get the link status if the link status is false it means that that linking failed we get the log length we get the log data we delete everything so that we don't leak in your resources and then we'll just paste this in so we'll say share the link failure okay and then we'll get that info log again and log that to the console and then at the end we detach everything because we don't need that after has been linked we don't need the actual individual files this is kind of similar I guess to how when we compile in C++ if we have multiple translation units they generate obj files I mean even if we have one it generates an obj file using MS PC which is like our kind of object file from our source code right but then after we've linked it into an exe file we no longer need those obj files so this is kind of like that right we just want to detach everything because once I've been into a program that's that and that's it okay in our destructor or we need to do is delete the program jail delete program and then this will be what am i doing zero ran dry day speaking of render ideas actually instead of taking this into program what we actually should do is oops is right M render ID equals gel crea program so we want to actually store that renderer ID in like as a member variable so that we can then access it obviously in different functions and obviously we wanted to persist outside the scope of just this constructor now obviously you can see you can see the program is used everywhere here so what I might just do is write as it was written gel you end program equals M render ID just so that just so that we don't need to paste M render ID everywhere here okay cool um there we go and obviously is not gonna change like this this value this variable is not gonna change throughout here so this is like totally fine okay cool so we delete the program and binding it is as simple as just doing gel use program and then render IDs this is what we do before we want to render something with this shader and then for unbind we just do gel use program zero okay cool that's how shared a class it's super simple now let's actually write a shader and render our triangle with it so if I just launched this code right now we'll make sure that our shader class actually compiles but also I want to see what we have right now because we should have a white triangle you can see that we do wonderful let's make that thing red so what I'm gonna do is I'm going to have to make I'm gonna include shader over here an application this is obviously extremely temporary hazel renderer shader will spill down here I'll put it over here next to our other kind of render IDs here so I'll write STD unique pointer this will be a shader I'll call this our shadow if I go back here I'm going to write this you can do this in a few ways you can do you know STD make unique for usually I like to do reset just because it's obviously less code you can see that's it there's no need to kind of make unique if you're using shared pointers you should usually kind of do make shared just because it needs to also allocate another another book of memory for reference counting but with unique pointers it doesn't really matter so over here I'm actually going to write a new shader and then we need some source code so let's go ahead and actually write some shader source code so a lot of people really hate writing source code in C++ like has strings because it's just annoying but I'll show you a little trick that makes it a lot better so we have our vertex shader source code which I'll write as vertex as SRC I guess what you could do here there's several ways of doing this right the first way is kind of annoying and it just means that you have to write like you know version and like whatever you're using 3:30 core or whatever backslash n and then you kind of write your whole thing out and usually the way the way that you do that is you'd kind of just make a new line write some code and then you know have your backslash N and then write a bunch of stuff and write a bunch of stuff and if C++ sees multiple strings together it kind of just depends of them so this is all fine obviously you won't get newline characters unless you actually write backslash n so it's kind of annoying though because you have to write quotes every line you have to write backslash and every on every line you can't just paste shader code from the internet into here because you need to add quotes and do all that stuff that's annoying so instead what you should do is just use this little prefix called R right then a court then an open and close parenthesis what this lets you do is actually have multiple line strings okay so you can see I can write stuff like that and guess what it's all part of the string okay so what we're gonna do is use that and then what it's gonna write a normal shade up so we'll write version like 3:30 core maybe because you know that's fine doesn't really matter what version of shadow of the shading language we're using here 334 should be fine in the future we'll probably use 450 as a minimum I think this is gonna be about a shadow so what we need to do is take in these this I don't remember this runs per vertex and actually tell OpenGL where we want this to be positioned now I'm not gonna run through how to write shaders and probably how everything works here but basically what we need is a main function here every trader has this and we also need to take in these attributes so the attribute being the position so we can just write layouts location equals zero in Veck three a underscore position so I like to have a naming convention which is a underscore for attributes we're taking in avec 3 because there are three component vector in this case doesn't matter you can take an avec for it will actually do the conversion for you but this layout location zero just says that it's at index zero you can see we enabled a texture trouble write zero that's it over here right this vertex with your pointer is 0 this corresponds to this that's actually where this attribute is inside a vertex buffer so we taking the position and all we're gonna do is we're not gonna do anything to it we're just go right position equals a position essentially okay what this does is tells OpenGL that the output position of our vertex is in this case the same as our input position which is exactly what we have here we might play around with this in the future just to kind of see what it does now jail position is a Veck fault so we need to actually convert this to a back fall and we'll add one on the end here as the W component okay cool so there we go that is our entire vertex shader done let's copy and paste this and we'll write our fragment shader so I'll change this variable to be fragment source and now instead of us kind of just inputting a position we actually output a color so color is a vector for I'm just gonna call this well I don't know what to call this I'll just call this color and this is our Apple color you could you know do our ones called color I don't really have a convention to be honest for output variables like this we'll just call it color and color in this case we're going to set two back for 1.0 0.0 0.0 1.0 which is pure red might make it look a little bit nicer by just doing 0.8 0.2 0.3 so what this is is a full component vector here which every component here is a color right so we have red green blue a alpha right so RGB a because that's the current format of our actual frame buffer that we're rendering 2 more on that later okay you can set this to anything you want but we're just gonna set it to a nice red color okay and that is actually it that's all you need just this is our entire true shaders we're gonna put them into our shader constructor here vertex or fragment source and then we're going to we could bind the shade of here and leave it but I'm just gonna bind it every frame over here so I usually like to bind it as the first thing I do the reason for this is like in other rendering API is like DirectX you have to actually have a shader before you even create like a vertex buffer because the layout has to kind of correspond OpenGL doesn't enforce that so it doesn't matter where you bind this as long as it's before you actually draw this line here is that real cool but I'm just gonna just bind it as the first thing because in other api's you actually have to all right cool so that's it that's done that should work hopefully I didn't make any mistakes but I guess we'll see let's just run the program and hopefully we get a red triangle all right and as you can see there is our beautiful red triangle now one thing I just want to show you guys as well is that obviously can says to whatever call you want but with this jail position like you can really do anything so if I kind of just move this by 0.5 right what this is going to do is translate that that position by 0.5 in every axis so instead of kind of being actually 1.5 is a bad thing to do in this case just because it's going to actually end up moving it outside of our screen because it'll move it up actually north 25 should be fine sorry 1 will move it up what this will do is actually translate everything and so it'll move all of these vertices that were kind of inputting here by 0.5 so this will become 1 negative 1 this one will be sorry this one will become 0 this zero so it'll be kind of centered it'll basically move it right and up okay so we should kind of see it in the top right of our screen and we can just do that by doing point five and as you can see here it is right so hopefully that kind of shows you what that is actually responsible for it's responsible for literally laying out our vertices whereas this is responsible for doing the colors now because I want to show you something cool I will what we're gonna do now is we're gonna actually access this position in here now we know first of all that this is between negative one and one which is already great colors need to be between zero and one in this case right this is between negative one and one but that's fine let's go ahead actually write some code that lets us access this a position variable in here and we can see if we can output the color like with regards to the position right so the color will be based on the position of the actual pixel so what we'll write is pretty simple we'll just write out Veck 3v underscore position these kind of variables that we transmitted between shaders are called varying variables that's what they used to be called so I'm kind of used to that and that's why I kind of give it a V underscore but it doesn't matter what you pull it at all we'll set V position here to be a position all right and then over here we need to have exactly the same code right with exactly the same variable name because that's how it matches it but instead of out we write in okay and you can transmit like entire like buffers as well and you can make like structures and do that instead but here we're just doing individual theropods and then what we'll do is we'll just output V position instead of color and hopefully we get something nice now that is pretty cool you can see that we get like green up there and red but we get this kind of black thing here which there's black kind of void here which I'm not really a big fan of let's just discuss why we actually get that so zero zero is in the middle right anything below zero any color values below zero like negative 0.5 up just zero right because colors are between zero and one they kind of get clamped to that range and because they get clamped to that range if we have anything below zero it's just black right because it becomes now clearly you can see that we kind of moved to a green gradient here the reason that's happening is because as we get closer to one on the y-axis what is the y-axis what is the Y component of our color vector it's the green channel because it goes RGB a so XYZ is RGB with X as you can see being red right what I want to do is actually move this into a space that is a little bit more friendly with kind of the space that we're in right now right so negative 1 to 1 is the current layout of our vertices but the colors are 0 to 1 so what I'm gonna do is with this color that we actually accept here I'm going to multiply by 0.5 and add 0.5 so what I've done here is since everything gets multiplied by 0.5 it gets harmed and then I'm adding 0.5 which moves it into the into the 0 to 1 range so now it's from 0 to positive 1 okay which means that you know if you take an example from this right negative 0.5 becomes 0.25 right because it gets harmed there negative 0.25 and then add it to 0.25 so if this used to be between negative 1 and 1 it used to be a quarter of the way right now it's between 0 and 1 and still quarter of the way cos is 0.25 what this means is that we shouldn't get any black at all on this we're actually at the very very bottom left edge so if we run this again you can see we get something a little bit nicer and we can actually see that as we move over here we kind of lose all of our red and we lose all of our green and that's why it kind of just goes into blue because blue is always going to be 0 which in this case is 0.5 because of that transformation okay and if we were to move this up to the top it would be closer to actual like full green and then if we move it like to the right edge it would be it'll just become more red we can actually try that and by going over here and actually like adding 0.5 we'll have to add the 0.5 over here to V position as well but now we should actually get full red on the very right side of the triangle and as say we do here because we will because we've also added green though it becomes a bit lighter than it would otherwise be but yeah you can see kind of how all that works it's very easy to kind of move data between the two shaders and hopefully this gives you a bit of a bit of an understanding as to what this GL position and what this Apple color is now the other big question that people might have that are not used to graphics API is is why is it not like why where is this interpolation coming from right you can see it's very smooth and that just has to do with how the graphics API works that's how rendering works what it actually does is it interpolates as it shades pixels between these values unless you explicitly tell it not to which you can do it actually will smoothly linearly interpolate between the two positions based on the value that you actually give okay so because we're sending a vector and the vector is per vertex when it shades a pixel that is like halfway between the two vertices it will linearly interpolate that data you set you've sent from the fetish header to the fragment shader to be kind of halfway through all right so anyway that is how shaders work this is like a very special episode I know I missed out last week and I'm really sorry about that the reason for that was just because my audio recording kind of failed halfway through and the microphone cut out and I had to rerecord the episode so I thought I'd give you guys a bit of a special last week when I recorded this it was not as comprehensive at all it was just kind of shaders let's just make the triangle read done right I was like half the length of this one so hopefully you guys can forgive me and like just you know me trying to put some extra work into this episode I hope you guys enjoyed this let me know what you think about everything we've done today and hopefully you're really excited but both shaders and by the kind of physically based rendering stuff that I've shown from the hazel development branch I really do need your help to make this series patron icon for Sasha the Turner is the place to support this if you like what I'm doing here on YouTube trying to teach this kind of game engine development and trying to get you guys so indefinite expo the powerful thing works and we're building this kind of hazel engine together your supports are really important even if it's like small it really is important for me to actually be able to kind of do this and obviously I want to spend as much time as possible doing this stuff because I absolutely love teaching I absolutely love developing this kind of stuff and hopefully you guys can kind of see that by just who I am and the support of obviously all of my patrons is what makes this possible in the first place so huge thank you to everyone next time one thing that I want to do is I really want to actually make more OpenGL stories videos covering a lot of how this stuff works but I think next time like once we've got this triangle now the most important step is actually we've we've got a vertex buffer we've got an index buffer we've got a shader we need to move this into kind of API specific classes right it's all OpenGL specific code it's currently sitting in our application file which is cross-platform and cross rendering API so what we need to do is create actual classes out of all of this deadlines in that OpenGL platform kind of layer right so we talked about the rendering API and how that's going to kind of be abstracted per API now we actually have to write that Sables cost code to make that happen so I think that's what we're going to move on to next time oh what an episode I'll see you next time goodbye [Music]
Info
Channel: The Cherno
Views: 32,733
Rating: undefined out of 5
Keywords: thecherno, thechernoproject, cherno, c++, programming, gamedev, game development, learn c++, c++ tutorial, game engine, how to make a game engine, game engine series
Id: QBCCHm_9HV8
Channel Id: undefined
Length: 42min 46sec (2566 seconds)
Published: Sun May 26 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.