Skip to content

Commit fd01869

Browse files
committed
Add ray tracing tests
1 parent b21b9b5 commit fd01869

4 files changed

Lines changed: 715 additions & 101 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

TODO.md

Lines changed: 5 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
- [x] Fix `ray_color` in `cam.rs` to accumulate emission with throughput
99
- [x] Create test scene with emissive quad (Cornell box)
1010
2. [x] Quads (axis-aligned and arbitrary)
11-
3. [x] Transforms (translate, rotate, scale) - see design notes at bottom of file
11+
3. [x] Transforms (translate, rotate, scale)
1212
4. [ ] Image textures
1313
5. [ ] Noise textures (Perlin noise)
1414
6. [ ] Bounding Volume Hierarchy (BVH)
@@ -35,107 +35,11 @@
3535
5. [x] Grid composition for `test` command
3636
- [x] Render each scene at 720p
3737
- [x] Stitch into 4x4 grid image
38-
6. [ ] Unit tests for ray-material interactions
39-
- [ ] Shoot specific rays at specific materials, verify resulting scattered ray
40-
- [ ] Test cases: Lambertian diffuse, Metal reflection, Dielectric refraction (entering/exiting)
41-
- [ ] Helps catch regressions like the normal-flip bug from instance transforms
38+
6. [x] Unit tests for ray-material interactions
39+
- [x] Shoot specific rays at specific materials, verify resulting scattered ray
40+
- [x] Test cases: Lambertian diffuse, Metal reflection, Dielectric refraction (entering/exiting)
41+
- [x] Helps catch regressions like the normal-flip bug from instance transforms
4242

4343
## Technical Debt
4444

4545
1. [ ] `Array<T, N>` currently requires `T: Copy + Default` because we use `[Default::default(); N]` for initialization. This is overly restrictive. Ideally, `T` would only need to be "zeroable" (all zero bytes is a valid default). This would allow types like `Sphere` that aren't `Copy` but can be safely zero-initialized. The goal is something like `core::array::from_fn(|_| Default::default())` but in a form rust-gpu accepts.
46-
47-
---
48-
49-
## Design: Instanced Transforms
50-
51-
### Current Architecture
52-
53-
The ray tracer currently uses `List` which contains arrays of world-space primitives:
54-
55-
```rust
56-
// rtx-obj/src/list.rs (current)
57-
struct List {
58-
spheres: Array<Sphere, N>,
59-
quads: Array<Quad, M>,
60-
}
61-
```
62-
63-
Each `Sphere` stores its center, radius, material, and precomputed bounding box.
64-
Each `Quad` stores its corner, edge vectors, normal, material, and bounding box.
65-
The `hit()` method iterates both arrays, testing each primitive in world space.
66-
67-
### New Architecture
68-
69-
Instead of storing concrete primitives with world-space coordinates, we use
70-
canonical unit primitives (implicit/hardcoded) and transform them via instances.
71-
72-
**Canonical primitives (not stored, just hardcoded in hit functions):**
73-
- Unit sphere: radius 1, centered at origin
74-
- Unit quad: 1x1, at origin (corners at (0,0,0) and (1,1,0), normal +Z)
75-
76-
**New types:**
77-
78-
```rust
79-
enum PrimitiveKind { Sphere, Quad }
80-
81-
struct Instance {
82-
kind: PrimitiveKind,
83-
transform: Mat4, // object space -> world space
84-
inv_transform: Mat4, // world space -> object space
85-
material: MaterialInfo,
86-
}
87-
88-
struct Scene {
89-
instances: Array<Instance, N>,
90-
}
91-
```
92-
93-
**Hit logic:**
94-
1. For each instance, transform ray to object space via `inv_transform`
95-
2. Test against hardcoded unit sphere or unit quad
96-
3. Transform hit point/normal back to world space via `transform`
97-
98-
**Mat4 capabilities:**
99-
- Translation: move objects in space
100-
- Rotation: rotate around any axis (start with rotate_y)
101-
- Non-uniform scale: `scale(4, 1, 1)` stretches only on X axis
102-
- Compose as: `translate * rotate * scale` (apply right to left)
103-
104-
**Mat4 structure (column-major):**
105-
```
106-
[ Rx Ry Rz | Tx ] R = rotation/scale (upper-left 3x3)
107-
[ Rx Ry Rz | Ty ] T = translation (fourth column)
108-
[ Rx Ry Rz | Tz ]
109-
[ 0 0 0 | 1 ]
110-
```
111-
112-
**Transforming Vec3 with Mat4:**
113-
glam's `Mat4` multiplies with `Vec4`, not `Vec3`. Use the `w` component to control behavior:
114-
- **Points** (positions): use `w=1`, affected by translation
115-
`transformed_point = (mat * vec4(point, 1.0)).xyz`
116-
- **Directions** (ray dir, not normalized): use `w=0`, NOT affected by translation
117-
`transformed_dir = (mat * vec4(dir, 0.0)).xyz`
118-
- **Normals**: use transpose of inverse matrix, or since we store `inv_transform`:
119-
`world_normal = (inv_transform.transpose() * vec4(local_normal, 0.0)).xyz`
120-
(then normalize)
121-
122-
### Implementation Plan
123-
124-
Phase 1: Add new types (non-breaking, ray tracer still works)
125-
- [x] Re-export `Mat4` from `spirv_std::glam` in `rtx-prim/src/types.rs`
126-
(glam already provides identity, from_translation, from_rotation_y, from_scale,
127-
inverse, and all the Mul impls we need)
128-
- [x] Create `PrimitiveKind` enum in `rtx-obj`
129-
- [x] Create `Instance` struct in `rtx-obj`
130-
- [x] Implement hardcoded `hit_unit_sphere()` and `hit_unit_quad()` functions
131-
132-
Phase 2: Refactor ray tracer to use instances
133-
- [x] Create new `Scene` type that holds `Array<Instance, N>`
134-
- [x] Update `hit()` logic to iterate instances, transform rays, dispatch to unit primitives
135-
- [x] Migrate existing scenes to use `Instance` with appropriate transforms
136-
- [x] Remove old `Sphere`, `Quad`, and `List` types once migration is complete
137-
138-
**Note:** The `t` parameter may need adjustment for non-uniform scale. When the ray
139-
is transformed to object space, direction length changes, so the same `t` value
140-
maps to different world-space distances. May need to recompute `t` in world space
141-
after hit, or be careful about comparing `t` values across differently-scaled instances.

crates/rtx-obj/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,8 @@ spirv-std = { workspace = true }
99
rtx-prim = { path = "../rtx-prim" }
1010
rtx-mat = { path = "../rtx-mat" }
1111

12+
[dev-dependencies]
13+
rtx-tex = { path = "../rtx-tex" }
14+
1215
[lints]
1316
workspace = true

0 commit comments

Comments
 (0)