C++ 3D DirectX Tutorial [Constant Buffers]

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
it's chillier welcome back to hardware 3d today our task is to implement some vertex transformations so we've got our little you know handicapped hexagon here and we want to spin this mothertrucker so from 3d fundamentals you know okay we're gonna be looking at a matrix rotation matrix right and we're probably gonna be implementing this with the vertex shader and that's all correct but there are extra things that you're gonna have to do in direct3d and we're gonna cover them today we're gonna work with a new type of resource called a constant buffer and we're gonna see how that fits into the whole pipeline system so we want to rotate this guy so that means we're gonna have to get any rotation matrix of transformation matrix to the vertex shader and then the vertex shader is going to multiply that matrix against all of the vertices in our mesh now one thing you may be wondering about is why do we insist on doing all of our transformations on the GPU side in the first place why do we have to do it in the vertex shader and the answer is we could do it on the CPU side we could transform all the vertices and then load them over every frame onto the GPU but think about it your meshes they often will have thousands or tens of thousands of vertices and you don't want to transfer all that data over every single frame that's a lot of bandwidth to use so what you typically do is you have a static mesh it doesn't change over the entire scene so you load that once at the beginning of the scene and then every scene in order to move that mesh around in the environment you just change a matrix so this would be a dynamic constant matrix it constant meaning that it doesn't change over one draw call so it'll remain constant for all the vertices in the mesh they all see the same matrix and we transfer that we update that matrix once every frame and I mean instead of updating thousands of vertices which is you know many many thousands of floating-point numbers we're only updating you know sixteen floating-point numbers framin that's a hell of a lot better now of course in our current code here we're actually transferring over every frame anyways because we recreate this vertex buffer within this function and we destroy it at the end of the function but in a real engine you're not gonna be doing that this is just some test code the other reason why we want to do this on the GPU side is because the GPU is a highly parallel processor it's gonna do a way better job of transforming thousands of vertices so now that we have that question answered the next question is well how do we get our matrix over to the GPU and the only way we really know of at the moment to get that information into the GPU pipeline is through the vertex data right but think about it you don't want to be attaching a matrix 16 floating point values to every single vertex especially since the entire mesh all the vertices are going to see the same matrix so you'd be duplicating the same data however many times you have vertices thousands of times that's a terrible idea so the answer is we use something called a shader constant buffer and now this will allow us to bind some constant values to a shader stage and they will be available for every invocation of that shader so we can bind a matrix to our vertex shader and that matrix will be available for every vertex that it processes in the mesh and so we've just identified our first task now we've got to create a constant buffer and a constant buffer is a buffer similar to a vertex buffer or an index buffer so it's gonna be a similar process to create one so first thing we're gonna do is we're gonna define the structure of our very constant buffer constant buffer so what is it gonna have well that's gonna have a single four by four matrix so we'll create a struct and we don't need to give it a name and a four by four matrix is basically just a floating point array of a four-element so it's four by four array right each element of the array we'll just call the element here so you'll element 0 0 element 0 1 that's good syntax and the name of our structure here will be transformation' because that's what this matrix represents and there you go we've got our constant buffer we could add more constants and we might later on but right now all we need is a transformation matrix now we've actually got to create some data for a buffer so so now I'm gonna create an instance of constant buffer call it CB we're gonna initialize it I'd only has one member that is the matrix which is just an array so we're gonna initialize that array we're gonna format it nicely so you can see the structure of the matrix and you can see right here very plain for anyone who's followed three-d fundamentals this is a rotation matrix rotation around z we haven't defined angle so our function is gonna take angle as a parameter and that's important because it's gonna have to change from frame to frame as we spin our hexagon alright so we've got our constant buffer now we actually have to create the resource so the process for creating constant buffer is the same as for any other kind of buffer we need a pointer here interface pointer and then we need the descriptor and a sub resource data the only real difference is here of course the buying flags is now going to be constant buffer I'm setting the usage to dynamic because that's how you typically use a constant buffer you update it every frame and you need a dynamic buffer to be able to do that we don't actually need to be dynamic in this test here because of course we're just recreating the buffer every single frame anyways but typically you would not recreate it every frame you'd create it once and then you update it and to do that needed dynamic so I said this is a dynamic and I also set the structure bytes tried to 0 because this constant buffer it isn't an array like the vertex buffers and array of vertices or the index buffer is an array of indices so you don't need to know the structure byte stressed 0 is just fine for this and sub resource data takes the pointer and then you create that bad boy now we gotta actually bind this to our pipeline and the constant buffers your pecan text it doesn't get bound to the input assembler like the index or the vertex it gets bound to the actual shader that is gonna use that content constant so we go vs for vertex shader set constant buffers and you can see here it says set buffers so we're gonna have to do some more erase again shenanigans here aren't we so the start slot there are multiple slots for binding constants but obviously we want to start at zero we don't have anything else number of buffers were only going to set one constant buffer and then again we need the PP but this isn't the PP for filling this is the pp as an array of pointers so what do we do P constant buffer dot get address of right we all know this by now what an interesting thing to note here is that unlike for the vertex you don't have to describe the layout of this constant buffer it doesn't do any checks for that sort of thing you just gotta bind it to the vertex shader now if we build this it should build fine unless I did a typo now so draw triangle does not take zero arguments so I changed the the signature of draw test triangle has to take an angle so let's set that to zero so now we see there's no build problem but if we run it and we're gonna see a problem here and the problem is D 3 D usage dynamic resource may only have CPU access right D 3 D 11 CPU access right and that makes sense because if you're creating a dynamic resource you are going to be updating it from the CPU every frame basically so access flags with no CPU access doesn't make any sense so we set this to D 3 D 11 CPU access right and if we build that it runs it runs perfect now it's not gonna do anything different of course because our shader hasn't it's not making use of that constant buffer yet but it is available for the shader when the shader wants to take advantage of it so next step is actually making the shader use the constant so to make use of the constant buffer we have to declare it vertex shader and it couldn't be easier to see buffer and you see that's a special keyword in HLSL we give it a name and we'll just call it C buff it doesn't really matter what we call it and then in here we give what is in the buffer and this has to match up with the layout that we described in C++ side so we have one matrix and we'll call it the transform and that's it now we have this available we can use it so if we want to multiply matrix by a vector very simple we just use mul there is a built-in function HLSL called mul now matrix multiplication is write multiply so the vector goes on the left the matrix goes on the right gotta keep that in mind so we just put transform we put the matrix on the right here the vector on the left and now all of our vertices will be multiplied by the transform that we bind to this shader stage one thing the note is that when you're actually using the constant you don't have to use do like C buff dot or CC buff colon colon you can just use this name directly like it's a global and it works and here we see that it builds fine that's perfectly fine syntax but in order to see that in action we got to do one small change to app we have to animate our rotation angle and we're just gonna use timer dot peak again in here for the angle of rotation that's gonna work fine for us let's run it and see if it breaks it doesn't break it does indeed rotate beautiful and there you go we've just bound a constant buffer to the vertex shader and we've used that to transform all the vertices in our model here now looking at this you might notice something a little strange going on with our model here as it rotates to highlight it a little more what I'm gonna do is I'm gonna take this vertex and I'm gonna make it touch the very bottom of the screen at zero degrees so now when we look at the bottom tip here we see that it touches the top and the bottom when we rotate and that makes sense but it also touches the left and the right side and that doesn't make sense because think about it this thing I mean the viewport is wider than it is tall so if it's just touching the top and the bottom then it should not reach to the left and the right but it does reach there so what's going on here well it all goes back to normalized device coordinates right normalized device coordinates the space is a perfect square and that maps to the rectangle of our screen and so you have to basically account for that mapping by squeezing it back down in the width direction this is I've covered this in 3d fundamentals when I talked about the transformation matrix now we're not doing a full transform yet but I do want to squeeze it so that my proportions are correct for my model and it doesn't get stretched in the the X direction now the aspect ratio of our window here is four by three so in order to counter that effect we want to multiply by 3/4 only in the X direction so how you could do that is you could multiply by a scaling matrix that has three quarters in the X part and then one in the rest of it and multiplying by that matrix is exactly the same as if we just multiplied this column by 3/4 that's just some basic linear algebra for you so this matrix here is basically just a concatenation of a rotation plus a squeeze in the X and that should give us the effect that we're looking for except that you see when we run it it hasn't given us the effect that we are looking for it's still touching both of the sides and the top and the bottom so what gives well here is the great deception of direct3d some of asking why is transposed required in my transformation matrix and the answer is in CPU arrays are stored row major so basically like I've done here but on the GPU there column major so what that means is that on the GPU it's expecting this here to actually be this column here so in order to make things jive within the CP on the GPU you have to transpose you've got to flip things across the diagonal now there's a few ways you could go about this we could manually Transpo's this initialization here I don't want to do that we could also create a matrix class and give it a transpose operation so we could just call transpose before we send it over to the GPU I don't want to do that either luckily there's a third option we can tell HLSL but this matrix is actually row major and then it'll say ok I understand you now and it will generate different operations and we try to multiply it will generate operations to be consistent with a row major matrix now this has a downside of multiplying by a row major matrix on the GPU is slightly slower than by a column major but it does make our life a lot easier in the future we are actually gonna transpose it on the GPU side before sending it over but for right now this is gonna make our life easier which I enjoy very much and now you see it's hitting the top but it's not hitting the left and the right so now the the shape is not being distorted as it rotates and that my friends is a beautiful thing indeed so take-home message of this is of course you gotta be aware of the the row ordering row major and column major between general what you do on the CPU and what HLSL defaults to one more thing you'll notice is that before we were rotating clockwise now we're rotating counterclockwise which is what you expect because a positive angle of rotation in just general mathematics gives you a counterclockwise rotation so now we're perfectly consistent and everything is running nicely now it might seem very annoying to you to have to you know figure out all these matrices by hand I mean in 3d fundamentals we created a matrix class and then we could just concatenate you know rotation scaling translation matrices together to build the transformation that we want and luckily direct3d also gives us a mathematics library to handle matrices like that and that's what we're gonna look at in the next video until then thanks for watching hope you enjoyed the video if you did please click the like button else a lot and I'll see you soon with some more hardware 3d [Music] [Applause] [Applause] [Music]
Info
Channel: ChiliTomatoNoodle
Views: 10,840
Rating: undefined out of 5
Keywords: 3d game programming, c++ 3d, game programming tutorial, c++, C++ tutorial, c++ game engine, how to, 3d game engine, Directx programming, direct3d programming, how to program 3d games, DirectX, Direct3D, D3D, programming, game, lesson, cpp, guide, code, tutorial, coding, software, development, windows, visual studio, game dev, chili, winapi, windows sdk, win32, constant buffer
Id: VELCxc0fmwY
Channel Id: undefined
Length: 15min 56sec (956 seconds)
Published: Fri Mar 29 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.