Raytracing With Landscapes
Introduction: Raytracing With Landscapes
Lately, I've been playing Genshin Impact and was blown away by the quality of the graphics in this game. Wow! Take a look at the terrain, the shadows cast by the abundant foliage, and the sunsets which change the usual greens and browns into a spectacular orange. All of this runs on my little iPhone SE, though you can get it on nearly any platform you prefer.
But Genshin is completely raster-shaded. It renders its graphics polygon by polygon, handling collision and lights on a per polygon basis. Meanwhile, a game like Cyberpunk is (in)famous for its raytraced graphics. Night City has so many levels and lights that it'd be unreasonable to process all that geometry. Instead, the game does it by simulating "photons" of light, where the path of the photon determines the color of each pixel. With this method, you can get more realistic effects and complicated behavior than raster.
Can we combine them? <PERSON>'s landscapes with raytracing? Let's see.
This instructable is meant to be a roadmap for someone who is familiar with C++ or a similar language like Java and is interested in building a neat little engine from scratch. I provide room for you to make your own customizations and extensions. If you are looking for specific code, Ray Tracing in One Weekend is a great classic tutorial (and it has heavily inspired the code snippets below as well).
The Instructable below is a S.I.D.E. project I worked on as part of Ms. <PERSON>'s POE class. To see more projects from our class, check out her website.
Step 1:
Raster-shaded games have been around forever. There are tons of optimizations and dirty tricks needed to make them look good. But through raytracing takes more graphics power, the concept and code are simpler. 983
We don't need many tools to get this running. I’ll try to keep external libraries to a minimum. Of course, if there are alternatives you prefer (especially if you are not using C++) feel free to mix and match.
C++
It’s fast. It's popular, which means it comes with good support. It comes with support for OO, which is very useful for this project (be ready for a good deal of programming)
Here I use the MSVC compiler with C++17, though any other modern C++ compiler and operating system should work - what I do in C++ should have equivalents in most other languages.
I'll leave it to you to set up your dev environment of choice, though if you're looking to get started fast, read on.
CLion
When in doubt about how/what to set up, just get an IDE. I use Clion over alternatives like DevC++ or eclipse because it doe such a good job of solving your problems for you.
Github
Big projects need version control. You'll thank yourself later. If you're using Clion you can set this up with a few clicks.
Step 2: Window Drawing Libraries
In Clion, you can set your C++ toolchain in Settings | Build, Execution, Deployment | Toolchains. It does a good job of auto-detecting if you already have C++ on your system.
Likewise, for Github (or Version Control of your choice), go to VCS | Enable VCS, and follow the GUI.
There are no graphics headers in the C++ library. Possibly, this is because C++ is used in so many contexts that it's impossible to have a standard graphics lib that would make everyone happy. Go get yourself the OLC Pixel Game Engine header (thanks, OneLoneCoder) and add it to your project directory. PGE is simple and works out of the box. You're free to use something else, like SFML or CINDER or Swing, or write directly to screen buffer if you are that kind of maniac.
Step 3: Drawing to Window
The OnUserUpdate function in the sample OLC program (which you can find on its Github) is called after OLC clears the screen buffer and is ready for the next frame. This is when we can call Draw (double x, double y, olc::Pixel pixel color) to specify individual pixels. This is all that our barebones raytracer needs in terms of drawing operations; the rest will be in nearly pure C++.
As an example, here I loop across every row and column and draw a gradient that depends on the pixel coordinates.
Step 4: Structure
For every frame of our raytracer, we
1. iterate over every pixel and send out a ray in the direction of that pixel. 149