 Hey guys welcome back to the third video in this writing cat routines from scratch series, so Basically, we've been doing a lot of work with these polygonal meshes and writing STL files. We've used this Web-based viewer to view our STL files in the browser. That's nice and all, but it's not really Self-contained enough. I want to be able to do this in our own function in our own code so Goodbye view STL.com And so that's the main objective of today's Video basically just a rendering engine. So what will that entail? Basically the objective at the end of the day today is a window X11 window I should say so Linux That we can draw our house on using open GL calls And we're gonna write our own vertex shader to handle all the math that goes into invite, you know computing this These vertex locations in in the shader itself, so that'll be fun to do So basically when it comes to rendering something you have two options. You can either do a perspective projection or a parallel projection So what's the difference? Well perspective rejection obviously things that are closer are bigger than things that are further away and Parallel projection basically means that no matter how far away something is from you It's always going to be look. It's gonna look the same the same size and in I guess more mathematical terms, I guess if you had a viewing plane all the Points would have a parallel line traced onto the screen That you can render so everything would look as it does at any distance so in terms of you know, if you want to make a game or you want to make 3d models for like a video or something you want to use a perspective projection Obviously, that's what you see with your eyes But if you're an engineer and you work on CAD for designing products You really should want to stay in a parallel projection and that's what we will do in this video series also, it turns out that a parallel projection is You know thousand times easier to implement, you know, there's the way we're gonna do it There's no need for any rotation matrices. It's no need for any kind of Trigonometry or signing cosine Everything we're gonna do is simple matrix algebra dot products and cross products So let me explain somebody math here With a simple drawing forgive my penmanship. I'm doing this with a mouse. So let's see you have a global axis You know X Y and Z global and you have some objects somewhere in space With a point on the object that you want to be the center of the view call it that the look at point will call that X zero Y zero Z zero That's that's terrible That's terrible and then any point on the object We'll say that's gonna be X Y and Z because we're gonna figure out how that relates to out to a Projection in the future. So we'll say of any point on the object. We're going to say is X Y Z and that's what we want to be able to translate onto a Sort of screen or a viewing plane like this And then we're going to have axis system on this viewing plane with I guess we'll call this one you one this one you two and We'll call the perpendicular cross product of what you want you to will call that one you three and then if you want to trace You know this point X Y Z Onto this It will co correlate to the point X prime Y prime Hope you can read that and so at this point We're almost done. We also need to be able to describe where this viewing plane is in space So the origin of this you one you two you three we'll call that I guess X1 Y1 Z1 Or we can just call this one the Look at And we'll call this one the Look how about that a little bit easier And now actually the the math that goes into computing how to project This object or any planets object onto this plane is extremely simple. Basically what you do is you're taking the X Y and Z component and you're Deconstructing them into you one you two and you three components in this axis system So it's it's very simple. I Guess I'll make some I'll write some matrix algebra. So let's say you you want to compute X prime Y prime Z prime That's it's simply Going to be like I said the dot product. So It's going to be you one Which by the way is three components you two Again three components. It's gonna be in 3d space and you three This is a terrible terrible job drawing. I'm sorry And you're multiplying that by not X Y and Z but rather Because you want X zero Y zero and Z zero to be the center of where you're looking. We're gonna actually take the difference between X minus X zero Y minus Y zero and Z minus Z zero So this Multiply this three by three times a three by one you get a three by one and you're done And actually by the way, this is Z prime value is irrelevant because when you're drawing on a 2d plane You don't care about the z-coordinate. The z-coordinate is irrelevant. All you care about is the X and the Y coordinate So honestly, you can throw out this whole Last equation for this linear algebra. So with that being said, let's get into the actual code I want to break this up into a few different pieces First I want to just render a blank screen Blank X level window will do that first then we'll do all the math that goes into, you know Computing these values and then we'll actually render it with openGL. I hope that makes sense So I'll be back in a few seconds. I'll put the code together Okay, so I put some code together really quick from previous project Just to open up a blank X 11 window. It's not too much Work, but it is a little bit confusing. So I don't want to go through line-by-line. I'm not even sure I don't I'm not an expert in this field. So I'm just gonna brush over it Basically, we have to include some extra things X11 obviously in some GL libraries as well as the standard Boolean library to use true and false They're gonna type def a context for openGL. We'll get into that a little bit later But I need it to compile so I'm just gonna describe it right now Then I made this draw function. I'll get back to that in a second and in our main function here I added three lines. So the first line is look at Float second line is look from float and last is a draw call So obviously look at and look from correlate back to You know these values here look from and look at those are required to parametrize and The other properties other parameters we have are the height The width and the height of the the screen that we're drawing to as well as the same basic four parameters That we've passed into the previous functions as well Just so we can actually have something something to draw, you know So that will be some parameters as well So this draw function is pretty simple like I said before you have a width and a height You pass in some of those geometric parameters And you have a few different few lines of code here that you have to go through You're making a display. You're making a window You're opening the display in the window with some simple properties with an width and height are both properties There this is also location on the screen. I believe some attributes that you're defining for the screen how you will draw to it Long story short eventually you map the window to it is Do a display so you have to do together you give it a name here I give it the name viewer you can do whatever you'd like and then we're making this a OpenGL current context And then we have a while loop here. So it doesn't immediately close when we run the program I just checked this should compile to compile it. You have a couple more command line options to run You have to include x11 LGL will need math in a few seconds if not now and then we have some extension prototypes to include So we'll do that so It should compile just fine. And I'll run the code You have a window. So this is a blank x11 window bare minimal Work done to get it. And so in the next step, I'm gonna write up all the code. We need to Encode this mathematics. So I'll do that and I'll be back in a minute Okay, I just put in the code. I think it seems to be okay I want to also point out that I did add this library of method H. Well, you will need that Coming up so First thing you'll see I have some shaders. I'll get into those in a second But more importantly, I have the actual linear algebra We just kind of talked about to define the u1 u2 and the u3 vectors so u3 is easy to define u3 is actually simply The vector from x0 y0 z0 or look at to Make a different color to x1 y1 z1 or look from so we can very simply Calculate the u3. So here I have u3 Equals look at zero minus look from zero Look at one last look from one look at two minus look from two So you're taking the three components and subtracting very simple now in terms of you one and you two This is obviously you can come up with an infinite number of you one and you two components That the cross product of which will give you u3, right? Yeah, these two you can have these two, right? So many that you can make an infinite number So I basically I've picked one for you one basically to take the Negative component of of the u3 Why an x and giving them inverted so basically that will give us a Value like this with no z component So it will have no out of plane component and we'll have this value in terms of x and and y And now for you to you can very simply compute that in terms of the cross product I won't worry about details. We did it last time. So simply multiplying And then this is this case. You're actually adding two Products together. So you compute you want you to new three with very simple matrix algebra Next what I did was I actually normalized those axes. So I didn't want to have a bunch of really long Values for you one you two and you three I wanted them all to be unit length That's pretty sensible. So to do that. I simply Get got the magnitude of each and I divided each component by that magnitude simple stuff Then I have a very simple debugging Print here giving us the look from look at vectors as well as you one you two and you three That's all I did so far as The you one you two and you three. Let's get into the actual shaders. That's the real meat of how this works so shaders are how Basically open GL draws things to the screen and it does it in a way. That's very parallelizable parallelizable. So if you can pass in Variables to be computed at every single Vertex it can do those all at the same time And so to do that you can kind of define inline code like this for a vertex shader and a fragment shader One of them being for positional data one being for color data And basically you can compile these at runtime in your in your code and you can kind of Attach those to the to the program and use those to render your your screen so How it works in particular here I'm I'm taking as an input in vector three of position. That's basically going to be the position of each Vertex or interpolated value between vertices Then I have these things called uniforms basically a uniform is something that doesn't change Between function call of this vertex shader. So basically Vectors you one you two and you three for example are going to be the same No matter what point you're rendering You're still gonna have the same view plane defined by you one you two and you three In the same way you also will have the same Value for a look at as well as a scaling parameter So you can say how far you know how large we want this view plane to be want to be this big You want to be this big? We want a scaling parameter just to you know zoom in and zoom out basically what we're looking at So the actual Math here is quite simple. So We're defining an output, which is called gl position just have to be called that but that's what it's called here, which is a Four-dimensional vector the only ones that actually the only parameters that actually matter in this vector are the first two And those are the simply the x and y location on the screen. So we have to give some math To the vertex shader to take in you know you one you two you three Look at as well as x y and z To give us x prime y prime and z prime and actually we only care about the first two as we mentioned x prime and y prime So basically the vertex shader is responsible for doing this calculation here and Remember we we've just computed what you want you to a new 3r and we're passing them into the vertex shader x0 y0 and z0 those are the look at A vector and they're also passed in so very simply we're just doing a dot product between you one And the three components subtracted you to and also you three to compute x prime and y prime So it's also Z prime So it's very simple and also I've added in a scaling parameter as I mentioned that will multiply this multiplication by To to get a scaled result. So let me show you how that looks in the code It's a it's quite long single line obviously I don't know why I did that but simple dot product so the x component times the Subtracted x plus the y component of you want times subtracted y plus the component of you want times subtracted z That's simply, you know, that's simply this Times this and that will give us this component. You can imagine the same thing occurs for y prime With this applied by this to give us so a very simple math there nothing out of the ordinary and then for Z I should say I have the scaling parameter here For X and Y for Z if you look at the end of this line here I have a zero and last component is one that might be the alpha value like opacity I think that's what it is. I left that at one So by itself this vertex shader should compute for us the locations on the view plane of any X Y and Z Vertex data Given these inputs so X zero Y zero Z zero as well as the the U1 U2 and U3 So that should work just fine The fragment shader is something different that's basically just the as I said before the colors So in this case, I didn't put any kind of variability for color. Everything here is green So this is RGB so zero for R zero for B and then one for Green so everything will be drawn in green Now at this point it won't do anything if I were to compile this it would probably not even compile I have to add in some more functions to you know compile these shaders and attach them to the program So I'll do that and I'll show you the result. Okay. I've just added in that code. It's quite a bit actually so We have to compile the shaders so we're basically Doing just that for the vertex shader. We're checking in the compiles that as case is an error There shouldn't be but if there is we'll see it here. It'll print out we're attaching both the for a text argument to the program and This point is also important. So Basically in order to pass these values into the The shaders we have to do so in a certain data type And it's not not quite what we had before we had a two-dimensional array before for the nodes and And the elements themselves in this case. We actually have to Do so in a linear fashion. So it's it's like a one-dimensional array. So It's very simple to do that. We just have to remap everything in terms of a one-dimensional array for the vertices and the indices Another thing you have to do is you have to bind these buffers to To actual buffers you bind in these values these these arrays to two buffers and then you are Passing them in in this way here or giving buffer data into the functions. I won't go into details You can look that up on your own Next we have to actually Get our Uniform variables from the shaders. So if you remember from the top, we had these vertex shader source and Fire shader source these uniform values. We have to be able to pass values into The shaders to use them So we have to be able to pass it you want you to u3 scaling and look at in order to use them in our In our function there So it's not quite it's not very hard to do that you simply have to use this Get you from location you want you to u3 scale and look at save them as gilded into values And now you can use gild uniform at every iteration of your render loop to pass in values. So in this case if you look in the while loop or passing in Into the u1 uniform in the shader These three values u1 0 u1 1 u1 2 same thing goes for you to u3 scaling and look at so in that way we can kind of if we wanted to in the future, you know change the viewing plane You know, maybe with the mouse or with the keyboard to rotate around the object in some way We could do that, you know in the render loop. That's why it's here And then something where we're drawing the elements As as triangles and we're actually doing it as As lines if we wanted to draw it with a solid face We could make this a gl fill, but here we're just drawing the the wire frame. So we'll leave it as in gl lines So at this point, I mean, I'll give you the code on on github You can take a look at it yourself right at yourself But if we were to run this that we're starting to compile it first run We get a You know a view. This is our model. You don't believe me. Well, I can I can prove it to you here is the Browser view and we're looking at it kind of front on like this and here's you can see the front of the triangle the top of the top of the the house there and There we have we have a working, you know render and if you can see the The points that we're looking from I'm looking at We're looking right down. It looks like the the y-axis here now I can prove to you that we can we can change those values if I go down to the Definition of look at and look from if I look from I don't know. Let's say 5.5. That should be at the corner If we recompile and we run you see this books a little bit weird, huh? This is actually the view from the side like this You're looking at the corner of the house basically and now, you know, obviously we could add more functionality This we could have the mouse rotate, you know left right up down You can use the keyboard maybe to zoom in and out But we could use the mouse to click on certain features and change the color many things can Can be made to work from this framework, so It's all I have in this video basically be made a working renderer in only a few minutes here, so I'll see you in the next one