Vertex Attributes and Layouts in OpenGL

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey what's up guys my name is a Chan and welcome back to my opengl series so last time we talked about vertex buffers there's a link up there if you haven't seen that video but there were some parts that we were missing to actually get our triangle rendering and specifically between major parts that we were missing were vertex attributes and shaders today we're going to cover vertex attributes so what what is a vertex attributes so basically the way that the OpenGL pipeline works is we've kind of discussed lightly in the last episodes and we will have some kind of in depth video on that in the future but to put it simply the way that the OpenGL pipeline works is that we basically supply our graphics card with data we store some kind of memory on the GPU which contains all of the data of what we want to draw and then we'll use a shader which is a program that is executed on the GPU to actually kind of read that data and display it on the screen more or less and typically the way that we actually draw to your tree is we use something called a vertex buffer that is basically a buffer of memory that's stored on the GPU so when the program the shader actually starts to read that vertex buffer it needs to know the layout of that buffer what is actually contained in the buffer is just a bunch of floats which dictate the position of each vertex do you also have texture coordinates normals anything else like that we have to actually tell OpenGL what is in the memory and how its laid out because if we don't do that then as far as opengl can say it's just a bunch of bytes it's the same as with c++ right I mean if I write a float and I assign it something we know that the memory there is a float because I'm telling say plus class this is a float if I cost that to an integer by the boy if I cost it to an int pointer if I take the memory address of the flow and cost it to an end pointer and then dereference it I'm gonna get something completely different because it's interpreting the memory differently that's kind of the same thing that's going on here we need to be able to tell OpenGL the first 12 bytes are three floats four bytes per flow and that is my position the next eight bytes after that are my texture coordinates I've got one float for X and one flip for Y or u U and V if you want to call it like that we need a way to be able to tell it in gel this is the layout of my memory and that is exactly what this whole vertex attribute pointer thing can do for us there's a function in OpenGL called geo vertex a true pointer that's what we're going to be taking a look today from the shade aside you also need to match the layout that you define on the CPU side on the surplus class site as well and when we actually introduced traders in the next episode we'll take a look at a basic example of what that looks like as well and again I'm just starting off with really basic examples so that you can see what it is like in its simplest form but as the series goes on we are obviously going to ramp it up and make it way more complicated I'm just kind of starting you guys off gently all right without just rambling on further let's take a look at the code so from last episode we have basically a float array of positions here you can send it the way that I've kind of divided it is that each line here is an actual vertex right so we'd have three vertex positions we have three vertices this is the x coordinate this is the y coordinate of our vertex on our spring now before I go on I just really wanted to find what I mean by vertex because people seem to get pretty confused with that and also use that word incorrectly a vertex has nothing to do with the position right and vertex is just a point that is on your geometry the way that most people think about them visually obviously is by its position so if I draw you a triangle you'll be like ah three vertices but vertices don't vertices are not positions right a vertex is not a position a vertex can contain way more than just a position so if you tell me hey I have a vertex it's 0.5 0.5 that doesn't tell me anything right because you're kind of implying that I mean vertex position and yes a lot of people do user like that but that's not exactly correct because the vertex can contain way more data than just a position although it does usually contain a position so I just wanna make that clear when I say vertex position that's what I really mean I'm not gonna just if I actually say that one vertex I mean the whole blob of data that makes up that vertex which again could include the position texture coordinates normals colors by normals tangents all of this kind of stuff right that could all be in a single vertex so just the position if you're just referred to the position you should really call it positional vertex position so that's why I've got this float here as kind of positions not just vertices because at the moment they're just positions although when I add stuff to them I really probably anyway so we have these three vertices here and then we're generating a buffer binding and then actually putting this kind of or I should say copying these positions into this actual buffer that we've defined here by just specifying a pointer to them and how big they are and that's it that's all we're doing so far so again OpenGL has no idea that if you look at this code we can clearly see okay so we have two floats per vertex and that is the X&Y position right OpenGL doesn't know that it might seem obvious to you because you're looking at this code you're like well how can open gel not know that but what if I suddenly introduced a third kind of coordinate right so in other words what if I want a three dimensional coordinate system instead of two dimension like we've kind of got here what if I do this well then suddenly you know how does OpenGL know that I've actually got three vertices of a three kind of dimensional coordinate system instead of just one two three and then kind of four and a half I guess of just two coordinates right because if I was to rearrange this code well then suddenly it looks like we actually have more than three vertices here if you kind of stick to the kind of two-dimensional rule right so what I'm kind of getting at is that we need to tell OpenGL out layout and that's what we're about to do so if we scroll down a little bit once we kind of bind the buffer and we fill it with dot e you don't have to do this before you fill it with Dada you don't have to do it after you can do it whenever as long as the buffer is actually bound that is when we can tell OpenGL what the layout of our buffer actually is and the main way that we do that if I just open up my favorite documentation website here doc so GL there are two kind of lines of code that we have to ride the most important one though is GL vertex at rib pointer which is right over here right I'll click on the jail for link and we can see what we have here so it takes quite a few parameters in by the index size type normalized stride and pointer let's go through them really quickly so the index is the index of the share info text attribute to be modified again this doesn't make much sense if you don't know how this works basically the way that our shader reads all this is by an index it's a picture it is almost like an array but the types in the array might be different so this is an index as to which attribute you're actually referring to all of these things by the way uphold attributes when I mentioned that a vertex is made up of more than just position right might be position texture coordinates normals colors whatever each one of those like a position is an attribute a color is an attribute a texture coordinate is an attribute a normal is an attribute they're all attributes so this index is just telling us hey what index is this attribute at the reason I say this is because typically if we have like a position for example at index 0 we need to be able to refer to it as index 0 so let's just picture this right I have three kind of attributes I have position texture coordinate and normal I need to be able to say that I want my position to be at index zero my texture ponent been at index 1 and my normal to be at index 2 so that when I kind of start reading stuff from the shader side from the GPU side and I go into that buffer I can simply reference them by hey index 2 placed and I know that that will be my normal because I've kind of defined that layout that's what an index is it's just the index of that actual attribute in your buffer size here can be a bit misleading it's the number of components per generic vertex attribute you can see that it says here clearly that it must be one two three four so again this size has nothing to do with bytes or like actual how much memories of this occupies it is it's basically the count so what I mean by that is when we specify the type here such as for our actual vertex positions GL float this is how many floats were providing so here clearly we have two floats per vertex so this size will be true because we're basically providing a two component vector that represents our position if we switch to that kind of 3d coordinate system as I mentioned earlier we would be we would be setting it to three because we have three floats for this for this actual attribute all right so type as I just explained is basically the type of data that we're providing jail flirt is what we want normalized this is a little bit more tricky we don't really need to worry about this particularly if we do if we're dealing with floats because they already are normalized but basically this is used for let's just say that we're specifying a byte that is between 0 and 255 because that's our color value that needs to be normalized to be between 0 and 1 as a floor in our actual shader and this is what will actually do it for us so basically this isn't sample of something that you can do yourself on the CPU side but you can also let open gel them for you if you're lazy or if you're kind of if you just want open gel to do it for you so we'll be setting this to false or specifically GL false because normalized is actually a GL boolean which is just an integer all right stride and pointer this is where a lot of people get a little bit confused so I wanted to be clear on this stride is as the documentation states the byte offset between consecutive generic vertex attributes this is worded in not sure why they why they worded it like this but basically what the stride is is the amount of bytes between each vertex so again in our example now kind of made-up example if we have a position we have a texture coordinate and we have a normal let's just say the position is a three component vector then the texture coordinate is a two component vector two floats and then the normal is is three components once again right so we have three floats that's 12 bytes plus two floats for the for the texture coordinate that's another eight bytes or on 20 bytes and then another 12 bytes for the normal so 32 bytes that is our stride because it's basically this site the size of each vertex the reason this is important is because if opengl and the reason it's called stride as well is because we if we want to jump from one vertex to the next so for example i want to look up index number one so from zero zero is our first vertex i want to jump to our second vertex which is index number one i need to go plus 32 bytes into the buffer so we have a pointer to the beginning of our buffer and I go 32 bytes into it I should be at the very start of the next vertex that is what the stride is so just remember that the pointer on the other hand is as this stay it's an offset of the first component of the I don't even want to read this to be honest it's so confusing pointer is a pointer into the actual attribute so this is already inside the space of the vertex right so you're just pretend don't worry about how many vertices you have anymore pretend you only have vertex I have one vertex and inside there I have position texture corner and normal what is the offset of each of those in bytes right so for position the offset will be zero because the very first the very first byte of our buffer the very first byte of our actual vertex is the position right then we go 12 bytes forward and we and we reach the beginning of the texture coordinate so for my texture quarter attribute this value would be 12 because 12 bytes into it is where we begin and finally 8 bytes into that we get to the vertex normal right so 20 bytes from the beginning is my vertex normal format so for my vertex normal attribute 20 would be the value of this pointer now instead of just writing generic things like 0 12 and 20 because that can get a bit confusing and not only that but if you actually rearrange your layout or something like that you'll have to reach out recalculating or manually there are macros and kind of well it really is a macro called offset of actually German demonstrated that in the arrow operator if you guys watch my arrow operator video which is should because it's important for C++ I actually kind of demonstrated offset of I actually wrote it myself instead of using a macro but the library actually the C++ library in the state library provides you with an offset of macro which you can use to actually find the offset of something in a struct or class so you can use things to kind of lie same with stride as well you can use kind of macros and stuff like that to automate this stuff for yourself so that you don't have to just be putting values like 12 or 32 as I said to the stride manually I'm just explaining to you what actually should be but just hopefully this explains the entire kind of bird X a three-pointer function - because again it's something that can be confusing if you're just starting out but it is actually really simple make sure that it's correct because if you get even one byte kind of out of place here you're gonna get weird artifacts probably in your rendering or just a black screen or something like that okay so back here in our code we can finally use this now so what I can do is with the buffer actually bound that's important I'm just going to write GL vertex at rib pointer all right so again first parameter is the index now this is a really simple case as I mentioned earlier we just have positions so first of all we only have one vertex attribute so we only need to call this function once because we're way to specifying a single attribute eyo the index of course will be zero because it's the first attribute the size remember the size is the component count not anything to do with actual size and bytes so how many floats represent this specific vertex attribute to somewhere right - next we have well this one way next we have the type so this is going to be the type of data that we have here clearly as it floats so we're going to type in GL float next we have whether or not we want them to be normalized no they're already flows they're already in the space that we want them to be so jail false next we have the stride now as I mentioned the stride is the amount of bytes that we have between each vertex so this is our first vertex second vertex third vertex clearly the stride is the size of two floats right so what we could write is just eight basically we could also write sizeof flirt times two and that's probably our best way to kind of automate that so to speak and make it as easy to read as possible here because we're not dealing with something like a struct or anything like that eventually when we have more than just kind of a position per vertex and we'd start introduce things like that your coordinates and normals we will have a struct that makes up a single vertex and in that sense we could just pass in the size of that struct as the stride and that would be fine but for now size of flow times two is how much you need to go forward to get to the second vertex and just that just to make that super clear because we only have one attribute here it's the amount that we need to go forward to get to the second vertex not the second attribute okay that's kind of what this next parameter is the pointer now keep in mind you can see here that it's actually a pointer it's taking in a pointer not a number so this might be a little bit confusing but it is still just a number because remember pointers are numbers if you don't understand pointers fully watch my video on pointers up there because that's gonna clear so much up for you so since this particular attribute is the first and in this case only attribute this is just going to be zero and you can just something like that if we had another attribute for example let's just say texture coordinate kind of started here or something like that then we would actually have to pass in the offset to that texture coordinate which would be 8 bytes so we could just write a it like that if you try and do that you're going to get an error here because we're trying to pass in 8 and it needs to be a constant word pointer the way you fixed that is you just cast 8 to a constant word pointer my seem a bit dodgy or whatever but it's totally fine that's what you meant to do and of course if you were doing this properly and you had a real kind of application or engine or something like that you would probably have a struct to actually define your vertex and then you could just use the offset of macro to figure out what this is for you instead of just writing 8 or something like that because that that's not a good way to go anyway of course for us it's zero there you go that's it that's how you use this function in this case and it should work just fine but wait there is always always something that I forget that you need to write to actually make this work I always forget this when I start writing up in jail stuff from scratch because obviously if you're kind of like me you don't typically write code like this often because this is something that you kind of do the first time when you're setting up a new engine and then you forget about it but there is one more line of code that we need otherwise we are just going to get a black screen and that is we need to actually enable that vertex attribute so back in our documentation here really wish that this documentation actually said something about enabling or having to call enable in fact I don't remember who does yeah it does at the end it says to enable or disable a generic vertex attribute array all GL enable vertex attrib array so if we go to this function you'll see what this does is it enables it say blah blah but basically we take in this is the function we're interested in we basically just take your me index the index of the generic vertex attribute to be enabled or disabled so back in our code I like to do this before I do the pointer it doesn't really matter Open GL of course is a state machine it's not like it's going to check to see if it's enabled and then not do this if it's not it doesn't matter like that you can call it any way you like as long as your actual buffa is bound so we're gonna call GL enable vertex a shrimp array and then the index we want to enable which is 0 okay and that is it these two lines of code are all you need to do in this case to tell OpenGL what the layout of our buffer is and theoretically if we had a shader which we're going to have in the next episode we be able to finally see our triangle and spring going up here it's going to remove this little error here there's extra kind of float that I added just for demonstration and that's it so basically to summarize this is the code that we've written today I hope you guys enjoy this video if you did you hit that like button you can also go and join my discord a discord or confidence re the channel become such discord which is just basically a community of people talking about stuff like this so you can discuss the episode there or you can leave a comment in the comment section below if you prefer to do it that way I really do recommend you join the discord though there's a great community of people where you can ask questions about everything that you kind of see in these videos as well as you know get help with your code not working or stuff like that so definitely join up there if you really want to support the series and you want to see more videos and you want to see videos early and get a bunch of other kind of rewards as well as I've actually started doing a hangout a one-hour hangout with all of the patrons on a specific tier where we just basically talk about stuff for an hour just did one yesterday went really well so definitely join up there if you're interested in that patreon for such the channel really does help support the series and make sure that I can kind of keep making these videos as much as possible next time we're gonna ride some shaders is probably gonna be quite a lengthy episode because there is actually going to be quite some code we need to set up to actually get shaders to compile on link in OpenGL but once we kind of get that down we'll finally have our triangle on the screen so it should be really exciting I'll see you guys next time goodbye [Music] [Music]
Info
Channel: The Cherno
Views: 146,345
Rating: undefined out of 5
Keywords: thecherno, thechernoproject, cherno, c++, programming, gamedev, game development, learn c++, c++ tutorial, opengl, Vertex Attributes and Layouts in OpenGL, vertex attribute, shaders, graphics
Id: x0H--CL2tUI
Channel Id: undefined
Length: 18min 54sec (1134 seconds)
Published: Sun Oct 15 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.