Team: 28
School: Los Alamos Mid
Area of Science: Graphics
Interim: 1. Overview
There are many ways to render 3D graphics with all being unique and taking different amounts of time to run. Some rendering techniques are 3D perspective, ray tracing, ray marching, ray casting, and isometric tiles. My project is to explore these rendering techniques. I implemented these rendering methods using python3 with pygame for visualization purposes, and with ray marching I also created a version using C++ using a website to compile and visualize the results [2]. The details on these methods are provided in section 2. The Speed tests section (section 3) contain a discussion of run times of the rendering methods and which methods are the best in different situations. My ray tracer speed test was performed using multi threading on a 2 core cpu and I found a 1.5x speed up on my ray tracer using it. I created a GitHub page to store the codes.
2. Rendering Methods
3D perspective is where you take two 2D linear lines and find what x is at 80. The value 80 would be replaced with a variable representing the players field of view (FOV). This first line represents the change in x over the change in z. This will get the x position on the screen. The other line is the change in y over the change in z times 80. Once again the value 80 would be replaced by a variable FOV. This will get the y position on the screen. After this you add the distance the camera is from the origin (0, 0, 0). This method requires rotating the position of the object before getting the position of it on the screen (the original coordinates stay the same but you create a copy and change the copy).
Ray tracing can be done in two ways. The first is forward and the second is backward. For forward ray tracing you have to trace billions of rays from the light source and it takes days to run because most of those rays miss the camera but still need to be calculated. The other way, backwards ray tracing only casts rays from the camera into the scene meaning that no rays will miss the camera and allows for real time ray tracing (this means it runs in a few milliseconds). Because of this, we will be using backwards ray tracing. For backwards ray tracing you will take rays from the camera and trace them into the scene and have the light travel on its path to the camera from the light source but backwards meaning you find 2 2D linear lines from the camera to the plane (this is size of the screen then normalized) which is at 80. The value 80 is replaced by a variable representing the players FOV. After this you trace the ray until it hits something. If it's solid, then you trace a ray to the sun to see if it should be in a shadow or not. If it's transparent then you continue and mix the color of the glass and the color of the final thing that it hits. You also change the angle of the light - this is called refraction. Finally if it's a mirror, then you reflect the ray and continue on. These rays will only go until the distance they moved is greater than the maximum render distance so rays won't continue on for forever and crash the ray tracer.
Ray marching is the big brother of ray tracing. It does the same thing as forward and backwards but once again we will use backwards as it produces the same output more quickly. The only difference that ray marching brings is that instead of marching 1 by 1 you find the distance (using a distance function for each shape in the scene) to the closest object and move that far. This is because you know that you will not pass through an object if you move that far because it was the closest thing. This means to hit a sphere a distance of 500 away, you may only have to step 100 times instead of 500 and this is a HUGE optimization. Another way you can make ray marching quicker is by, instead of checking if the distance is equal to zero, check if it's less than a small number like 0.01. This also means you can render fractals!!! This is because you can create a distance function for fractals and any shade you can make a distance function for, you can render.
Ray casting is very similar to ray tracing but can't be used in a forward manner. It is rather used only backwards and basically its ray tracing but you only do a 2d ray tracer and therefore don't also iterate over the screens y resolution. Basically you find where the ray hits a wall and use math to find how high the wall is. This means the camera can't move up or down but only left, right, forwards, and backwards. This also means the world can't be more than 1 story. This method is really old but can still create fun 3D games. It also happens to have been used for the original 3D arcade games such as wolinsine 3D as it's very quick. There’s also an optimization where you find where the ray intercepts with a grid with its spacing equal to the tile size of the map. You can find this on the x and y axis and move by going from intercept to intercept, meaning, you may only need to move a few times to hit a wall. Many games have been made with it and allow for quick rendering of a scene on slow hardware.
Isometric tiles is a way to render a 2D game that looks like a 3D game. This is produced by using a tiled tile that's created in a way to look 3D and then rendered as if it was a 2D game, but with some changes. This is not a very good way necessarily to create a good 3D game because you can't rotate the camera and do a lot of other things. It also lacks perspective. This mostly is used to give 2D games a bit of depth. An example of it being used is by the game “Lumermill†[1].
3. Speed tests
I performed speed tests for each rending method described in the previous section. I also tested multithreading with python3 to run the code on multiple cores on the cpu. This can speed up your program by quite a bit. I only tested the ray tracing program with multithreading and got a speed up of around 1.5x on a two core cpu. The reason it's not 2x is because starting and running a thread takes some time so you wont get the full speed up. I also found that my C++ ray marcher program is much faster than my python3 one and this is simply because C++ is something like 50x+ faster than python3 in my experience. The results are as follows where frames per second is denoted as FPS.
3D perspective = 15 FPS
Ray tracing = 800 seconds
Ray marching (backwards) python3 = 60 seconds
Ray marching (backwards) C++ = 60 FPS
Ray casting = 10 FPS
Isometric tiles = 20 FPS
4. References
1 Lumermill game: https://store.steampowered.com/app/1151300/Lumbermill/
2 Shader toy compiler/shader: https://www.shadertoy.com/
3 My project on ray tracing on repl.it: https://repl.it/talk/share/Ray-Tracer-20/76999
4 My tutorial on 3D perspective on repl.it: https://repl.it/talk/learn/Haha-yup-thats-python-for-you-lol-Is-t/57491/362349
5 A link to a website that helped me understand 3D perspective and ray training: https://www.scratchapixel.com/lessons/3d-basic-rendering/introduction-to-ray-tracing/how-does-it-work
6 Another link to another website that helped me understand 3D projections and ray training: https://www.scratchapixel.com/lessons/3d-basic-rendering/computing-pixel-coordinates-of-3d-point/perspective-projection
7 A project on scratch.mit.edu that inspired me to do this project: https://scratch.mit.edu/projects/412737809/
8 The source that contained the tutorial that [7] used: https://www.realtimerendering.com/raytracing/Ray%20Tracing%20in%20a%20Weekend.pdf
Name: Andrew Morgan
Mentor: Nathaniel Morgan
Team Members:
Sponsoring Teacher: Nathaniel Morgan