 Hey guys, welcome back schizone series episode 22 topic today is going to be 3d rendering in particular Parallel projection rendering of wireframe geometries Onto a frame buffer that we can then view on the screen So pretty interesting stuff, but first I want to apologize in the previous video I made some completely ridiculous comments about 9-11 that you know the two planes Didn't hit the towers. That's completely ridiculous. There's actually video evidence of the planes in the towers It's not CGI and honestly when you have an event like this or 3,000 people lost their life It's really insensitive to make comments that suggest that it's a conspiracy, right? So I made this 3d model we can rotate pan everything around Zooming in out and I put the planes I put the towers Also put the explosive charges that George Bush used to demolish the towers and motivate the war in the Middle East and This file if you look at it is Around 14k that's 7k of instructions and 7k of Vertex information to draw the models And so it's pretty it's pretty small of a file. There's no dependencies. No libraries It's all from scratch and we'll talk about some of the math Required for this in today's video and if you're curious all the code is available in the suppository online So take a look at that in the description So with that out of the way, let's get back into the slide deck here Parallel projections so when it comes to rendering something there I guess probably at least two different ways you can render something our eyeballs are more of like a perspective projection so we can See things that are further away They tend to be smaller in our eyesight and things that are closer a parallel projection is not that Basically, this preserves the size of things no matter how far away that they are and the reason why most Engineers use parallel projections because when we design objects in CAD or in 3d space we want to not have to reorient ourselves to To the sizes of things when we spin the model around and so usually engineers when they get the CAD software for the first time The first setting that they change will be the projection method and they'll turn off Perspective mode and put on parallel mode at least in my experience That's what people have been doing around me in my life And that's what I also do so It's very useful and I think it's probably the most both the better way to view things on a screen and also Well, unless you're playing a video game or like making a movie or something in which case Obviously it has to look like we see things with our eyes but if you're designing like Buildings or structures or whatever it's best to use a parallel projection for the render And also it's just way easier math wise like you don't have to do all this fancy stuff. It's just literally Like couple multiplications and you're done So the way it works is you basically have to define an axis system call it your eyeball Where to the right of you you can call that the extra action Above you through your skull That's the y-direction and then the way I've drawn it here for a right-hand system Z would be backwards, but you're looking in the negative z-direction essentially and the way it works is you take an object you project it in that z-direction onto your XY plane and You put your eyeball at the middle of that plane and you've essentially Then you can rasterize and draw your object like this and so it's It's very simple actually how this works But there's no for wire frames there's no Suggestion of how far something is from the camera because you don't have a Perspective to use to get that kind of information Intuitively so you have to be able to say well only looking at this This distance this depth from the viewer. We won't cover that in this video, but You can imagine how that would work And so I know for me Particularly when I was younger and I was looking at how to render stuff to the screen and like open GL or whatever or WebGL It was always like oh, yeah You have these world matrix and then a object matrix and a Perspective matrix and this is where the viewer is and this is what it is and you have to translate and rotate It's just a bunch of nonsense to overly complicate the problem. Really all you need is a view system and A model and that's it you can implement a parallel projection Exclusively with like dot products and so it's not as hard as it would seem at least if you're using a parallel projection If you have to do something with perspective, it might be more complex Okay So the way it works is and I won't go over the detailed math on this because I think it's pretty straightforward And you could probably do better job than I could at implementing it But the way it works is basically you have a point in space call it point P You have an origin where your eyeball is call that oh basically you then just take the displacement of from that from yourself and dot product that with your Rightward and upward directions that gives you components that you can then measure On your screen, you know right words and upwards say this point for example up here That's like the first step and that's the hardest steps have one step to you And then the third step would be just scaling it because there's no suggestion of how big things are and so you may have to Implement some kind of construction for how big something is like how how many pixels is my View my pixel like you know what I mean? So you may have to figure out how much I want to scale this object left right up down to To render to the screen So once you have that The last thing is to rasterize that to pixels and you're done only two caveats here One would be to know that your screen has an aspect ratio Probably not one to one unless you have a very old or specialized computer and so You'll have to implement something that zooms differently in the X and Y direction based off your computer's aspect ratio Very easy to do and last thing would be that you have to remember that your eyeball At the origin here is not the bottom left or the top right of your screen. It's the center and so This green and red and blue intersect has to be at the W over 2 h over 2 pixel on your screen in order for it to make sense Okay, with that out of the way Some frequently asked questions So from that you can see how to render a point. Well, how would you extend that? So how do you render a line? Well, simply The frame buffer the screen is it just a bit map and we covered that in episode 12 How to draw a line basically you just have to draw a line between two points that you've projected Onto this UX UI plane So you can do that already. It's very straightforward. It's just literally a plug-and-play with what we did in episode 12 How would you do a cube then? Well a cube if you're ignoring the faces on the cube and just care about the wire frame Which is today's video. It would just be 12 lines. So just copy the First question 12 times, you know how to cube great How would you do that? Well, what you'd have to do is you have to define eight points in space Right, and then you think how am I going to project that onto the screen? You have a way to do that above. It's an algorithm here to do that The question then is you have eight points. What's next? Well, then you have to figure out what combination of those eight points for the cube are Edges right not every point Touches every other point on a cube. You have to think well this point touches these three points Etc. Etc. You have to draw the the lines of the edges of the cube in that way Question number four. How would you do it if you had multiple cubes? Well? Couple ways you could do that. Maybe you would just say let's add some more points and some more more edges between them That would work or alternately You can say maybe have an array of Like ten cubes and you have a pointer to all the different smaller arrays of points and edges that might be an option or You could say well, what if I have a linked list that might also work Many ideas for doing that and last question would be could I do instances and like rotate and translate each one? And could that also work? Yep, that would be a good idea as well That's it. That's the entire theory of the video not very math heavy I want to get in to show you the actual code because I think it makes more sense to see how it works in code Because it's very hard for me to explain the code in these slides So I have three examples here one is a simple cube shown I'll show that you know right now then we have a more of a streamlined approach for rendering wireframes in 3d So I have some functions to do that. I'll show you that and then I have a scene you already saw it Was the 9-11 scene with multiple bodies, so I'll show you how that works as well So if you want to see this You can go to example 22 and You'll see three different examples a b and c so the first one is example 22 a Let's just let's just run it first You have to run this as a user that has access to you both Use the mouse and also So like the mouse device and the framework for device both have to be accessible to you So I'm using sudo to do that if you have access to those files. That's fine as well so You can see here. I've got a cube and To explain why I said before a cube is just eight points. So eight vertices one two three four five six seven eight I'm pointing with my Pepe cursor and There are 12 edges between various pairs of those vertices So that's how this works. I can rotate the model I can zoom in and out with the middle mouse button I can pan right left up down so I can kind of manipulate this model like I would in Some kind of CAD software It's very familiar to me to do this So that's the first example I'll show you the next example first, which was example B should show you what what's on the on deck In this case, I've drawn a different shape with a drew across here change the cross cursor I'll show you why this is different in a bit But you can see I'm again, I can rotate the model zoom in and out pan right left up down and again I'll show you the Example C just to keep it fresh in your mind, but what we're working for here I Have three different towers with ten different explosive charges. Everything's a different color. I have two planes I think each plane is like 56 vertices and 93 or something edges, so yeah, I mean you can kind of see how you can go from a cube to much more complicated geometries And now I'll show you how that works in the actual code so example a How does this work? Well as Before with frame buffer, we have to have a heap so we have people to Allocate memory dynamically and so we have our own implementation of that. I won't describe that What do I have going on in the actual code? Let's see Basically, I initialize the heap the frame buffer and the mouse because all of those three things are being used Then I Basically, so here's how it works I Don't want to have to constantly recompute The the rendering right if I have to re-render the entire screen over and over and over again Whenever the mouse moves a single pixel after redraw the entire, you know rendering That's not really the best case scenario because if I move my mouse wildly on the screen. I'm just Doing extra work for no reason if if the rendering hasn't changed. Let's just not bother updating it. So I only want to update the model when the structure has been rotated or Translated or zoomed in and out or whatever. I don't want to have to re-render the model unless it's moved in some way What if yeah, what if it's moving? What if it's a train on a train track? Then I would want to update the model Otherwise leave it the way it is and so how I've done that here's basically we have a I have two separate Buffers one is the frame buffer Which is what is we're drawing to the screen and then I also have an intermediate buffer that just contains the Rendering with no cursor on it. And so That's what this intermediate buffer is here that I'm saving the address at in our 15 It's just a frame of the screen sized Buffer of pixels that I'm going to render the screen like the Scene to and on top of that I'm going to copy that into the frame buffer and then on top of that frame buffer I will then draw the cursor whenever the cursor moves. So that's kind of what's going on there So what do I do first like clear the screen then I basically create my My eyeball access system. Let me go down and show you the different Data first so I have values for Yaw pitch the sign and cosine values thereof So yeah, yaw pitch I have a tolerance for the rotation Sign and cosine so when I were kidding around something for the mouse movements I need to be able to compute sign and cosine and for that we have a Taylor series expansion that we're going to need Tolerance for which we implemented that in one of the first videos in this series Then I have a scale for everything for rotating for panning and for zooming I have an access system here for the eyeball you can see that you want you to you three And then I have a buffer to store the previous access system and why do I have that well? So I can basically when I click the mouse to rotate the screen I can save the old value of the view system here and update the new one Here essentially So this way if I click and I zoom right or if I click and I rotate right left right left go back to where I was The model will go back to where it was so it's not it's not like Course dependent of the mouse it it's matter of where you clicked and where you unclicked Then I have this perspective structure This is actually what you pass into the rasterized function I covered that rasterized function ever so briefly in a previous video basically you have to pass in Here is your eyeball position. This is that oh Origin system that I described before look at is where you're looking. So there's a point in space. I'm looking at here I'm looking at zero zero zero from point one one zero that Characterizes my z direction if you think about it and then I have my up direction Which is one one negative one in my zoom value, which characterizes how big things that are on the screen in and out is point three just an arbitrary value for that and so you can see I'm not passing in the X-direction the rightward direction that can then be computed by a cross product later on right the Y-direction cross with the z-direction is the x-direction and so I don't have to waste time Computing that we're keeping track of that actually I do but not for not in this data structure And then here you can see what I was talking about for how to render a cube so here I have what's called a What I'm calling an edge structure that just contains a list of all the edges and their pairings so you can kind of see the cubic consists of eight points or vertices and 12 edges and I have a Pointer to the point array and a pointer to the edge array So what's the point array? It's a list of the eight vertices of the cube So I've got a unit two cube here where every side is is two units long and so vertex You know the first vertex vertex zero would be at point negative one negative one negative one and the last vertex would be a point one one one That's the point array. So it's eight points each point is x y z so three values They're all doubles so quad words So you can compute that how many bytes that is if you'd like Then here I have the edges So this edges is just pairs of vertices So this basically means that the there's an edge between point zero and point one Also, I can point two and point three point four point five and so you can see here I've described twelve different edges as combinations of I should say as pairs of vertices and That is then passed into this edge structure, which is then passed alongside This perfect perspective structure into the rasterized edge function Which I'll cover in a bit that then actually draws things to the screen and then lastly down here I have Pepe the frog defined I have defining some different colors for his skin and his shirt color and or whatever else So that's just to find here in spit. This is the cursor basically that we've used to for this particular example So how does this work? Let's see so I Won't get into the the fine details here, but basically I'm have to initialize those That eyeball axis system. That's the first line. I have to do and that's what I do here I make sure it's normalized I make sure everything's in there in its rights in its right location in both that view axis structure As well as the perspective structure Then what I do you can see here the first thing is I actually call this rasterized edge function What does that do? Well, that actually is what draws to the frame buffer our cube so you can see we're passing in the frame buffer address or then passing in the Color that I want to draw the cube in this case. It's some kind of orange color Then I have the frame buffer width and height. I also have the Basically addresses or pointers to the perspective structure Which is like, you know the looking direction as well as the upward direction and the zooming value and then I also pass in the pointer to the edge structure again that contains everything having to do with the vertices and edges for the cube Then I rasterized that with this function And then I flush to the screen and now you can see right off the bat I have the initial cube drawn to the screen Initial cube being that you know that unit to cube drawn in the perspective of Whatever is defined in that perspective structure. So if I go let me go down to the bottom Whatever this structure is characterizing for our view of the cube is then In addition to that edge structure pass into the function, then we can draw the cube to the screen simple enough then I Have some registers that I'm using here for the mouse position So whenever I click for example if I hold the left mouse and click the X value and the Y value of the initial click location of the mouse is saved in a registers 12 and 13 And then I have a flag to see if we're if we're dragging or not Then I have a loop that goes forever. Basically we pull the frame the the mouse We covered this in the last video and then based off the mouse being clicked or not We can then do some manipulations To the model and so the way it works is if we're holding left click We can rotate the model if we're holding their middle click We can zoom in and out and if we're holding the right click we can pan left and right or up down So how about left click well this works simply by first things first is I'm grabbing the I'm Basically computing how much we've turned about the look point So if you think about it when we rotate the model, what are we turning about? Well the way I set it up is that we rotate about that look point So we have look from and look at that look at is our center rotation and we're spinning around that point in in space And so we have a yaw and a pitch to characterize Based off how much our mouse has moved vertically or horizontally. So if we move Vertically, that's a pitch. So if I've clicked that, you know y value 7 and then I drag my mouse to y value 12. I have pitched Down I guess looking more down on the model and similarly if I move the mouse right or left while clicking I will then rotate in a in a yaw sense about the look at point Then you can see here I call cosine and sine and that's for both the Pitch and the yaw so now I'm saving some trigonometric Computations for sine and cosine of those pitch and yaw values and I save the results You know in memory here somewhere, so I have I've moved those resulting sine and cosines into You know cosine pitch sine pitch cosine yaw and sine yaw somewhere in memory in this file Then what I do is I basically manipulate the model to spin around basically It's a rotation essentially about the look point. So that's what I'm doing here I'm reconstructing the x and the y directions Based off our new rotation as well as the z direction And so everything has to get adjusted because we've now moved the model around So x y and z are changed and then lastly I copy that new view system into that perspective structure And then finally we can re-render everything to the screen So we jump down to this draw cube and that re-renders the cube to the screen behind the cursor For right-click is actually a little bit easier. All I'm doing is again I'm computing how much my mouse has moved with the right-click depressed and Based on how much I've moved determines how much we've shifted in the ux or the ui direction And you can see that's pretty straightforward. All we're doing is we're basically Adding to our look from and look at points So I have a look at point and a look from point if I pan up I move both points down essentially, you know You know what I mean basically we can Move both points that define our looking direction up down left right based on how our mouse moved Simple enough not worth describing and the zoom factor is even easier So the way it works is with the middle mouse button or the scroll wheel clicked if I move the mouse up I zoom in If I move the mouse down it zooms out, I think and so that's simple enough I just changed that zoom value in the perspective structure accordingly And then lastly in all through those cases if I use the left mouse the right mouse with the middle mouse All that happens is I eventually do some math and then I call this draw cube Well, I jump to the straw cube label and then I re-rasterize the cube and then jump back to the top What else happens here nothing really all that important? I have some stuff like you know the first time we click or whatever so think about it Whenever you click first you have to record But first off where you clicked but also what was the original configuration? So I clicked with this particular view axis system And I know I'm gonna change that when I move my mouse, but I have to keep track of what it was before because I'm always comparing I'm always going to be rotating that original system if I move to the mouse 10 pixels. That's a 10 pixel Manipulation of the original system over the mouse 20 pixels. That's a 20 pixel manipulation So no matter what I need the original configuration saved somewhere in memory Yeah, the adiata. What else nothing else really the last step here is basically that I I Only render the background when I have to and I only render the foreground when I have to The background is the scene and the foreground is the cursor and so that's what this is doing here. I copy the Background to the frame buffer and then I copy the cursor on top of that essentially and then I draw to the screen So again, let's just execute that one more time Here's the cube my mouse is here. It's Pepe the frog I can click and drag to rotate the model and you can see all what's happening is the model is being Spun in space and no matter what I do if I go back to where I was when I first clicked it It goes back to the way it was so Yep, I can rotate the model with left mouse click no problem I can zoom in and out with the middle mouse button So zoom in and out and one cool thing that it's kind of interesting is the way I set it up if I zoom out enough Everything flips upside down and the model is now inside out Which is kind of weird But I guess it kind of makes sense because if I zoom enough Everything is skilled by a negative number at that point and yeah, so the whole model kind of Gets turned inside out and the last thing I want to show is the panning So if I move the mouse if I click the right button and move at the mouse left right up down the model pans around So yeah, and then you can do like combinations of those things I can read the model in and out zoom scroll pan everything everything, you know Will work in succession Okay, cool example B This one is way simpler. I'll show you how it looks In this case, it's pretty much the same setup the same pretty much but in this case, I'm only including Functions, so this is all happening behind the scenes now. I've basically Combined all that stuff into these two functions frame buffer 3d render in it and Render loop and so the first thing you do is you call this render in it Let's just show you here. Well, sorry person. You do here I'm calling render in it with a perspective structure and a linked list in this case There's gonna be one entity, but you could have multiple for a scene and Essentially, yeah, you just I'm also passing in a cursor So I pass in the perspective structure which describes where I'm looking and how much I'm zooming and whatever then I pass in the actual object Linked list and then I pass in a pointer to the cursor function so I could have a different cursor Let's say I'm I'm making a program I want the cursor to change like say if I'm if I'm painting something in 3d space and I want to have a paint brush I can change that cursor here essentially And then I have a loop that runs forever That calls frame buffer 3d render loop and so this there's actually no exit call in this program It just loops forever. You can see it loop and then jump loop Um So let's talk about the cursor first. So the cursor function here. I have to define the cursor basically It's taking in these things so free buffer address the color event of Interest here is yellow And then the free buffer size and the mouse position. This basically just draws a cross. It's two lines It looks like a seven pixel cross and 14 or 21 it's 14 pixel across 21 pixel tall cross and then I'm also passing in the like you saw before the perspective structure and then lastly I'm passing in this linked list now here is how That whole cube thing that from the previous example starts to manifest in a more Interesting way and so again I have this edge structure for this cube in this in this case It's actually a cross and so there's 24 vertices 36 edges and then here you kind of can see different parts of the cross I have the base of the cross the bottom of the cross beam top of the cross beam Everything here to find in terms of x y c position Then I have some edge pairs for all the different edges of the cross rendering all the way down there And basically those two things edges and points are then encoded in this edge structure And then I have a linked list with only one entry Of this geometry and so I could have multiple crosses if I wanted or multiple objects I could put Christ on the cross if we want but here the link list next entity is Null and so that's how I that's how people usually describe that it's the end of the link list if the next entity is Null the list is over and so all it is is basically a Pointer to the edge structure and then a color the ARGB values of the wireframe that you want to draw to the screen And so we can actually make the cross a different color if you want. We can make the cross blue. Let's do that ARGB so that's to be FF zero zero zero zero now. We'll have a blue cross. I can save this file Also should say you can see where it says The type of structure so a DB and then a binary value I'm not using that right now, but basically we can change that Byte to describe. Hey, this is a wireframe or wait. No, it's gonna be a point. It's gonna be a face It's gonna be a whatever I can change down the road I can use the same structure for multiple different types of renderings to the screen right now It only supports wireframes, but it in the future. It will support other types of Entities as well Okay, very good. If I run this Here we have a blue cross. We just changed the RGB value So it makes sense and again the cursor is it's up is itself across so we can we can rotate the model all around zoom in and out Pan right left up down and we can even zoom out infinitely and invert the cross inside out. So yeah What do you expect? It's just way easier to do this now that we have functions that do everything And all we have to do is pass in the underlying geometric data in the first place very cool last example was the scene and actually this is even easier well not easier, but it's It's easy now that you know how it works. And so how does this work? It's the same exact thing Actually, you can only need to know that we can get rid of this include that's unused We have the same cross cursor as before the same program It's the exact same instructions as the previous example 100% exactly the same instructions. The difference is we're only passing in Different things so different data, but same instructions So in this case we pass in the same cross cursor I think the same perspective structure as well, but in this case we're passing in a Length list that's not just across and so here I have the north tower geometry. So how does this look? well, North North Tower geometry contains both a pointer to the Tower structure that contains the edge and the point information for the north tower and the color of the north tower Which is gray I guess But it also contains a pointer to the next geometry in the length list, which is the south tower South tower points to the third tower third tower points to the explosive number one Most of two three four five six seven eight nine ten ten charges of explosives and then down here I have the geometry so I have the plane geometry. Oh, where's the plane? I have a plane structure somewhere here here plane structure one plane two structure down here And then I have the points For everything so I have the north tower points south tower points third tower points tower edges Here's the plane one points in space. You can see a lot of random numbers in here That took me a while to come up with plane two points here And then I have the edges for the planes and the cool thing is If you think about it Even though there's two different planes, let me draw it so you can see Even though there's two discrete planes with different vertices in space like this tail point and this tail point are not the same and This wingtip and this wingtip are somewhere else in space The same edge combinations are correct Edge so like edge one contains point seven and point nine no matter what plane you're on And so as long as the vertices are in the same position in the array The same edge structure is applicable to both planes And similarly all the towers and all the explosives. They're all just prisms. And so both So any structure that you can see here that is a like a prism Uses the same set of edges to define it We don't have to define another set of edges because it's all The same combinations of vertices the royalties are different the verts are different spots the points themselves are different spots But the edge combinations are the same So yeah pretty cool This was fun to make to be honest. I enjoyed this very much This brings me back to when I was younger this kind of stuff So anyway, let's show you how how that looks in the cobalt last time So I've got all the points defined edges defined and all those are encoded in these structures here So explosive six contains Pointer to the number of to the array of points for explosive six But also for the tower edges because the tower edges and the explosive edges are both the same combination of Points essentially So yeah, that's how it works. You can change colors of things if you want to make here Let's make explosive three a different color. Let's make it instead of being red. Let's make explosive three a Green Rerun this and you can see explosive number three has now turned green. I don't know. It's pretty cool, right? If that interested you Check out our discord. I'll put a link in the description If not, I'm surprised you watched this long-laid video and it did interest you. I guess I'll see you in the next video Thanks for watching