Fragment Interpolation - Vulkan Game Engine Tutorial 07

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in the previous video i showed how we can pass data into a vertex shader with the use of a vertex buffer and by declaring variables in our shader with the in storage qualifier it is also possible to pass values from the vertex shader to the fragment shader we do this by declaring and initializing a variable in the vertex shader with the out storage qualifier then for each out variable in the vertex shader we should have a corresponding in variable declared in the associated fragment shader note that what's important here is that the locations match the variable's name can actually differ between the vertex and fragment files but hey wait up our vertex shader runs per vertex but the fragment shader occurs after rasterization in our pipeline and runs per fragment how do we go from a per vertex value to a per fragment value each of our vertices can set the out variable to a different value so ideally what comes to mind is that we need to combine these values in some way the answer is linear interpolation interpolation simply put is the process of estimating an unknown value that falls between known values it's pretty much like connecting the dots there exists many different possible ways to do it linear interpolation just means that we do this by using straight lines this is perhaps the most common mathematical operation in computer graphics if we let a variable t correspond to the fractional distance between two points then for any value of t from 0 to 1 we get a point that falls along the line connecting the two endpoints the position of a point can easily be calculated by the sum of the endpoints weighted by t and one minus t for example a value one third of the way between a equals six and b equals twelve gives us the value of eight additionally this easily extends to multiple dimensions each component is just simply calculated separately so our new x is just the linearly weighted sum of our points x components and the y component is just the weighted sum of the two points y components therefore if we want to find the point halfway between points a and b we can just use the same formula with t equals 0.5 on the x component and on the y component therefore calculating p x and p y is quite straightforward you can interpolate any type of value you want not just positions this works just as well for colors if we have a red point and a blue point by interpolating each color component separately we get the range of colors between these two points so the red component will vary from 0 to 255 the green component will always be 0 and the blue component will vary from 255 to 0. however this helps with lines but most often we will be working with triangles how do we smoothly interpolate a property's value across a triangle there exists a variety of ways to do this but the simplest is to use barycentric coordinates fortunately the graphics pipeline handles calculation of the barycentric coordinates for us automatically so in this video series i won't go through the full derivation since it is unnecessary for our purposes and we'll just go over the basic ideas if we have three points a b and c we can think of barycentric coordinates as a scaled non-orthogonal coordinate system with basis vectors b minus a and c minus a okay let's slow down if you're not familiar with linear algebra don't panic this is still pretty simple conceptually essentially it is kind of like performing a linear interpolation between a and b with the fractional distance being denoted by the beta symbol and then doing a second interpolation between a and c and denoting that weight with gamma then to calculate an interpolated value at point p anywhere within the triangle we can use beta gamma and one minus beta minus gamma as the linear weights for the three points b c and a respectively this is just like basic linear interpolation but instead of one t value parameter we have two parameters beta and gamma this also works just as well in three dimensions often we'll simplify one minus beta minus gamma to just alpha then we get the equation p equals a times alpha plus b times beta plus c times gamma with the additional constraint that alpha plus beta plus gamma equals one so hopefully i've explained well enough that conceptually you understand how this works one nice feature of barycentric coordinates is that a point is inside a triangle if and only if alpha beta and gamma are all greater than zero and less than one okay now back to vulcan in our graphics pipeline i've previously said the purpose of the rasterization stage is to break up our geometry into fragments for each pixel the triangle overlaps in addition to this for each fragment the rasterization stage will automatically interpolate any variables we've declared as outputs from the vertex to fragment shader using barycentric coordinates each fragment has different coordinates based on its position within the triangle so the input color to the fragment shader will be slightly different each time the fragment shader is run so now let's add a second attribute in your vertex shader add a new layout location equals 1 in vect3 color now what we want to do is pass this color from our vertex shader to the fragment shader we can do so by specifying an output attribute so we have layout location equals zero out color so there is no association between input locations and output locations so even though our position is at input location 0 that has no effect at all on us setting the output location of freight color also to zero then in the main function just pass through the input color to the fret color with bright color equals color next in the fragment shader we need to add the corresponding input to this output so layout location equals zero in vect3 right color all that matters is that the locations and the data types match the variable's name can differ between the shader files then just replace the hard-coded rgb values with our freight color variable so constructing a vec like this is valid it uses the three values from our vect3 frag color and one as the fourth component now back in the model class let's update the vertex struct to have a color attribute now there's two ways we can structure this we could create a second separate binding for the color attribute or we could interleave the color or tribute with the position in the existing binding the implementation is simpler for interleaving so we will go with that option all we need to do is add the line glm vect3 color to the vertex struct now in the implementation file the binding description doesn't need to change since by using the size of operator on vertex the stride will already be adjusted to account for the additional color attribute but we do have an additional attribute now so we will need to update the get attribute descriptions function first update the attribute descriptions vector to size 2. copy and paste these four lines and make sure to update the indices otherwise you won't get a compiler error but will get weird behavior at runtime so the binding is still zero because we are interleaving both position and color together in the one binding next the color location is one because this needs to match the location used in the vertex shader don't forget that it's the location that matters not the variable name we could call the color attribute one thing in our vertex struct and something completely different in our vertex shader and it wouldn't be a problem so long as the locations match then the format needs an additional component because color is a vect3 rather than evec2 so add b32 and now our offset can easily be calculated using the offset of operator which takes a type as the first argument and a member name as the second so we have offset of vertex and then color so this macro will automatically calculate the byte offset of the color member in the vertex struct it would actually be a good idea to do this for our position member as well since not only does it make it more clear that this description is for the position member variable but it also makes it so the order we've declared the members in our vertex struct does not matter alternatively you may prefer this construction just remember the proper order of arguments is location then binding format and offset and we don't actually require any other changes in the model class when we create our vertex buffer the new size is already accounted for and when we copy our data over the interleaved color data will be copied automatically as well if any of this is confusing i'd recommend re-watching the first five minutes of tutorial six this is where i explain binding and attribute descriptions in much more detail so all that's left to do is to provide rgb values for each of the vertices note that we don't get any errors right now and if you build and run you get a black triangle because the default initialization here will set our glm vect3 color to zero zero zero in the first app implementation file load models function navigate to the vertices initialization and set the first vertex to red so in between the two braces here add 1 0 0 then the second vertex to green so 0 1 zero and finally set the last vertex to blue so zero zero one let's build and run and we get a multi-colored triangle so the graphics pipeline automatically handles calculating the barycentric coordinates of each fragment and the vertex shader will be run once for each corner of the triangle so only three times in this case because we only have one triangle the fragment shader is run once for every pixel within the triangle which if your window is still 800 by 600 our triangle covers 1 8 of the window so that would be 60 000 pixels each time the fragment shader is run the freight color input will have a different value based on the location of the pixel relative to the containing triangle these interpolation calculations are done blindingly fast using dedicated hardware on your graphics device in parallel gpus use what is called a simd model single instruction multiple data what this means is that it is able to execute the same instruction on multiple different input values but all at the same time rather than sequentially so for example imagine it takes a group of fragments the size of this group is dependent on your specific hardware each fragment has a different input of barycentric coordinates the device can first calculate a times alpha but for the entire group simultaneously then in lockstep it proceeds to the next instruction b times beta for the entire group then c times gamma and so on until the entire interpolation calculation has been completed for all of the fragments within the group this is one of the main reasons why gpus can process large amounts of data much quicker than a cpu it's a trade-off gpus sacrifice flexibility for volume of course this is a drastic simplification of gpu architecture and i only have a novice understanding on this specific topic myself i think this is a good enough view for getting started with computer graphics if you navigate to your swap chain class implementation you will see in the choose swap surface function i am using u-norm as our first choice but with a non-linear color space change u-norm to srgb so that gamma correction is applied explaining this mistake could be its whole own video so i can't really go through it here the appearance of this triangle will differ depending on the color space and format being used i wrote up an explanation and have linked it in the description it's an optional read since none of it's really relevant until we're farther along in the series and that's it for today this was mostly a theory based video and we didn't get to do too much coding the next couple videos should be more coding focused though let me know how you feel about the balance of things so far do you want future tutorials to be more coding focused or should i continue with the current balance between theory and code please like and subscribe it goes a long way and thank you for watching
Info
Channel: Brendan Galea
Views: 5,838
Rating: undefined out of 5
Keywords: linear interpolation, interpolation, barycentric coordinates, barycentric, separate buffer, Vulkan, vulkan api, vulkan tutorial, 3d game engine, coding tutorial, vulkan coding, vulkan graphics, 3d graphics, gpu programming, vulkan programming, vulkan game engine, vulkan game engine tutorial, vulkan engine tutorial, learn vulkan, how to code vulkan, Vulkan beginner, graphics beginner, vulkan noob, vulkan from scratch, vulcan, vulcan api, vulcan tutorial
Id: ngoZZkMuCOM
Channel Id: undefined
Length: 13min 25sec (805 seconds)
Published: Thu Feb 18 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.