Friends from the Engineering School and ACM Class have finished their courses! Check out these amazing artworks:
The ACM Pseudo Photography Company, affectionately known as PPCA, was established in 2021. 😉
The main goal of this project is to implement a ray tracing renderer using the Rust programming language. Through this, you'll deepen your understanding of programming language design and compiler principles by learning a new (and beautiful) language, while also getting a fun introduction to the fundamentals of computer graphics.
This year, we've added a peer-review session. Use your hand-written renderer and unleash your artistic talent to create stunning, surreal masterpieces!
Primary reference materials:
More references can be found in the Reference section below.
Click "Use this template" at the top right to copy this project to your own GitHub profile. Then, you'll need to do some preparation.
- Modify the author information in
raytracer/Cargo.toml. - Change the author in
LICENSEto yourself. You may also switch to a different license. - Install Rust using rustup. If the download speed is slow, consider using the SJTUG Mirror for rust-static and crates.io.
- Next, install some tools. Navigate to the project directory and run
rustup component add clippy rustfmt. - Then, run
make ci. If the program runs successfully, your environment is set up. - Push these changes to GitHub. Both "Lint and Test" and "Build and Upload" should pass in GitHub Actions.
- The program output will appear in the GitHub Actions artifacts. The
outputfolder should contain content generated at runtime. Changes to theoutputfolder should not be synced to GitHub (this folder is listed in.gitignoreand will be ignored by git). - Finally, you can remove the tutorial sections from README.md and replace them with your own program description, usage instructions, etc.
We recommend spending the first week getting familiar with Rust syntax. Please read The Rust Book (or another tutorial you find suitable).
- Generally, you'll only need Chapters 1-6 and Section 10.2.
- If you run into lifetime issues, read Chapter 4 carefully, especially the examples in 4.2.
- You can also use smart pointers from Chapter 15 to resolve some lifetime-related issues.
- Rust's object-oriented features (traits, analogous to C++ classes) can be found in Section 10.2.
- (Advanced) When working with multi-threaded rendering, you can read Chapters 15 and 16.
-
Ray Tracing book 1 — a gentle weekend.
-
Code review (60 pts for Engineering School, 30 pts for ACM Class): Monday of Week 2.
- Details related to Book 1.
- Rust proficiency (basic, not beyond the required chapters).
-
Ray Tracing book 2.
-
Multi-threaded rendering.
-
Code review (Engineering School only, 35 pts for both Engineering School & ACM Class): Friday of Week 2, 3:00 PM.
-
Required portion (20 pts — no points if incomplete): BVH, Rectangles and Lights. The remaining parts are scored based on completion, capped at finishing all of Book 2 + multi-threaded rendering.
-
Pay special attention to the implementation details of Book 2, especially the BVH section — make sure you understand it!
-
Engineering School friends finish the course here! 🎉 Peer review session! 🤯
-
-
Ray Tracing book 3 (20 pts for ACM Class).
-
Advanced features (Bonus, 10 pts for ACM Class).
- Full marks for completing 3 tracks.
- If you hand-wrote your own obj_loader (without using a library), you only need to complete 2 tracks (including Track 7) for full marks.
-
Code review: Friday of Week 4.
- Details related to Book 3.
- Details related to advanced features.
- ACM Class friends finish the course here! 🎉 Peer review session! 🤯 (5 pts for ACM Class)
-
Track 1: Reduce Contention — Prerequisite: multi-threaded rendering. In a multi-threaded environment, cloning/dropping
Arccan cause performance degradation. The goal is to reduceArcusage: cloneArconly when spawning threads; everywhere else, use references instead ofArc. -
Track 2: Static Dispatch — Calling functions on
Box<dyn trait>/Arc<dyn trait>/&dyn traitincurs extra overhead. We can solve this with generics.- Define new generic materials, transforms, and objects — e.g.,
LambertianStatic<T>— and use them in your scenes to reduce dynamic dispatch overhead. You can also create a separate module with structs that share names with the original materials. - You can find examples of generic usage in
material.rs. - Only use
dyninHitRecord,ScatterRecord(from the remainder of Rest of Your Life),HittableList, andBVHNode. - If interested, explore using
macro_rules!to reduce boilerplate from writing nearly identical code twice.
- Define new generic materials, transforms, and objects — e.g.,
-
Track 3: Code Generation — Prerequisite: BVH.
- Currently,
BVHNodeis constructed at runtime. This process can actually be done at compile time. Use procedural macros to generate all objects and build a staticBVHNode, improving rendering performance. raytracer_codegenandraytracerlikely can't share modules, so you may need to copy some implementations (e.g.,Vec3) intoraytracer_codegen.- You can use
cargo expandto inspect the code after macro expansion. You can also output the generated code directly during compilation. - The
codegenportion does not need to pass clippy. - If interested, explore passing arguments to procedural macros — e.g.,
make_spheres_impl! { 100 }to generate a function that produces 100 spheres.
- Currently,
-
Track 4: PDF Static Dispatch — Prerequisite: the remainder of Rest of Your Life. Use generics for the objects handled in PDFs, removing
&dynfrom the code path. -
Track 5: More Code Generation — In a procedural macro, read a file and generate the scene from a YAML or JSON file (pick one).
- Examples are provided in the
datafolder. - The
bounding_boxfield in the exampleBVHNodeentries is redundant — you can ignore it. - You may use a library to read JSON / YAML.
- Examples are provided in the
-
Track 6: Advanced Features — Add PDF support for Transforms.
-
If you have extra time, benchmark the before and after of your optimizations.
- Before starting Track 3, back up your code (e.g., record the git commit id). When working on Tracks 4, 5, and 6, keep the original scene and program and add new content on top.
- You can use the
criterioncrate for benchmarking. For example, benchmark the time to cast a single ray by shooting random rays into a pre-built scene.
-
Track 7: Support for .obj — Support loading and rendering
.objfiles. To complete this, you may need to:- Understand the OBJ file format.
- Implement an obj_loader.
- You may use a library, e.g.,
tobj. - See the tobj documentation for the OBJ format and library usage, or search on your own.
- You may use a library, e.g.,
- Implement rendering of simple polygons.
- Supporting OBJ will make your final masterpiece even more spectacular! :) Image source: reference materials ↓
The Makefile contains common commands for running the raytracer. If make is not installed, you can also run cargo ... directly.
make fmt— Automatically formats all Rust code.make clippy— Enforces additional code style constraints.make test— Runs unit tests. YourVec3implementation must pass all tests.make run_release— Runs the optimized program. Generally, you should use this to run the raytracer; otherwise, rendering will be very slow.make run— Runs the program in debug mode.make ci=fmt + clippy + test + run_release. It's recommended to runmake cibefore pushing code to the remote repository.
This repository is pre-configured with GitHub Actions. Whenever you push code to the remote repository, GitHub runs the following two checks:
- Lint and Test — Runs all unit tests and checks code style.
- Build and Upload — Runs the optimized program and uploads the generated files from the
outputdirectory as build artifacts.
- The Rust Programming Language
- rustlings — A collection of small Rust exercises. If you prefer learning Rust through practice, give this a try.
- Ray Tracing in One Weekend — The Book Series
- (Advanced) Procedural macros:
- Procedural Macros (focus on function-like procedural macros)
- quote crate
- (Advanced) JSON / YAML reading:
- serde-json — only the untyped part is needed.
- yaml-rust
- Generally, you won't need the serialization/deserialization package below.
- serde

