RAY TRACING! // Code Review

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey what's up guys my name is the channel welcome back to another code review the series in which you guys send me your code and projects and all that stuff and i take a look at it so last time we were on linux taking a look at a voxel rendering kind of engine so check out that video i'll have it linked up there if you wanted to catch up with this series today we're taking a look at something that i think is going to be really interesting to a lot of people and we're back on windows yes what's this your computer will restart so this time around we have maka iwashko saying that your series about hazel engine motivated me to finally do my own engine as well it's a software ray tracing engine supporting reflection refraction area lights soft shadows polygon mesh rendering so i would be very grateful for a code review there is also an important readme with pictures in the repository and here is linked repository as always i will have that link in the description below in case you guys also wanted to check it out now i thought this would be kind of interesting because whilst we have looked at engines in the past we really haven't looked at any ray tracing engines and for those of you who don't know ray traces typically do not run in real time and especially not a software ray tracing engine so software tracing means that it's done on the cpu not on what we call hardware which i mean a cpu is also like a piece of hardware but hardware usually refers to gpu or the graphics processing unit so your graphics card this on the other hand is a software ray tracing engine which kind of makes it seem like it is in fact done from scratch a lot of people don't really perceive hardware programming as a from scratch because you're usually using like a graphics api like vulcan or opengl or directx whereas if you do things on the cpu you're typically just programming basically just like mathematical equations that contribute to a pixel kind of array which to a lot of people is kind of a lot more bare bones there's no kind of worrying about what the graphics card is doing or having to use their apis you can kind of more or less focus on the actual algorithms and the mathematics behind that stuff instead of worrying about an api like vulcan or opengl that's if you compare it to something like doing this in a compute shader in vulcan where you would have to worry about like where the computer fits into the pipeline how to get data to the compute shader how to then get that texture and render it as part of like a graphics pass so you can see it on the screen it's like a whole thing right so this is definitely going to be a lot more simple and to the point which is something that i like to see now let's quickly like i'm just going to gloss through this i'm going to read this in front of you guys again the link to this is in the description below if you want to read everything it's written fully in cpr 17 which is interesting looks like cmake is important however actually if you're using visual studio it looks like it does in fact include a solution file so we'll just be able to open it in that and uh yeah it looks pretty cool so let's take a look at this in more detail wow he's actually got a pretty decent like readme this is this is looking up this is looking looking good but before we jump in and take a look at this project i want to thank skillshare for sponsoring this video for those of you who don't know skillshare is an amazing online learning community where millions of creative and curious people from all around the world can come together into a community and learn new skills by taking taking a class on almost anything imaginable whether you're interested in photography videography animation illustration marketing business stuff productivity they literally have everything on their platform and all of their classes are incredibly high quality concise to the point no fluff no churno style videos no just just you know just good videos they don't have to worry about youtube and the algorithm and likes and comments and don't forget to subscribe no their videos are actually perfect for learning i've been using skillshare here and there for little things that i've wanted to learn such as concept art for an upcoming game i may or may not be working on and other things as well such as youtube and how to make better videos if you would like to check out skillshare you're in luck because skillshare is offering the first 1000 people who click the link in the description a one month free trial of skillshare premium so you can check out literally all of their classes for a month and see if there's anything there that you like so definitely click the link in description below there's no reason not to check them out huge thank you as always to skillshare for sponsoring this video so what i've done here is i've cloned the repository just a little quick tip whenever i clone i tend to clone recursively just in case there are like git sub modules that i also need to fetch looks like there weren't any in this repository but just a little tip because i've seen a lot of people not clone recursively and then wonder why things don't work out of the box that's that's the reason that it might not work now speaking of working out of the box last time i had to fix the project so let's see how good this goes and i i vowed that if someone was to send me a project that didn't work again i would be extremely upset so let's go ahead and try and build this see if it builds let's find the main file and uh looking good so far um all right guys let's let's let's let's bring it in let's bring it in this is about to become a tutorial on files and working directories because i see this way too much one of the biggest problems when learning programming and i i used to do this this is also a personal story about what i was like back in the day people just seem to struggle with files loading files like if i want to load a file in java or c plus or some other programming language like how do i load the file if i don't use some kind of absolute path what is it relative to i have no idea why like this doesn't seem to specifically be taught i'm pretty sure i was never really taught about the concept of a working directory but let's let's dive into this so the first thing is obviously for those of you who don't know this is a problem because this is something we call an absolute path absolute path because it kind of begins from the root right this begins from the d drive and then it goes into the dev directory and then cg and ray tracing the thing is i do have a d drive but i and i do have a default this is all very suspicious but i do not have a directory called cg and this is certainly not the directory of this file does not exist on on my computer on the person who who actually wrote this presumably it does but the bigger problem is there's no actual reason to ever write code like this in fact absolute pods are something you absolutely want to avoid in certain situations they may be required because you might have certain files on your computer in different places usually you would maybe set an environment variable and then required to kind of access that environment variable if you need to like get a path of something else on your computer that's kind of specialist cases for the most part there's no reason for this part to be here we need to be able to open this file in a more relative sense so when i write some c plus plus code to open a file how do i create a relative path like what is that path relative to well to understand that you need to understand something called the working directory in visual studio if we right click on the project and go to properties and then debugging you can see we have this thing called a working directory under the debugging properties this is currently set to a macro or a variable called project der now what that is what that macro is is the actual location of this project file on disk this vcx proj because that's our ray tracing project file that directory so in this case c dev youtube code review ray tracing that is what this is right and if you have any doubt as to what this is you can of course edit it and see what the macro expands to in fact you can see what all of the macros expand to so if you wanted to know what the solution directory was in this case it's the same you can see the value of that macro here very important stuff so that means that when i run this through visual studio right in this case the debug configuration on the x64 platform right i mean this will be the same for all configurations the point is it will set the working directory to be that project directory so if i open up that directory again you can see that this is now my relative kind of root so if i then just go input slash you know shotgun.scene right which in this case would just look like this or i could use or i could use forward slashes and instead of backslashes and i guess forward slash is what i mean this is also technically a maybe a slightly better way to write this because for slashes work on all platforms i'm actually not sure depending on what simplest api you use it might actually work even on like linux if you do this or on mac but it for a more portable way you always want to use false lashes with like file paths so this path now is in fact going to work because again our working directory is this and if you pass in this actual path you don't modify it at all into like you know std if stream or something like file and then you do like you know fopen right if open then it's that's gonna work right because again it's relative to the working directory but wait there's more a lot of people get confused by this because if you actually go into the exe file directory right so when we actually build this project it obviously outputs an exe file which is what we end up running if you try and launch it here it's not going to work because the working directory is now no longer what visual studio says it is when it runs your actual application the working directory is not the project directory anymore it's this actual directory with the exe file and so that's why people also get confused they end up not knowing which directory the assets should be in should they be in the directory with the exe file should they be in the directory with the project file and so sometimes they decide oh yeah it has to be both because if i want to distribute this application i want it to be alongside the exe file that's mostly true however if you are just testing this exe file outside of visual studio what you can actually do is create a shortcut of the exe file then if you go into properties you can modify this start in parameter right so instead of this being this actual directory that contains the exe file you can actually change it back to be the directory of the project file and so now if i launch this shortcut it's going to set the working directory to be the same as what visual studio sets it at so that means that i don't have to copy any assets because you definitely want to avoid copying assets that's i would never do that you have to keep them in sync some people do like a post build step which is just it's just slowing down your build for no reason don't do that just just run this shortcut of course if you're packaging it for distribution that's a different story one final thing i want to mention is you can also programmatically check and set your working directory now since this project is simple 17 you can actually use the uh brilliant file system api which i love and if you uh actually execute this function sd file system current path you'll see it takes no parameters which lets you actually check what that current working directory is but then you can also set it to be something so if you wanted it to be like some kind of working directory or whatever you can actually write code like this and it will programmatically change your working directory to something else we use this in hazel for a number of uh different kind of scenarios in which we actually want to allow the application or the game to set its own asset directory and stuff like that but the point is that's also possible right so there are a number of ways to kind of control where your files are relative to but uh i hope you enjoyed this little tutorial maybe this should have been a standalone video but anyway working directories that's how they work i don't want to see any more absolute parts from any of you um please just just just be aware of this because it is a little bit of a tricky thing in the beginning and i was i definitely struggled with this in the past especially because i mentioned java in the beginning and java has this whole concept of like you know of files being within the jar file and then that's like a entirely different story but anyway we won't touch on that so now that we can actually potentially run this let's just go ahead and run it and we'll dive in and take a look at the code exception throne nothing nothing ever works okay well let's go ahead and fix this because as usual these code reviews are also not just reviewing the code but also hopefully sharing a little bit of how i debug problems and how i approach things actually one thing i want to mention is that like you know a lot of people find code on github and the code itself might be decent but they download it and especially if you're a beginner you kind of just expect that code to work like you want to be able to download it hit f5 it works everything's fine but the reality is sometimes you just need to tweak some things right and again usually it's the fault of the author the person who puts their code online i mean sometimes they don't intend people to you know actually look at their code they're just doing it for themselves although they if that were the case they wouldn't be sending an email to churner review gmail.com but anyway the point is i made a video a while back about the best way to like learn class loss and get better at it i'll link it up there if you want to check it out but my point is that in that video i said one of the best ways to learn c plus more is to actually look at other people's code like you need to be doing that but other people's code doesn't work all the time so hopefully these little kind of debugging sessions are a way for you to kind of get a glimpse of how i approach these kind of kinds of issues i mean sometimes there's just no fixing something because you're not going to spend like days fixing it but in this case it's probably something trivial i expect i have no idea so let's take a look at it and hopefully this will be helpful in some way so what's the problem i like to look at the exception if we look at the output window it says that there's the access violation reading location blah blah blah 3c okay so this is already a very dodgy memory address right 3c that's the memory address that's way too small right memory addresses cannot be that small memory addresses are usually quite high so what this tells me is that probably it's trying to find an offset from a null memory address now i just happen to know this but you know let's let's see how else we can see if that's a true story or not if i go a little bit up the call stack um then uh okay well that was pretty quick so this is actually null right so we are dealing with a num pointer acceleration structure is null we're trying to call this intersect acceleration structure um uh function right but the actual acceleration structure instance is null right and if we go up a little bit further i expect we'll see this ac should be null or empty i guess it's like a unique pointer or a shared pointer that's why it says empty and not null but the point is this actual if you look at the raw view then you can see that we are pointing towards like a null pointer so ac is null not sure why this has happened um one good thing you want to do almost immediately is actually look at the log uh and you can see that did pay off because we can't load files again right even though we fixed this scene and it seemed to load the scene fine but it just can't seem to load anything else right so always look at the log in case the person who's written this code actually logs like error messages for file reading and stuff like that i suspected it was like a missing file because acceleration structure is obviously the thing that holds like our vertices and our triangles so that we can do ray tracing with them and it's null right so probably couldn't load the mesh or something like that so let's go up a little bit and see why i've already got so much to say about this code but let's go a little bit um higher uh let's see so what do we do we do main we do render so scene i'm guessing the scene constructor probably ends up loading all of these files right so let's take a look at that um load scene we loaded the scene and then we do root paths so root path is scene path and then we find input and we we okay so we erase okay let's put a breakpoint in here by the way another reason why it's important i think to fix projects that you get from github if you're trying to learn more about them is because you can read the code and that's all fine but being able to actually tweak the code play with it debug it see how the code flows by placing breakpoints and then hitting them and stuff like that that's a far better way to learn than simply reading the code right so that's why you know if you download a project the code's there great but it doesn't work that's also a very bad situation to be in so yeah anyway so we we find uh we find input so input shotgun scene is is the actual um this should be like a debugging series shouldn't it code review more like debug review or like anyway whatever so um okay so so we erase well in this case like what are we even raising we just raised everything so the root path is nothing rootpath.fine input wait hold your horses let's go back to this does it really just wipe that hold on a minute hold your collective horses everyone okay right so it okay no it doesn't work i thought what it might do is like just strip the entire path up until input and it's like okay that's what you mean but that's also weird anyway yeah this does have a root path and then i guess uh it sets the root path to be that and then it tries to like it separates the scene path or something it's all very weird i'm not sure why it's written this way to be honest um i think that like the way that the file system is working in this project is a little bit over complicated for not much reason at all but you try to extract the root path because you expect an input directory from there and then also i think from the console we could also tell that it looks a little bit weird because um like we're trying to load uh like why is there a leading slash here that's a bit of a that's a bit bad news i mean input objects um what do we have here so like input objects shotgun yeah like this stuff seems to exist right there's a shotgun obj but this leading slash is probably ruining everything so i'm not sure why that's happening so let's go back another great strategy is to actually like this seems like a uh an actual string is to just search for strings that you see printed in the console because obvious obviously i've never looked at this code before i don't know where it is but if i search for that you can see i i can find it so load bmp could not open bmp file and then the file name so let's just place a breakpoint in this function and work backwards a little bit so file name is uh slash okay so there is a leading slash for some reason i want to try and track it down so this obviously comes in from here and mesh oh okay so it tries to do options root path plus this and then plus this string value okay and what's value input objects slash shotgun diffuse okay so this value here seems okay right input objects i assume that's coming from the actual uh where are we i assume that's coming from the actual like shotgun.scene file right so we have input objects shotgun all of that stuff that's coming in from this file however again this whole like root part situation like i'm not sure why this is here i think i know why it's here i think it's because again this person is trying to load everything in an absolute fashion because if we think back to what this used to be right the root path used to be like the absolute root of this project so it was like d drive dev blah blah and then eventually ray tracing so in that case what they're doing is they're they obviously have decided wisely to not store absolute parts inside the scene file so these are all relative parts but they think that they need to load they need to like stitch them up with the absolute root to then be able to map this file and load it properly right which i get that idea but that's just not true and in fact again it's much better to just set a working directory that is not absolute and then um kind of see what's going on uh that way right if you really wanted to stitch them up for some reason you could also query to get the current working directory right so if i wanted to like here's what i could do right the correct fix for this probably would be to just remove all of this well what i would do is i would remove all of this options root path you don't need a root path right unless again your assets directory is somewhere else on your computer in that case you probably will but for this little ray tracing project i would just remove this and then you can just obviously use whatever value you've read in from that scene file however one thing you could do is if you really want a root path um is actually set this road path to be std file system current what was the current directory let me just include file system and then we'll see so you could make it i think it's supposed to be yep current path you could actually get this and then i guess as a string assuming this is what you're storing as a string yeah and so if you actually do that let's take a look at what that would give us that would give us the root path of like that would give us our working directory right and if you stitch that up with everything else such as where were we trying to load the bmp file let's hit f5 you can see now we are looking at an actually real um absolute path right so you could do that um maybe that will work now yeah it seems to work okay i assume it's just very slow so let's go ahead and run it in release mode and maybe we can like see a frame or something 71 oh there we go look at that it even opened up the photo view on my computer so we have a shotgun great i'd like to see that other kind of reflection scene as well um but yeah it looks pretty good i guess um looks like maybe something like mipmapping could definitely be implemented as well as uh anti-aliasing because that looks pretty uh jaggy but yeah that's pretty cool so you're rendering a 3d model looks like there's a bit of lighting going on as well you mentioned i think normal maps although it's kind of hard to see no well no there is clearly like a lot of bump going on here which i assume isn't just from the albedo map so yeah looks pretty cool in general opening up in a photo app as well is pretty nice the final kind of testing that i will do before we jump in and take a look at the actual code is i want to see if there's um i mean what scene should i run i think full scene.scene sounds juicy so so let's let's go ahead and try that one out a little status update i've been sitting here for about a minute this is this is taking this is taking some serious time it'd be funny if at 100 just like delete system 32. what am i doing with my life okay finally so we have this result i don't know why that took so long because the shotgun looks almost identical i mean there's a sky box and a sphere with like some reflection going on but that's that's a bit weird look at that 105 seconds like two minutes almost that took so that's quite a bit anyway let's finally dive into the code and take a little bit of a look and i'm going to see if i can criticize this to death i love these things okay so scene scene path so okay so well let's talk about this right so i don't like this kind of pattern right if you're making a variable like this on the stack there's nothing wrong with it per se but i would much rather create it like this and then actually call render on it the reason being that making a making an object like this um you're not really giving it like an l value it's still kind of it's all kind of in temporary land and then you're calling dot render on it and like the scope of this is kind of not really as easy to define as well as if you wanted to have other functions that wouldn't really work there's nothing really bad with this per se it's not like it's gonna you know ruin your program but it's just stylistically not something i would do i wouldn't create an object on the stack like this and then call dot render on it as if it was like some kind of chained function that's not really how i would do things i would create the variable like this and then call.render on it which i think gives a little bit more scope to scene but also if you're doing if you're doing stuff like this it's almost like worthwhile to ask yourself do i really need to make a class out of this and do i really need to be like all object-oriented about this again nothing specifically bad about that but um you know this maybe could have just been a function although i do kind of understand what you're doing you're almost creating scene as a context with an actual file right and then you can you know use that file to do things with it but again because you have written it in one line like this and it this scene instance doesn't really exist for long it also doesn't really make sense in a way if you catch my drift so um yeah i mean i would totally understand it if you you know had seen you set the path that's kind of the context it loads the file and now you can do many kind of operations on it like render it multiple times from different camera angles and you want it to all revolve around this context that in my opinion is a good use for like my hand class out of it but anyway moving on to the scene which is where everything begins let me take a look at what you've got here um okay so all right nothing really too big to draw attention to here this is interesting we'll take a look at this in a minute the the thing that stands out to me immediately is that everything is public right so some of this stuff probably does not need to be public you should probably hide some of this stuff and make it private right either that or just use structs because it looks like everything here is just all exposed which is again totally fine i don't really think that you need to be necessarily pedantic about private and public stuff unless you're designing an api this is clearly just like a little project um so i'm not sure if that's even worth drawing attention to but for everyone watching you know you probably want to not have some of this stuff here one thing i really like doing that i didn't used to do in the past i used to also just declare variables at the top i think i was coming from java and i was used to um you know you kind of declare your variables at the top of the file there is no header file c plus plus kind of file in java but what i do these days is i actually declare the functions at the top and then usually i have my private variables down the bottom i know these are public variables but same thing almost same thing so the reason i do this and again it doesn't might not make sense in this case because you have made everything public so everything is kind of user-facing but usually when you open a header file right the the first thing that you care about is what can i do with this class and usually that does not involve ah yeah the private members that i can't even touch right um and i mean in this case i doubt that you are going to be you know looking at this file and being like oh yeah i guess i'll play with the lights like this directly right especially if you if you happen to like have functions for setting lights or whatever so my point being that it's nice to be able to look at a class and the first thing you see is the api what can i do with this class you know how do i call the constructor what parameters what parameters does it take that kind of stuff right that's why i like to reorganize stuff um like that again i'm just using you as an example here because obviously everything's public so technically everything is user-facing and therefore maybe you would like to see this but i still maintain that the constructor is usually the first thing you want to look at when you're dealing with um like a class like this unless it's like a structure of data and the constructor is there for convenience uh camera you've got here okay this looks like okay so you've made your own little math library that's interesting um that's kind of cool uh let me just quickly uh breeze through this okay so you've got matrix 4x4 okay so basically everything you need for ray tracing it looks like you've pretty much got that stuff here now a word of advice to people doing this kind of stuff i probably wouldn't write my own math library right unless this was like the first time i've ever done any like graphics programming or mathematics and i really just wanted the experience of actually writing that stuff myself to learn more about it that is totally okay and in fact i probably encourage that but for the most part right i am going to assume that this specific project is about ray tracing right if you want to learn about ray tracing you don't need to write your own math library you definitely need to understand the mathematics behind it right but you don't need to like i wouldn't waste time making a math library basically is my point right unless as i mentioned earlier you really want to because you've never done it before and you're interested in how math libraries work and you're kind of this project is not just about ray tracing but from now on i'm going to assume this project is just about ray tracing so in that case i wouldn't use i i would i wouldn't write my own math library i would definitely use something like glm now the reason is twofold first of all it's a tremendous waste of time in my opinion to implement all of these functions make sure they work with different types and all of that stuff right because it's much better to just learn the theory of how to use these functions if that makes sense the implementation is not really that important the second reason though possibly more important is that uh this is going to be much slower than something like glm because this does not use any ssc instructions right so sse is a way for you to be able to kind of execute uh you know certain certain mathematical operations like multiplication between matrices and between vectors and stuff like that a lot faster because what you can actually do is load up these special registers on your cpu like a 128 bit wide register which holds four floats you can load them up with data and then execute that kind of multiplication operation in a single instruction rather than rather than four instructions right for each float so that tremendously speeds up stuff and something like glm obviously implements sse right they've written their math library with ssa support it's also called neon on arm architectures so the point being that like you know unless you're going to learn that which actually is something useful i think to learn to just be aware of how ssc works and i mean a lot of people don't even know that it exists so it's really it it is definitely very educational but my point is that you know instead of spending time on this you could have spent time learning a little bit more about ray tracing in fact i think the topic of ray tracing is something that pretty much every c plus plus programmer should touch at some point there's like this ray tracing in one weekend thing um it's like a little book slash website uh definitely recommend you guys check that out if you haven't already it's it's a fantastic exercise um it's actually something i wanted to make as like a little video series on youtube just basically implementing that ray tracing in one weekend thing and talking about everything and maybe how we can speed it up and stuff like that let me know if you're interested in that but that was an idea i had a while a while ago but yeah so those are kind of my thoughts on the idea of making a math library again not that i'm 100 against that i just think that um in the in the long term it's probably not something you generally want to do so let's dive into the scene constructor and see how this works so this. okay so this is like another thing right you don't need to use the this pointer right the only reason you would ever use this pointer really like this is if you had like you know you had a member variable called c name and a local variable called c name and you wanted to specifically reference like the uh the member variable right then you would have to use this if they have the same name again in my experience you wouldn't have the same name because you would use like a convention like m underscore and that would kind of fix that as well as you'll be aware of your member variables versus local variables um but and obviously the other use case is if you actually want like a pointer to the current instance for whatever reason then of course you'll need to use this or const if it's in a const function right that's pretty uh that's pretty pretty standard but otherwise there's just no reason to use that right just call the load scene function like this okay so if enable output then we output stuff uh okay cool road path blah blah blah um okay and then and then you do a lot of okay a few things here um so you basically do a lot of parsing that kind of what looks to be a custom file another thing that i probably wouldn't do right again unless you want to learn about parsing and learn about how to make your own file format which is totally totally legit nothing wrong with that but again if your purpose is ray tracing um i would focus on that instead and you could use a well-defined format like json or yaml i would use yaml for this because it's a little bit more human readable no like square brackets and curly brackets everywhere to keep track of and commas you know yaml is really quite nice for like human readability and for a human to edit right so that's what i would do instead of doing this stuff because you could use a library for that you wouldn't have to write the parsing code yourself and also it's just a more robust file format that can support a lot more things um you know like looking at this like i don't know is our space is implemented what if a space isn't and then i want to write this in quotes does your code handle quotes like there's a lot of kind of problems with custom file formats obviously and a lot of little things that you might not take into account um so in the in the general case for most people i would just use something like yaml the other thing that's kind of that's kind of uh standing out to me let's say is this kind of log error thing right so log error is obviously a macro that you've got going on here and like it's a little bit okay well so let's talk about this right so the reason you're using a macro here probably is because you want to use these kind of preprocessor variables right you want to know what file is coming from what the function name is and then also the line number right that that's totally legit but the thing is i would write this a little bit differently what you're doing here is you're kind of breaking cpu syntax and it's actually causing some problems so what i mean by that is you know because the preprocessor is literally just something that copies and pastes stuff right before we send it to the compiler it means that we can really define things that break the c plus kind of syntax and style and stuff like that so a good example is if i wanted to you know i could create a little begin and end keyword if i wanted to be more like some other language that has that i think pascal has something similar um but if i wanted to basically do that i could write code like this right and that would be totally fine it would compile it would work but the reason i don't do that is because it kind of breaks c plus syntax right like if we're reading c plus plus code we want to kind of be in the loop with c plus plus we don't want to do something that completely changes the language usually right and so that's what i don't like about this function or this macro right what i would do instead is i would add some parentheses and i would get rid of the semicolon it's a very very small kind of um uh change right but what that would mean is that instead of writing code like this right you would actually need to write code like that and now that looks like an actual function call and that makes sense even though it it's a macro you can see my syntax highlighter is saying there's a macro because it's purple but also it's in all caps and usually if something is capitalized we usually use that convention for macros right we wouldn't have a function uh you know with this kind of naming convention or this kind of naming naming style i guess but anyway so that's what i would do the other problem though with the with what you've done here which is not very good at all is what it looks like is that if this is the case your return result and then it looks like you return well nothing at all if if there was no error like this whole thing in this weird indentation which i can see you don't have here but this just looks bizarre right because again it's kind of tricking you it's almost saying that like well if that happens log error but really the purpose of this if statement is to execute this which again then results in no return no variables being returned what's actually happening here though because you of course do have a semicolon at the end of this macro what really is happening here is it's actually just doing this and then it's returning all the time right i mean look even like even my even visual studios can like confuse us to the indentation of this because there's no semicolon here and i guess it doesn't really properly evaluate that preprocessor so it's it's all a little bit weird and it breaks c plus syntax a little bit so what i would do instead is first of all never write one line if statements maybe with the exception of if you have a conversion function and it's like if the string equals blah blah blah return this enum value you know for stuff like that i do tend to put it onto one line because more or less a conversion function but for actual code flow functions like this put them on two lines like this right just do this right um why okay let's talk about why so first of all the re like the biggest reason why i like this apart from readability so i in my opinion this is easier to read right but obviously that's an opinion but in terms of um why i would actually do this i think that the biggest reason is probably debug ability right so the problem with having every having your if statement on one line is that if i put a break point here what happens well it's going to break when it hits this if statement and then also if it hits the result or not so so at a glance as i'm debugging i don't know if this evaluates to true or false right if it is like this very easy to see if i hit f10 once this breakpoint has been hit it's going to jump to this it's not going to jump into here so that already makes it easier the other really good debugging thing that this allows you to do is actually specifically break on errors right so again if this was all on one line like this then i i'm going to break on every call of this if statement which is useless to me because i want to break only on the error this lets me put a break point on this line and then only detect errors and then be in a state where i can debug why that was an error does that make sense so that's what i would do instead of um you know putting if statements on one line because it gives me that ability to kind of debug my code a lot easier and i also personally just think that it does in fact look a little bit more readable now i actually used to do one line of statements like this when i back up like 10 years ago when i used to program in java but since then i've definitely switched to this because i think it's just it's just a lot easier to deal with um so yeah so that's my kind of speech about your use of macros and also if statements uh and hopefully you can see kind of why that's a problem and why i wouldn't do it this way so that's all the whole parsing thing let's actually dive into some of the ray tracing code um and we will uh we will we'll we will get on with this the general structure of this code as i glance through it it's pretty good like i don't really have many issues with it there are some stylistic changes that i would you know i write my code differently like i would use um uh curly brackets i would open them up on a separate line again when i used to write code in java i did used to do it on the same line but since uh working with c plus and a little bit more in the professional industry i think this is much more readable um so that's what i would do especially because it is more or less like a c c plus kind of convention and usually i end up writing code uh usually not always but i end up kind of matching my convention with whatever language whatever the main convention kind of or the main kind of uh style is for a language um okay so let's surrender work let's kind of uh where's the actual render function right because i remember that that was something that was called from the main function so render okay so i've seen low success return negative one okay you're returning negative one but then you're not handling that so that's also something that maybe you could print an error or like i'm not sure handle that also long long like bro we're way past that simple you're specifically using c plus or 17. that's what you said right so you are way past that stop using int stop using well you can use end fine i'll i'll let you use int but long long i don't want to see that man if you want something to be a long long use in 64 t right or you went for unsigned long long right start using these types and stop using the kind of built-in types right because these obviously the biggest the biggest difference here is that these have a size in the name 8 bits 16 bits 32 bits 64 bits right we know how big these are they are guaranteed to be that size on whatever compiler on whatever platform you're working with this useless pile of garbage has absolutely no information about how big it is right along on linux is not the same as a long on on windows along windows typically is 32 bits right a long long is 64 bits which is why you see this defined that's different on other platforms so it's just it's really just annoying right so just use these as much as you can ins i don't in my experience it's like always 32 bits i'm sure there's probably not but i've never seen in b not 32 bits or not four bytes but so that's it therefore like i i do use int from time to time but i think if you want to be pedantic about it you should just use these types for all of your integer needs and that's it um so that's what i would do i would not use long long right that's uh definitely not a good idea so uh we have a frame but okay options high width so this is our main frame buffer this is that pixel array i was talking about vect3 so we have like an rgba sorry not rgba just rgb kind of float array of color values in here which is what we end up outputting to that bmp file oh how are you outputting the bmp file save image ah cool yeah that's what i thought so bnp is actually a really easy file format to work with to the point where you don't need a library really to kind of you know write bmp files or read bmp files you can see there's just a little header here which you uh basically so you have this header uh with a header size of 54 bytes that you kind of allocate here on the stack and then you particular offsets write in data which is what we what we do for the bmp file format and then you obviously uh basically copy across all those pixel values into that um into that buffer which is pretty cool right so it's really easy to do this that's again something i expected you to do just because you also made your own math library and did all that stuff so that's cool it's nice to see stuff like this teaches you a lot about it so i'm beginning to think this is more of like a general purpose educational programming thing and not specifically about ray tracing but that's cool um and then what are you doing here shell execute open okay so this is the code that actually opens up whatever your default windows app is for um looking at uh image files which is why it opened up photos on my computer and actually showed me that resulting result of the ray tracing so that's pretty cool that's kind of just just a little hunch i had okay so anyway um so where is that render function i keep losing it okay here we go show ac to show ac we need to go another routine what is show ac use ac show ac user okay well set to it's set to zero okay bro like balls don't set them to one and zero set them to true and false like what's going on there come on um yeah that's interesting well we didn't do this so we won't do this but maybe this is like some somehow it i don't know what this does i mean like i'm like in regards to what this does compared to this but anyway launch workers is what happens launch workers so if debug render work in debug mode will not create any additional threads oh man okay so why why is this okay i i okay where do i start with this i would not do this um for a number of reasons first of all i i really don't understand why you would not multi-thread this in debug like that doesn't make any sense to me if you really wanted to have a single threading mode well just have like you know if single threaded then whatever right that makes a lot more sense or you could even have it as like a runtime variable um not necessarily not you don't need to make it like a preprocessor thing and then recompile your code if you want to switch between the two modes but my point is um so first of all debug resource is already slow especially for stuff like cpu side ray tracing debug is a lot slower on the math side of things right um so because of that you're already kind of shooting yourself on the foot with trying to get any kind of good performance i mean i know you're more about like seconds per frame here instead of frames per second but my point is that it's that's why i couldn't even run it in debug mode i had to switch the release immediately which presents another problem what if i have an issue that i'm trying to debug well i have to what sit through like you know five hours of waiting for stuff to render before i finally hit my break point on like this in this in this kind of lighting pass or whatever this reflection pass iteration or whatever you know when i'm tracing that ray and i want to debug it like that's it just makes debugging a lot harder also what if i'm having multi-threading issues what if i'm having things that are weird race conditions that i want to maybe debug by putting a break point in and seeing what thread ends up calling that like it it just it's just a bit of a mess right and i can't do that because apparently i only multi-thread and release so that's a bit weird to me like i definitely wouldn't do this um try and keep your code more or less the same in debug and release unless of course there's extra code like error checking that you want to do only in debug that's totally valid but changing the whole architecture of everything between debug and release is a bit weird um so i definitely wouldn't do that um so so okay so this calls okay so it ultimately does the same thing right so zero and what does this function take uh frame buffer uh y zero y one so that's okay okay i see right okay so you've split the i guess you've split it into vertical strips how many threads do we have eight jokes on you i have 16 cores and 32 threads but anyway um so we've split it up into eight threads and they're working on these kind of vertical slices um of the image right so this is actually kind of similar to what we looked at last time with the voxel engine however it's obviously doing ray tracing instead um although i guess maybe the voxel thing could be perceived as some form of ray tracing maybe well it's more great casting i guess but anyway whatever um so uh that's what we're doing here right as opposed to this in which we go from zero to height right so we go from oh maybe we're doing horizontals no we're probably doing horizontal slices i don't know why i thought vertical slices um from zero to height yeah yeah we're doing horizontal which is better anyway you usually want to do that um tiles would also be interesting but anyway so we go all the way from zero to height in a single pass versus splitting it up into eight different sections and working on them simultaneously which is what uh the purpose of kind of multi-threading this is i guess so um again wouldn't do that i would definitely run it multi-threaded in both debug and release let's dive into this a little bit okay so you have a thread pull which is cool you have a thread pool of unique pointer threads which you're creating here again no reason to do that right um instead of doing this i would make a thread pull of just threads because i don't really see why you would want them to be unique pointers right so unique pointers um obviously can't be copied same as threads i'm pretty sure the thread copy constructor is disabled so let's just say okay so let's just say i did this let's actually move this down yeah so what i would do ultimately is have a thread pull let's call this churno pool um that's like like this right and then um so if i try and copy this uh to here i'm assuming yeah so that won't work because the copy constructor has been deleted but i can still move it just yeah thanks bro just just like as what's happening here you can't copy you can't copy unique pointers but you can move them into this vector which is what's going on here um in fact we can see that it's specifically the move the r value reference version of pushback that's being used here right so in terms of like copying and moving stuff around you can do whatever you can you can do whatever you want with thread um as with unique pointers right but also i yeah i just don't see any reason why you would ever use unique pointers here i mean you only have eight instances of them so it's not like the heap allocation cost um is gonna be like something that you'll even see but i just think that like it's in general it's good to get into the practice of just not heap allocating whenever you can avoid it right so just try and allocate stuff on the stack as much as you can unless there's a specific reason why you need why you need it to be on the heap right um a lot of people see unique pointers and smart pointers as a way that obviously they hide that new kind of call and so it looks like maybe it's not as scary or not as bad but obviously they're still calling you i would definitely um just make a vector of threads like this so um yeah so that's the only thing here but it's good that you're you know you're not creating these every frame i mean you don't have the concept of frames you just launch this and you finish your render and then that's it the application terminates um so uh yeah but in general it's nice to have a thread pull which you then wait for every thread to join obviously so you wait for every thread to complete the rendering before you actually bounce back to this code and return zero so that's fine um the pitchfork is a load so what's this okay so you're using atomic variables for statistics so i'm assuming each of these workers when they finish they write um okay so i wouldn't use load right i would just use with atomic variables you can just use them as normal variables like as if they were just that right so there's no real reason to use load there are technically some differences between load and not using load but in this case you're not going to see any difference so i would definitely write the code just like that because it's just a little bit easier to read you don't need the load function everywhere but other than that like good you know um obviously you don't want a case in which you uh try to try to write to one of these variables simultaneously so atomic is is a good fix for that um and then okay i see so this is just this is basically just you on the main thread if apple progress is set to true um you're basically you just have this like a loop that basically prints how long does it sleep for like 10 milliseconds in this case okay whatever um you just have this like little loop here that basically at a fixed time interval it looks like um it just outputs the percentage of that's what we saw that's where we saw the percentage being output like when it said 94 whatever um that's happening here so on the main thread again which is you don't need to like lock this uh logging log log lock you don't need to log log um because uh we're on the main thread we're only printing stuff from the main thread um for anyone else there who might be multithreading stuff just remember if you are printing from multiple threads like see out isn't thread safe so you want to probably have a lock have a log function and then have it like a little spin lock that you um basically lock the that function so that you can't call it from multiple threads at once because then you'll get stuff printing in the middle of other stuff and yeah so but but doesn't apply here because we're only doing the printing on the main thread which is cool so this is a nice little system and then we join so finally last thing we'll look at today this is a very long video um but i don't know i'm just this is just what i'm doing this is just me that's just what i happen to be doing today looking at this for an hour um so render worker so we get a frame buffer um we have this like uh you know kind of region here on the y-axis that we've decided to render to and then i'm assuming yeah we do like class ray we get away from the camera based on where we um which pixel i guess we're working on um and then we do so again you know multiple line i would drop your little uh if statements here especially if you seem to be doing it sometimes we have a little show normals thing which is cool um i mean again i'm not really going to get into like the algorithm of ray tracing if you guys are interested in that then i'll leave a link in the description to the ray tracing in one weekend thing um because that's that's definitely a good read and it's probably good for everyone to read but um yeah i mean this just looks like pretty typical so you've got area lights so area lights and these are all kind of pointers because i think that's probably okay so you have a light hierarchy um kind of like i guess what you that's why i saw that object pointer as well i guess object is also a base class for a lot of things like triangles meshes and spheres and stuff like that i assume yeah there's sphere okay i mean that's reasonable i don't know if i would hierarchically kind of structure it um with inheritance and stuff like that if i was doing it it's just a stylistic thing no real reason not to i guess um and i'm assuming that these things do in fact have different functions like setpoints and illuminate what does that do nothing uh yeah i think one thing that i probably wouldn't do as well is have these functions like you know directional or distant light distant light interesting definitely it looks like a directional light although it does have a point that is unused um yeah i i guess i get why you're doing that it's because lights are different types and you have a different illuminate function for each but i think i would try as much as possible to contain the algorithm within like the function right i think the only real reason that you would want to maybe bring stuff out into functions is if you want to like specifically avoid code duplication that's very valid and that's where you would do that like you don't want to be copying and pasting the same code multiple times but changing parameters that's what functions are for but you know sometimes people tend to break stuff up into functions or into classes for no reason and it actually makes stuff harder to read because instead of looking at this screen like i am right now and seeing the whole algorithm i have to jump to this function and then be like ah but what light type is it you know what i mean it just makes stuff a little bit harder to read um so that's why like i i would maybe avoid that not to mention that if it's a it'll be a virtual function call here which isn't a big deal but potentially worth being aware about um so i would try as much as possible to just contain the algorithm within this function or again with code duplication you know maybe bring it out into other sub functions that are also kind of in this file and i'll call this file like renderer dot cpp so that's probably what i would do if i was doing this uh maybe i will when i do that ray tracing in one weekend video series but um yeah other than that like yeah you've just got some kind of basic like rendering equations here there's nothing too like complicated with this i think um not nothing i can see from the code nothing i can see from the result really you're just basically shooting out racing what they hit mathematically what they intersect what the color should be based on the material on the surface and stuff like that reflecting rays and refracting them for certain glass types i guess because i i think i saw refract yeah refract compute refraction and this is for like transparent material so if you've got like glass so yeah so overall um yeah pretty cool little project uh definitely a lot of things i would probably uh change if it was me writing it as well as i would definitely um try and kind of make it real time i'm more of a real-time rendering programmer but i do still love ray tracing and i think that you could definitely start to maybe move this stuff into like a compute shader or even keep it cpu but kind of um make an application out of it that you can actually see the result with and play around with like where the camera is and stuff like that um that's where i would probably take this next if you were serious about kind of making a ray tracing engine because that's obviously something that's um possible to do and also i think a lot of fun but generally i don't have that much like i mean apart from what i've said i like the structure of your project actually one thing that um kind of caught my eye when i glanced at like the source directory is like you've only got five cpp files and like they're pretty big like well they're not that big but kilobytes 25 kilobytes like this is good you know a lot of code that i look at has three times the number of files and they're all like two or one kilobyte right that's really annoying for me to look at and i i don't like that like i think i remember back when i worked at ea um i looked at frostbite and i'm pretty sure that like the entire directx 11 i think renderer that frostbite had like all of the api calls to directx 11 at least back in the day they were all in one file like they were just a file called dx11 or dx12 or whatever and they were just all there like literally the whole the whole render it's like 20 000 lines or something like that the whole kind of back end for that api was in a single file and i looked at that i'm like man this is amazing i i thought i would have to spend like hours looking through the whole code base and you don't want to look through the whole code base man there's like 250 projects in the visual studio solution i think something like that like you don't want to look at the whole like it's a there's a lot it's very complex right but seeing that was like it was almost like a light a light shining from above that was just like it's all here man you just look at the whole thing here like you don't have to you don't have to open more than one file literally like it's amazing so i really like that i really like that approach to software again sometimes you could argue it doesn't make sense i agree i'm not saying you should have main.cpp and that is the end of that i'm saying that trying to minimize separating all of that stuff out um and abstracting stuff in like an oop way which is what so many people love to do right i think that dramatically helps pretty much everything anyway i hope you guys enjoyed this video please don't forget to hit the like button if you did uh cherno review gmail.com is in the description and that is the email address where you can send me your project if you guys have any suggestions for projects that you want me to look at i've been looking at a lot of like rendering related stuff this is this is stuff i'm naturally interested in i mean we haven't looked at ray tracing before but like if there's something specific that you want me to take a look at i know you don't really know what emails i received but still if there's a topic just write a comment below send in that email with whatever project you um you have and you want me to take a look at don't forget to check out skillshare using the link in the description below and i will see you guys next time goodbye [Music] [Music] you
Info
Channel: The Cherno
Views: 84,222
Rating: 4.8898387 out of 5
Keywords: thecherno, thechernoproject, cherno, c++, programming, gamedev, game development, learn c++, c++ tutorial, game engine, ray tracing, code review, c++ ray tracing, file system, c++ files
Id: sb1FtMYAcYQ
Channel Id: undefined
Length: 58min 32sec (3512 seconds)
Published: Fri Sep 03 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.