city scene
This commit is contained in:
parent
3209f1cb31
commit
a9b2d17183
2 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 |
|
||||
| `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 |
|
||||
| `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.
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,11 @@ pub const SCENES: &[SceneInfo] = &[
|
|||
description: "The classic Cornell box: ceiling light, two boxes, colour bleeding",
|
||||
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> {
|
||||
|
|
@ -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.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