city scene
This commit is contained in:
parent
ac3d81359c
commit
02b92c32ac
3 changed files with 107 additions and 0 deletions
|
|
@ -93,6 +93,7 @@ It uses all cores via rayon, shows a progress bar and (for the `cornell` scene)
|
||||||
| `spheres` | Large scene: ~470 random diffuse/mirror/glass spheres under a sky (after *Ray Tracing in One Weekend*). The BVH makes it ~11x faster than testing every object |
|
| `spheres` | Large scene: ~470 random diffuse/mirror/glass spheres under a sky (after *Ray Tracing in One Weekend*). The BVH makes it ~11x faster than testing every object |
|
||||||
| `night` | The same field at night, lit by 61 glowing spheres. Each shading point picks one light, weighted by power / distance², so many lights cost no more shadow rays than one |
|
| `night` | The same field at night, lit by 61 glowing spheres. Each shading point picks one light, weighted by power / distance², so many lights cost no more shadow rays than one |
|
||||||
| `classic` | The classic Cornell box: one-sided ceiling panel light, two boxes, colour bleeding from the walls |
|
| `classic` | The classic Cornell box: one-sided ceiling panel light, two boxes, colour bleeding from the walls |
|
||||||
|
| `city` | Massive scale: a procedural 400x400-lot city of ~124,000 objects (buildings with stepped tiers, mirror-glass towers, parks with trees) under a low sun. Renders 800x600 at 64 spp in ~2.5 s; without the BVH it is ~1000x slower |
|
||||||
|
|
||||||
Scenes are defined in [`rust/src/scenes.rs`](rust/src/scenes.rs) with a small builder (`plane`, `sphere`, `cuboid`, `box_light`, `sphere_light`, `rect_light`). Every emitter must be added with a `*_light` method so light sampling knows about it.
|
Scenes are defined in [`rust/src/scenes.rs`](rust/src/scenes.rs) with a small builder (`plane`, `sphere`, `cuboid`, `box_light`, `sphere_light`, `rect_light`). Every emitter must be added with a `*_light` method so light sampling knows about it.
|
||||||
|
|
||||||
|
|
|
||||||
BIN
rust/city.png
Normal file
BIN
rust/city.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 MiB |
|
|
@ -38,6 +38,11 @@ pub const SCENES: &[SceneInfo] = &[
|
||||||
description: "The classic Cornell box: ceiling light, two boxes, colour bleeding",
|
description: "The classic Cornell box: ceiling light, two boxes, colour bleeding",
|
||||||
build: classic,
|
build: classic,
|
||||||
},
|
},
|
||||||
|
SceneInfo {
|
||||||
|
name: "city",
|
||||||
|
description: "Massive scale: a procedural city of ~125,000 objects under a low sun",
|
||||||
|
build: city,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
pub fn find(name: &str) -> Option<&'static SceneInfo> {
|
pub fn find(name: &str) -> Option<&'static SceneInfo> {
|
||||||
|
|
@ -218,3 +223,104 @@ fn classic() -> Scene {
|
||||||
b.cuboid(Vec3::new(265.0, 0.0, 295.0), Vec3::new(430.0, 330.0, 460.0), white);
|
b.cuboid(Vec3::new(265.0, 0.0, 295.0), Vec3::new(430.0, 330.0, 460.0), white);
|
||||||
b.build()
|
b.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A procedural city on a GRID x GRID lot layout: buildings (with stepped
|
||||||
|
/// tiers on the tall ones) that rise towards downtown, some mirror-glass
|
||||||
|
/// towers, and parks with sphere trees. Lit by a low sun and the sky.
|
||||||
|
fn city() -> Scene {
|
||||||
|
const GRID: usize = 400;
|
||||||
|
const LOT: f64 = 10.0;
|
||||||
|
// Every BLOCK-th row/column of lots is left empty as an avenue.
|
||||||
|
const BLOCK: usize = 5;
|
||||||
|
let extent = GRID as f64 * LOT;
|
||||||
|
let downtown = Vec3::new(0.5 * extent, 0.0, 0.5 * extent);
|
||||||
|
|
||||||
|
let mut b = SceneBuilder::new(
|
||||||
|
View {
|
||||||
|
lookfrom: Vec3::new(0.35 * extent, 120.0, 0.29 * extent),
|
||||||
|
lookat: Vec3::new(0.5 * extent, 0.0, 0.5 * extent),
|
||||||
|
vfov: 42.0,
|
||||||
|
},
|
||||||
|
Background::Sky,
|
||||||
|
);
|
||||||
|
let mut rng = SmallRng::seed_from_u64(2024);
|
||||||
|
|
||||||
|
b.plane(Vec3::ZERO, Vec3::new(0.0, 1.0, 0.0), diffuse(0.22, 0.22, 0.23));
|
||||||
|
|
||||||
|
// The sun: a distant sphere light 1.5 degrees in radius, low and off to
|
||||||
|
// the side of the view so the buildings cast long shadows across it.
|
||||||
|
let sun_dir = Vec3::new(0.85, 0.26, -0.35).normalize();
|
||||||
|
let sun_dist = 50_000.0;
|
||||||
|
b.sphere_light(
|
||||||
|
downtown + sun_dir * sun_dist,
|
||||||
|
sun_dist * 1.5_f64.to_radians().tan(),
|
||||||
|
Vec3::new(1.0, 0.88, 0.72) * 1600.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
let concrete = [
|
||||||
|
Vec3::new(0.72, 0.70, 0.66),
|
||||||
|
Vec3::new(0.56, 0.56, 0.60),
|
||||||
|
Vec3::new(0.76, 0.70, 0.60),
|
||||||
|
Vec3::new(0.46, 0.43, 0.41),
|
||||||
|
Vec3::new(0.82, 0.80, 0.77),
|
||||||
|
Vec3::new(0.62, 0.52, 0.46),
|
||||||
|
];
|
||||||
|
let grass = diffuse(0.18, 0.36, 0.12);
|
||||||
|
|
||||||
|
for gx in 0..GRID {
|
||||||
|
for gz in 0..GRID {
|
||||||
|
if gx % BLOCK == BLOCK - 1 || gz % BLOCK == BLOCK - 1 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let x0 = gx as f64 * LOT;
|
||||||
|
let z0 = gz as f64 * LOT;
|
||||||
|
let lot_center = Vec3::new(x0 + 0.5 * LOT, 0.0, z0 + 0.5 * LOT);
|
||||||
|
let r = (lot_center - downtown).length() / extent;
|
||||||
|
|
||||||
|
if rng.gen::<f64>() < 0.05 {
|
||||||
|
// Park: a lawn with a few round trees.
|
||||||
|
b.cuboid(Vec3::new(x0 + 0.5, 0.0, z0 + 0.5), Vec3::new(x0 + LOT - 0.5, 0.15, z0 + LOT - 0.5), grass);
|
||||||
|
for _ in 0..rng.gen_range(2..6) {
|
||||||
|
let radius = 1.0 + 1.2 * rng.gen::<f64>();
|
||||||
|
let c = Vec3::new(
|
||||||
|
x0 + radius + 0.5 + (LOT - 2.0 * radius - 1.0) * rng.gen::<f64>(),
|
||||||
|
radius + 0.8,
|
||||||
|
z0 + radius + 0.5 + (LOT - 2.0 * radius - 1.0) * rng.gen::<f64>(),
|
||||||
|
);
|
||||||
|
let shade = 0.7 + 0.3 * rng.gen::<f64>();
|
||||||
|
b.sphere(c, radius, diffuse(0.12 * shade, 0.32 * shade, 0.08 * shade));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Height falls off from downtown, with a long tail of towers.
|
||||||
|
let skyline = 7.0 + 95.0 * (-(r * r) / 0.0068).exp();
|
||||||
|
let height = (skyline * (0.35 + 0.9 * rng.gen::<f64>().powi(2))).max(3.0);
|
||||||
|
// Tall buildings fill more of their lot.
|
||||||
|
let inset = if height > 40.0 { 0.4 + 0.6 * rng.gen::<f64>() } else { 0.8 + 1.4 * rng.gen::<f64>() };
|
||||||
|
let (mut lo, mut hi) = (
|
||||||
|
Vec3::new(x0 + inset, 0.0, z0 + inset),
|
||||||
|
Vec3::new(x0 + LOT - inset, height, z0 + LOT - inset),
|
||||||
|
);
|
||||||
|
let material = if height > 45.0 && rng.gen::<f64>() < 0.15 {
|
||||||
|
mirror(0.35, 0.45, 0.55)
|
||||||
|
} else {
|
||||||
|
let c = concrete[rng.gen_range(0..concrete.len())];
|
||||||
|
Material::Diffuse { albedo: c * (0.85 + 0.15 * rng.gen::<f64>()) }
|
||||||
|
};
|
||||||
|
b.cuboid(lo, hi, material);
|
||||||
|
|
||||||
|
// Tall buildings step in with up to three narrower tiers.
|
||||||
|
let mut tiers = if height > 30.0 { rng.gen_range(1..4) } else { 0 };
|
||||||
|
while tiers > 0 && hi.x - lo.x > 2.5 {
|
||||||
|
let step = 0.6 + 0.6 * rng.gen::<f64>();
|
||||||
|
let tier_height = (0.15 + 0.25 * rng.gen::<f64>()) * height;
|
||||||
|
lo = Vec3::new(lo.x + step, hi.y, lo.z + step);
|
||||||
|
hi = Vec3::new(hi.x - step, hi.y + tier_height, hi.z - step);
|
||||||
|
b.cuboid(lo, hi, material);
|
||||||
|
tiers -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.build()
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue