diff --git a/build/_esbuild-config.mjs b/build/_esbuild-config.mjs
index 791780d..291274d 100644
--- a/build/_esbuild-config.mjs
+++ b/build/_esbuild-config.mjs
@@ -14,10 +14,11 @@ export const get_esbuild_config = (outdir, prod) => {
return ({
entryPoints: {
- index: project_path('src/js/index.ts'),
- render_worker: project_path('src/js/render_worker.ts'),
- coi: project_path('src/js/coi-service-worker.js'),
- styles: project_path('src/js/main.css')
+ styles: project_path('src/js/main.css'),
+ render: project_path('src/js/entry-points/render.ts'),
+ select_scene: project_path('src/js/entry-points/select_scene.ts'),
+ render_worker: project_path('src/js/entry-points/render_worker.ts'),
+ coi: project_path('src/js/coi-service-worker.js')
},
bundle: true,
sourcemap: dev,
diff --git a/build/static-files.mjs b/build/static-files.mjs
index 1ae412b..913d309 100644
--- a/build/static-files.mjs
+++ b/build/static-files.mjs
@@ -29,12 +29,13 @@ export const prepareSingleFile = async (basePath, filePath) => {
export const createPrepareFile = (pathFallbackChain) => async (url) => {
let preparation = null;
- for (const basePath of pathFallbackChain) {
- const filePath = url.endsWith("/")
- ? path.join(basePath, url, 'index.html')
- : path.join(basePath, url);
+ for (const base_file_path of pathFallbackChain) {
+ const url_path = new URL(url, 'http://dummy').pathname;
+ const file_path = url.endsWith("/")
+ ? path.join(base_file_path, url_path, 'index.html')
+ : path.join(base_file_path, url_path);
- preparation = await prepareSingleFile(basePath, filePath);
+ preparation = await prepareSingleFile(base_file_path, file_path);
if (preparation.found) {
return preparation;
diff --git a/src/js/index.ts b/src/js/entry-points/render.ts
similarity index 68%
rename from src/js/index.ts
rename to src/js/entry-points/render.ts
index edce0f9..625c807 100644
--- a/src/js/index.ts
+++ b/src/js/entry-points/render.ts
@@ -1,10 +1,10 @@
-import { create_array_writer, create_canvas_color_writer } from './ui/color-writers';
-import { single_threaded_render } from './single_threaded_render';
-import { multi_threaded_render } from './multi_threaded_render';
-import { generate_random_permutation_u16, generate_straight_order_u16 } from './utils';
-import { ConsoleProgressReporter, MultipleReporters, ProgressBar, ProgressText } from './progress-reporters';
-import { ACES, apply_gamma, clip_to_unit_range, compose_color_flow, expose } from './color-flow';
-import { select_model_ui } from './ui/select_model_ui';
+import { create_array_writer, create_canvas_color_writer } from '../ui/color-writers';
+import { single_threaded_render } from '../single_threaded_render';
+import { multi_threaded_render } from '../multi_threaded_render';
+import { generate_random_permutation_u16, generate_straight_order_u16 } from '../utils';
+import { ConsoleProgressReporter, MultipleReporters, ProgressBar, ProgressText } from '../progress-reporters';
+import { ACES, apply_gamma, clip_to_unit_range, compose_color_flow, expose } from '../color-flow';
+import { scenes } from '../scenes/index';
const aspect_ratio = 1;
const image_width = 840;
@@ -16,7 +16,25 @@ const thread_count = globalThis?.navigator?.hardwareConcurrency
? globalThis?.navigator?.hardwareConcurrency - 1
: 4;
-const scene = await select_model_ui(document.getElementById('top-row') as HTMLDivElement);
+const params = new URLSearchParams(location.search);
+const scene_index_str = params.get('scene');
+if (scene_index_str === null) {
+ throw new Error(`No scene index`);
+}
+const scene_index = parseInt(scene_index_str);
+if (Number.isNaN(scene_index)) {
+ throw new Error(`Bad scene index.`);
+}
+const scene_tuple = scenes[scene_index];
+if (scene_tuple === undefined) {
+ throw new Error(`No such scene index ${scene_index}`);
+}
+
+const [scene_name, _scene_tag, load_scene] = scene_tuple;
+
+document.title = scene_name + ' - Ray tracing in one weekend.'
+
+const scene = await load_scene();
const progress_reporter = new MultipleReporters([
// new ConsoleProgressReporter(image_height, thread_count),
diff --git a/src/js/render_worker.ts b/src/js/entry-points/render_worker.ts
similarity index 89%
rename from src/js/render_worker.ts
rename to src/js/entry-points/render_worker.ts
index 8de4541..71edfa6 100644
--- a/src/js/render_worker.ts
+++ b/src/js/entry-points/render_worker.ts
@@ -1,13 +1,13 @@
-import { add_vec3_r, ArenaVec3Allocator, use_vec3_allocator } from './math/vec3.gen';
-import { ray_color } from './ray_color';
-import { RenderWorkerParametersMessage } from './types';
-import { ArenaQuatAllocator, use_quat_allocator } from './math/quat.gen';
-import { run_with_hooks } from './utils';
-import { configure_camera, get_ray } from './camera';
+import { add_vec3_r, ArenaVec3Allocator, use_vec3_allocator } from '../math/vec3.gen';
+import { ray_color } from '../ray_color';
+import { RenderWorkerParametersMessage } from '../types';
+import { ArenaQuatAllocator, use_quat_allocator } from '../math/quat.gen';
+import { run_with_hooks } from '../utils';
+import { configure_camera, get_ray } from '../camera';
-import './hittable';
-import './materials';
-import './texture';
+import '../hittable';
+import '../materials';
+import '../texture';
export interface RenderWorkerMessageData {
y: number;
diff --git a/src/js/entry-points/select_scene.ts b/src/js/entry-points/select_scene.ts
new file mode 100644
index 0000000..f1500f4
--- /dev/null
+++ b/src/js/entry-points/select_scene.ts
@@ -0,0 +1,21 @@
+import { scenes } from '../scenes/index';
+
+const render_links = (id: string, tag: string) => {
+ const book1_scenes = document.getElementById(id) as HTMLUListElement;
+
+ book1_scenes.innerHTML = scenes
+ .map(([name, tag_name], i) => {
+ if (tag_name === tag) {
+ return `
${name} `;
+ }
+ return null;
+ })
+ .filter(str => str !== null)
+ .join('');
+
+}
+
+render_links('book1_scenes', 'book1');
+render_links('book2_scenes', 'book2');
+render_links('book3_scenes', 'book3');
+render_links('gltf_scenes', 'gltf');
diff --git a/src/js/main.css b/src/js/main.css
index 95a8cda..95905a6 100644
--- a/src/js/main.css
+++ b/src/js/main.css
@@ -16,6 +16,10 @@ body {
gap: 12px;
}
+a {
+ color: #4493f8;
+}
+
#top-row {
flex: 0 0 auto;
padding: 0 12px;
diff --git a/src/js/multi_threaded_render.ts b/src/js/multi_threaded_render.ts
index 87268f8..56e2d98 100644
--- a/src/js/multi_threaded_render.ts
+++ b/src/js/multi_threaded_render.ts
@@ -1,6 +1,6 @@
import { RenderParameters, RenderWorkerParametersMessage } from './types';
import { ColorWriter } from './ui/color-writers';
-import { RenderWorkerMessageData } from './render_worker';
+import { RenderWorkerMessageData } from './entry-points/render_worker';
import { color } from './math/vec3.gen';
import { ProgressReporter } from './progress-reporters';
import { ColorFlowItem } from './color-flow';
diff --git a/src/js/scenes/book-1-final-scene.ts b/src/js/scenes/book-1-final-scene.ts
index 5ca0652..139c80b 100644
--- a/src/js/scenes/book-1-final-scene.ts
+++ b/src/js/scenes/book-1-final-scene.ts
@@ -1,11 +1,16 @@
import {
- add_vec3, ArenaVec3Allocator,
+ add_vec3,
+ ArenaVec3Allocator,
len_vec3,
- point3, rand_vec3, rand_vec3_min_max, sub_vec3, use_vec3_allocator,
+ point3,
+ rand_vec3,
+ rand_vec3_min_max,
+ sub_vec3,
+ use_vec3_allocator,
vec3
} from '../math/vec3.gen';
import { create_checker_3d_texture } from '../texture/checker_3d_texture';
-import { solid_color, create_solid_color } from '../texture/solid_color';
+import { create_solid_color, solid_color } from '../texture/solid_color';
import { random, random_min_max } from '../math/random';
import { create_camera } from '../camera';
import { create_scene, Scene } from './scene';
@@ -73,7 +78,7 @@ function create_lots_of_spheres(): Hittable {
});
}
-export const book1_final_scene = (): Scene => create_scene({
+export const create = (): Scene => create_scene({
camera: create_camera({
look_from: point3(13, 2, 3),
look_at: point3(0, 0, 0),
diff --git a/src/js/scenes/book-2-final-scene.ts b/src/js/scenes/book-2-final-scene.ts
index e19f342..df91f2f 100644
--- a/src/js/scenes/book-2-final-scene.ts
+++ b/src/js/scenes/book-2-final-scene.ts
@@ -1,12 +1,5 @@
import { create_scene, Scene } from './scene';
-import {
- add_vec3,
- ArenaVec3Allocator,
- point3,
- rand_vec3_min_max,
- use_vec3_allocator,
- vec3
-} from '../math/vec3.gen';
+import { add_vec3, ArenaVec3Allocator, point3, rand_vec3_min_max, use_vec3_allocator, vec3 } from '../math/vec3.gen';
import { solid_color } from '../texture/solid_color';
import { random_min_max } from '../math/random';
import earthUrl from './earthmap.jpg';
@@ -34,7 +27,7 @@ import { create_bvh_node } from '../hittable/bvh';
import { create_image_texture } from '../texture/image_texture';
import { create_noise_texture } from '../texture/noise_texture';
-export const book2_final_scene = async (): Promise => {
+export const create = async (): Promise => {
return async_run_with_hooks(async (): Promise => {
use_vec3_allocator(new ArenaVec3Allocator(1024 * 6));
@@ -47,23 +40,23 @@ export const book2_final_scene = async (): Promise => {
const y0 = 0.0;
const boxes1 = create_zx_grid(boxes_per_side, boxes_per_side, 101, w, point3(-1000, 0, -1000));
for (let i = 0; i < boxes_per_side; i++) {
- const x0 = -1000.0 + i*w;
+ const x0 = -1000.0 + i * w;
const x1 = x0 + w;
for (let j = 0; j < boxes_per_side; j++) {
- const z0 = -1000.0 + j*w;
- const y1 = random_min_max(1,101);
+ const z0 = -1000.0 + j * w;
+ const y1 = random_min_max(1, 101);
const z1 = z0 + w;
- zx_grid_add_hittable(boxes1, i, j, create_box(point3(x0,y0,z0), point3(x1,y1,z1), ground));
+ zx_grid_add_hittable(boxes1, i, j, create_box(point3(x0, y0, z0), point3(x1, y1, z1), ground));
}
}
objects.objects.push(boxes1);
const light = create_diffuse_light(solid_color(7, 7, 7));
- const light_hittable = create_quad(point3(123, 554, 147), vec3(300,0,0), vec3(0,0,265), light);
+ const light_hittable = create_quad(point3(123, 554, 147), vec3(300, 0, 0), vec3(0, 0, 265), light);
objects.objects.push(light_hittable);
const center1 = point3(400, 400, 200);
- const center2 = add_vec3(center1, vec3(30,0,0));
+ const center2 = add_vec3(center1, vec3(30, 0, 0));
const moving_sphere_material = create_lambertian(solid_color(0.7, 0.3, 0.1));
objects.objects.push(create_moving_sphere(center1, center2, 0, 1, 50, moving_sphere_material));
@@ -73,16 +66,19 @@ export const book2_final_scene = async (): Promise => {
point3(0, 150, 145), 50, create_metal(solid_color(0.8, 0.8, 0.9), 1.0)
));
- const subsurface_scattering_sphere = create_sphere(point3(360,150,145), 70, create_dielectric(1.5));
+ const subsurface_scattering_sphere = create_sphere(point3(360, 150, 145), 70, create_dielectric(1.5));
objects.objects.push(subsurface_scattering_sphere);
objects.objects.push(create_constant_medium(subsurface_scattering_sphere, 0.2, create_isotropic_phase_function(solid_color(0.2, 0.4, 0.9))));
const fog_boundary = create_sphere(point3(0, 0, 0), 5000, create_dielectric(1.5));
objects.objects.push(create_constant_medium(fog_boundary, .0001, create_isotropic_phase_function(solid_color(1, 1, 1))));
- const emat = create_lambertian(create_image_texture(await load_dom_image(earthUrl), {flip_y: true, decode_srgb: true}));
- objects.objects.push(create_sphere(point3(400,200,400), 100, emat));
+ const emat = create_lambertian(create_image_texture(await load_dom_image(earthUrl), {
+ flip_y: true,
+ decode_srgb: true
+ }));
+ objects.objects.push(create_sphere(point3(400, 200, 400), 100, emat));
const pertext = create_noise_texture(0.2);
- objects.objects.push(create_sphere(point3(220,280,300), 80, create_lambertian(pertext)));
+ objects.objects.push(create_sphere(point3(220, 280, 300), 80, create_lambertian(pertext)));
const spheres: Hittable[] = [];
const white = create_lambertian(solid_color(.73, .73, .73));
@@ -93,7 +89,7 @@ export const book2_final_scene = async (): Promise => {
objects.objects.push(create_transform(
trs_to_mat3x4(
- vec3(-100,270,395),
+ vec3(-100, 270, 395),
axis_angle_to_quat(vec3(0, 1, 0), degrees_to_radians(15)),
vec3(1, 1, 1)
),
diff --git a/src/js/scenes/cornell_box.ts b/src/js/scenes/cornell_box.ts
index 1fe26db..8417168 100644
--- a/src/js/scenes/cornell_box.ts
+++ b/src/js/scenes/cornell_box.ts
@@ -16,70 +16,72 @@ import { create_box } from '../hittable/box';
import { create_sphere } from '../hittable/sphere';
import { create_hittable_list } from '../hittable/hittable_list';
-const hittables = run_with_hooks(() => {
- use_vec3_allocator(new ArenaVec3Allocator(128));
+export const create = (): Scene => {
+ const hittables = run_with_hooks(() => {
+ use_vec3_allocator(new ArenaVec3Allocator(128));
- const aluminum = create_metal(solid_color(0.8, 0.85, 0.88), 0);
- const glass = create_dielectric(1.5);
- const red = create_lambertian(solid_color(.65, .05, .05));
- const white = create_lambertian(solid_color(.73, .73, .73));
- const green = create_lambertian(solid_color(.12, .45, .15));
- const light = create_diffuse_light(solid_color(15, 15, 15));
+ const aluminum = create_metal(solid_color(0.8, 0.85, 0.88), 0);
+ const glass = create_dielectric(1.5);
+ const red = create_lambertian(solid_color(.65, .05, .05));
+ const white = create_lambertian(solid_color(.73, .73, .73));
+ const green = create_lambertian(solid_color(.12, .45, .15));
+ const light = create_diffuse_light(solid_color(15, 15, 15));
- const light_hittable = create_quad(point3(343, 554, 332), vec3(-130,0,0), vec3(0,0,-105), light);
+ const light_hittable = create_quad(point3(343, 554, 332), vec3(-130,0,0), vec3(0,0,-105), light);
- const metal_box = create_transform(
- trs_to_mat3x4(
- vec3(265, 0, 295),
- axis_angle_to_quat(vec3(0, 1, 0), degrees_to_radians(15)),
- vec3(1, 1, 1)
- ),
- create_box(point3(0, 0, 0), point3(165, 330, 165), aluminum)
- );
+ const metal_box = create_transform(
+ trs_to_mat3x4(
+ vec3(265, 0, 295),
+ axis_angle_to_quat(vec3(0, 1, 0), degrees_to_radians(15)),
+ vec3(1, 1, 1)
+ ),
+ create_box(point3(0, 0, 0), point3(165, 330, 165), aluminum)
+ );
- const glass_sphere = create_sphere(point3(190,90,190), 90, glass);
- const root = create_hittable_list([
- create_quad(point3(555, 0, 0), vec3(0, 555, 0), vec3(0, 0, 555), green),
- create_quad(point3(0, 0, 0), vec3(0, 555, 0), vec3(0, 0, 555), red),
- light_hittable,
- create_quad(point3(0, 555, 0), vec3(555, 0, 0), vec3(0, 0, 555), white),
- create_quad(point3(0, 0, 0), vec3(555, 0, 0), vec3(0, 0, 555), white),
- create_quad(point3(0, 0, 555), vec3(555, 0, 0), vec3(0, 555, 0), white),
-
- metal_box,
- glass_sphere,
- // create_transform(
- // trs_to_mat3x4(
- // vec3(130, 0, 65),
- // axis_angle_to_quat(vec3(0, 1, 0), degrees_to_radians(-18)),
- // vec3(1, 1 ,1)
- // ),
- // create_box(point3(0, 0, 0), point3(165, 165, 165), white)
- // )
- ]);
-
- return {
- root,
- light: create_hittable_list([
+ const glass_sphere = create_sphere(point3(190,90,190), 90, glass);
+ const root = create_hittable_list([
+ create_quad(point3(555, 0, 0), vec3(0, 555, 0), vec3(0, 0, 555), green),
+ create_quad(point3(0, 0, 0), vec3(0, 555, 0), vec3(0, 0, 555), red),
light_hittable,
+ create_quad(point3(0, 555, 0), vec3(555, 0, 0), vec3(0, 0, 555), white),
+ create_quad(point3(0, 0, 0), vec3(555, 0, 0), vec3(0, 0, 555), white),
+ create_quad(point3(0, 0, 555), vec3(555, 0, 0), vec3(0, 555, 0), white),
+
+ metal_box,
glass_sphere,
- metal_box
- ])
- };
-});
+ // create_transform(
+ // trs_to_mat3x4(
+ // vec3(130, 0, 65),
+ // axis_angle_to_quat(vec3(0, 1, 0), degrees_to_radians(-18)),
+ // vec3(1, 1 ,1)
+ // ),
+ // create_box(point3(0, 0, 0), point3(165, 165, 165), white)
+ // )
+ ]);
+
+ return {
+ root,
+ light: create_hittable_list([
+ light_hittable,
+ glass_sphere,
+ metal_box
+ ])
+ };
+ });
-export const cornell_box: Scene = create_scene({
- root_hittable: hittables.root,
- light: hittables.light,
- camera: create_camera({
- look_from: point3(278, 278, -800),
- look_at: point3(278, 278, 0),
- v_up: vec3(0, 1, 0),
- focus_dist: 10,
- aperture: 0,
- y_fov: 40,
- time0: 0,
- time1: 1
- }),
- background: Skybox.create_black()
-});
+ return create_scene({
+ root_hittable: hittables.root,
+ light: hittables.light,
+ camera: create_camera({
+ look_from: point3(278, 278, -800),
+ look_at: point3(278, 278, 0),
+ v_up: vec3(0, 1, 0),
+ focus_dist: 10,
+ aperture: 0,
+ y_fov: 40,
+ time0: 0,
+ time1: 1
+ }),
+ background: Skybox.create_black()
+ });
+};
diff --git a/src/js/scenes/cornell_box_with_smoke.ts b/src/js/scenes/cornell_box_with_smoke.ts
index e3fe0e9..e3ab1dd 100644
--- a/src/js/scenes/cornell_box_with_smoke.ts
+++ b/src/js/scenes/cornell_box_with_smoke.ts
@@ -1,4 +1,4 @@
-import { create_scene, Scene } from './scene';
+import { create_scene } from './scene';
import { create_camera } from '../camera';
import { solid_color } from '../texture/solid_color';
import { ArenaVec3Allocator, point3, use_vec3_allocator, vec3 } from '../math/vec3.gen';
@@ -15,63 +15,65 @@ import { create_constant_medium } from '../hittable/constant_medium';
import { create_transform } from '../hittable/transform';
import { create_box } from '../hittable/box';
-const red = create_lambertian(solid_color(.65, .05, .05));
-const white = create_lambertian(solid_color(.73, .73, .73));
-const green = create_lambertian(solid_color(.12, .45, .15));
-const light = create_diffuse_light(solid_color(7, 7, 7));
+export const create = () => {
+ const red = create_lambertian(solid_color(.65, .05, .05));
+ const white = create_lambertian(solid_color(.73, .73, .73));
+ const green = create_lambertian(solid_color(.12, .45, .15));
+ const light = create_diffuse_light(solid_color(7, 7, 7));
-const light_hittable = create_quad(point3(113,554,127), vec3(330,0,0), vec3(0,0,305), light);
+ const light_hittable = create_quad(point3(113,554,127), vec3(330,0,0), vec3(0,0,305), light);
-export const cornell_box_with_smoke: Scene = run_with_hooks(() => {
- use_vec3_allocator(new ArenaVec3Allocator(1024));
- return create_scene({
- root_hittable: create_bvh_node([
- create_quad(point3(555,0,0), vec3(0,555,0), vec3(0,0,555), green),
- create_quad(point3(0,0,0), vec3(0,555,0), vec3(0,0,555), red),
- light_hittable,
- create_quad(point3(0,555,0), vec3(555,0,0), vec3(0,0,555), white),
- create_quad(point3(0,0,0), vec3(555,0,0), vec3(0,0,555), white),
- create_quad(point3(0,0,555), vec3(555,0,0), vec3(0,555,0), white),
+ return run_with_hooks(() => {
+ use_vec3_allocator(new ArenaVec3Allocator(1024));
+ return create_scene({
+ root_hittable: create_bvh_node([
+ create_quad(point3(555, 0, 0), vec3(0, 555, 0), vec3(0, 0, 555), green),
+ create_quad(point3(0, 0, 0), vec3(0, 555, 0), vec3(0, 0, 555), red),
+ light_hittable,
+ create_quad(point3(0, 555, 0), vec3(555, 0, 0), vec3(0, 0, 555), white),
+ create_quad(point3(0, 0, 0), vec3(555, 0, 0), vec3(0, 0, 555), white),
+ create_quad(point3(0, 0, 555), vec3(555, 0, 0), vec3(0, 555, 0), white),
- create_constant_medium(
- create_transform(
- trs_to_mat3x4(
- vec3(265, 0, 295),
- axis_angle_to_quat(vec3(0, 1, 0), degrees_to_radians(15)),
- vec3(1, 1, 1)
+ create_constant_medium(
+ create_transform(
+ trs_to_mat3x4(
+ vec3(265, 0, 295),
+ axis_angle_to_quat(vec3(0, 1, 0), degrees_to_radians(15)),
+ vec3(1, 1, 1)
+ ),
+ create_box(point3(0, 0, 0), point3(165, 330, 165), white),
),
- create_box(point3(0, 0, 0), point3(165, 330, 165), white),
+ 0.01,
+ create_isotropic_phase_function(solid_color(0, 0, 0))
),
- 0.01,
- create_isotropic_phase_function(solid_color(0, 0, 0))
- ),
- create_constant_medium(
- create_transform(
- trs_to_mat3x4(
- vec3(130, 0, 65),
- axis_angle_to_quat(vec3(0, 1, 0), degrees_to_radians(-18)),
- vec3(1, 1, 1)
+ create_constant_medium(
+ create_transform(
+ trs_to_mat3x4(
+ vec3(130, 0, 65),
+ axis_angle_to_quat(vec3(0, 1, 0), degrees_to_radians(-18)),
+ vec3(1, 1, 1)
+ ),
+ create_box(point3(0, 0, 0), point3(165, 165, 165), white)
),
- create_box(point3(0, 0, 0), point3(165, 165, 165), white)
- ),
- 0.01,
- create_isotropic_phase_function(solid_color(1, 1, 1))
- )
- ], 0, 1),
+ 0.01,
+ create_isotropic_phase_function(solid_color(1, 1, 1))
+ )
+ ], 0, 1),
- light: light_hittable,
+ light: light_hittable,
- camera: create_camera({
- look_from: point3(278, 278, -800),
- look_at: point3(278, 278, 0),
- v_up: vec3(0, 1, 0),
- focus_dist: 10,
- aperture: 0,
- y_fov: 40,
- time0: 0,
- time1: 1
- }),
- background: Skybox.create_black()
+ camera: create_camera({
+ look_from: point3(278, 278, -800),
+ look_at: point3(278, 278, 0),
+ v_up: vec3(0, 1, 0),
+ focus_dist: 10,
+ aperture: 0,
+ y_fov: 40,
+ time0: 0,
+ time1: 1
+ }),
+ background: Skybox.create_black()
+ });
});
-});
+};
diff --git a/src/js/scenes/damaged_helmet_gltf.ts b/src/js/scenes/damaged_helmet_gltf.ts
index a9cb408..b123944 100644
--- a/src/js/scenes/damaged_helmet_gltf.ts
+++ b/src/js/scenes/damaged_helmet_gltf.ts
@@ -5,7 +5,7 @@ import { create_camera } from '../camera';
import { load_rgbe } from '../texture/image-parsers/rgbe_image_parser';
import { Skybox } from '../hittable/skybox';
-export const load_damaged_helmet_gltf = async (): Promise => {
+export const create = async (): Promise => {
const scene = await load_gltf('gltf/DamagedHelmet/glTF/DamagedHelmet.gltf', 1024000, 1024, 25);
const env = await load_rgbe(2000, 'hdr/Cannon_Exterior.hdr');
const skybox = Skybox.create_hdr(env);
@@ -26,9 +26,9 @@ export const load_damaged_helmet_gltf = async (): Promise => {
background: skybox,
exposure_config: {
aperture: 16,
- shutter_speed: 1/25,
+ shutter_speed: 1 / 25,
ISO: 100,
exp_comp: 0
}
});
-}
+};
diff --git a/src/js/scenes/earth.ts b/src/js/scenes/earth.ts
index 2251311..03575bf 100644
--- a/src/js/scenes/earth.ts
+++ b/src/js/scenes/earth.ts
@@ -8,10 +8,13 @@ import { load_dom_image } from '../texture/image-parsers/image-bitmap';
import { create_image_texture } from '../texture/image_texture';
import { create_sphere } from '../hittable/sphere';
-export const create_earth_scene = async (): Promise => {
+export const create = async (): Promise => {
const earth_image = await load_dom_image(earthUrl);
return create_scene({
- root_hittable: create_sphere(point3(0, 0, 0), 2, create_lambertian(create_image_texture(earth_image, {flip_y: true, decode_srgb: true}))),
+ root_hittable: create_sphere(point3(0, 0, 0), 2, create_lambertian(create_image_texture(earth_image, {
+ flip_y: true,
+ decode_srgb: true
+ }))),
camera: create_camera({
look_from: point3(13, 2, 3),
look_at: point3(0, 0, 0),
@@ -24,4 +27,4 @@ export const create_earth_scene = async (): Promise => {
}),
background: Skybox.create_solid(0.7, 0.8, 1.0)
});
-}
+};
diff --git a/src/js/scenes/index.ts b/src/js/scenes/index.ts
new file mode 100644
index 0000000..e1f2efd
--- /dev/null
+++ b/src/js/scenes/index.ts
@@ -0,0 +1,13 @@
+import { Scene } from './scene';
+
+export const scenes: [string, string, () => Promise][] = [
+ ['Two spheres', 'book1', () => import('../scenes/two_spheres').then(({create}) => create())],
+ ['Book 1. Final scene', 'book1', () => import('../scenes/book-1-final-scene').then(({create}) => create())],
+ ['Earth texture', 'book2', () => import('../scenes/earth').then(({create}) => create())],
+ ['Simple light', 'book2', () => import('../scenes/simple_light').then(({create}) => create())],
+ ['Book 2. Final scene', 'book2', () => import('../scenes/book-2-final-scene').then(({create}) => create())],
+ ['Cornell box', 'book3', () => import('../scenes/cornell_box').then(({create}) => create())],
+ ['Cornel box with smoke', 'book3', () => import('../scenes/cornell_box_with_smoke').then(({create}) => create())],
+ ['simple.gltf', 'gltf', () => import('../scenes/simple_gltf').then(({create}) => create())],
+ ['DamagedHelmet.gltf', 'gltf', () => import('../scenes/damaged_helmet_gltf').then(({create}) => create())],
+];
diff --git a/src/js/scenes/simple_gltf.ts b/src/js/scenes/simple_gltf.ts
index 8ca7861..f7b51d6 100644
--- a/src/js/scenes/simple_gltf.ts
+++ b/src/js/scenes/simple_gltf.ts
@@ -5,7 +5,7 @@ import { create_camera } from '../camera';
import { load_rgbe } from '../texture/image-parsers/rgbe_image_parser';
import { Skybox } from '../hittable/skybox';
-export const load_simple_gltf = async (): Promise => {
+export const create = async (): Promise => {
const scene = await load_gltf('gltf/simple/model.gltf', 102400, 1024);
const env = await load_rgbe(2048, 'hdr/street.hdr');
const skybox = Skybox.create_hdr(env);
@@ -26,9 +26,9 @@ export const load_simple_gltf = async (): Promise => {
background: skybox,
exposure_config: {
aperture: 16,
- shutter_speed: 1/10,
+ shutter_speed: 1 / 10,
ISO: 400,
exp_comp: 0
}
});
-}
+};
diff --git a/src/js/scenes/simple_light.ts b/src/js/scenes/simple_light.ts
index 752f88c..ec0dafd 100644
--- a/src/js/scenes/simple_light.ts
+++ b/src/js/scenes/simple_light.ts
@@ -10,30 +10,33 @@ import { create_quad } from '../hittable/quad';
import { create_sphere } from '../hittable/sphere';
import { create_hittable_list } from '../hittable/hittable_list';
-const perlin_texture = create_noise_texture(4);
-const light1 = create_quad(point3(3,1,-2), vec3(2,0,0), vec3(0,2,0), create_diffuse_light(solid_color(0.5, 4, 1)));
-const light2 = create_sphere(point3(0, 7, 0), 2, create_diffuse_light(solid_color(4, 1, 0.5)));
-export const simple_light: Scene = create_scene({
- root_hittable: create_hittable_list([
- create_sphere(point3(0, -1000, 0), 1000, create_lambertian(perlin_texture)),
- create_sphere(point3(0, 2, 0), 2, create_lambertian(perlin_texture)),
- light1,
- light2
- ]),
- light: create_hittable_list([
- light1,
- light2
- ]),
- camera: create_camera({
- look_from: point3(26, 3, 6),
- look_at: point3(0, 2, 0),
- v_up: vec3(0, 1, 0),
- focus_dist: 10,
- aperture: 0,
- y_fov: 20,
- time0: 0,
- time1: 1
- }),
- background: Skybox.create_black()
-});
+export const create = (): Scene => {
+ const perlin_texture = create_noise_texture(4);
+
+ const light1 = create_quad(point3(3,1,-2), vec3(2,0,0), vec3(0,2,0), create_diffuse_light(solid_color(0.5, 4, 1)));
+ const light2 = create_sphere(point3(0, 7, 0), 2, create_diffuse_light(solid_color(4, 1, 0.5)));
+ return create_scene({
+ root_hittable: create_hittable_list([
+ create_sphere(point3(0, -1000, 0), 1000, create_lambertian(perlin_texture)),
+ create_sphere(point3(0, 2, 0), 2, create_lambertian(perlin_texture)),
+ light1,
+ light2
+ ]),
+ light: create_hittable_list([
+ light1,
+ light2
+ ]),
+ camera: create_camera({
+ look_from: point3(26, 3, 6),
+ look_at: point3(0, 2, 0),
+ v_up: vec3(0, 1, 0),
+ focus_dist: 10,
+ aperture: 0,
+ y_fov: 20,
+ time0: 0,
+ time1: 1
+ }),
+ background: Skybox.create_black()
+ });
+};
diff --git a/src/js/scenes/two_perlin_spheres.ts b/src/js/scenes/two_perlin_spheres.ts
index b816ebc..8f57908 100644
--- a/src/js/scenes/two_perlin_spheres.ts
+++ b/src/js/scenes/two_perlin_spheres.ts
@@ -1,29 +1,31 @@
import { create_noise_texture } from '../texture/noise_texture';
import { point3, vec3 } from '../math/vec3.gen';
-import { create_scene, Scene } from './scene';
+import { create_scene } from './scene';
import { create_camera } from '../camera';
import { create_lambertian } from '../materials/lambertian';
import { Skybox } from '../hittable/skybox';
import { create_hittable_list } from '../hittable/hittable_list';
import { create_sphere } from '../hittable/sphere';
-const pertext = create_noise_texture(4);
+export const create = () => {
+ const pertext = create_noise_texture(4);
-export const two_perlin_spheres: Scene = create_scene({
- root_hittable: create_hittable_list([
- create_sphere(point3(0, -1000, 0), 1000, create_lambertian(pertext)),
- create_sphere(point3(0, 2, 0), 2, create_lambertian(pertext))
- ]),
+ return create_scene({
+ root_hittable: create_hittable_list([
+ create_sphere(point3(0, -1000, 0), 1000, create_lambertian(pertext)),
+ create_sphere(point3(0, 2, 0), 2, create_lambertian(pertext))
+ ]),
- camera: create_camera({
- look_from: point3(13, 2, 3),
- look_at: point3(0, 0, 0),
- v_up: vec3(0, 1, 0),
- focus_dist: 10,
- aperture: 0,
- y_fov: 20,
- time0: 0,
- time1: 1
- }),
- background: Skybox.create_solid(0.7, 0.8, 1.0)
-});
+ camera: create_camera({
+ look_from: point3(13, 2, 3),
+ look_at: point3(0, 0, 0),
+ v_up: vec3(0, 1, 0),
+ focus_dist: 10,
+ aperture: 0,
+ y_fov: 20,
+ time0: 0,
+ time1: 1
+ }),
+ background: Skybox.create_solid(0.7, 0.8, 1.0)
+ });
+};
diff --git a/src/js/scenes/two_spheres.ts b/src/js/scenes/two_spheres.ts
index f005828..e60b78a 100644
--- a/src/js/scenes/two_spheres.ts
+++ b/src/js/scenes/two_spheres.ts
@@ -17,7 +17,7 @@ const create_two_spheres = (): Hittable => {
]);
};
-export const two_spheres: Scene = create_scene({
+export const create = (): Scene => create_scene({
root_hittable: create_two_spheres(),
camera: create_camera({
diff --git a/src/js/ui/select_model_ui.ts b/src/js/ui/select_model_ui.ts
deleted file mode 100644
index a8f1280..0000000
--- a/src/js/ui/select_model_ui.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-import { Scene } from '../scenes/scene';
-
-export const select_model_ui = async (container: HTMLDivElement): Promise => {
-
- const scenes: [string, () => Promise][] = [
- ['Two spheres', async () => {
- const {two_spheres} = await import('../scenes/two_spheres');
- return two_spheres;
- }],
- ['Earth texture', async () => {
- const {create_earth_scene} = await import('../scenes/earth');
- return await create_earth_scene();
- }],
- ['Book 1. Final scene', async () => {
- const {book1_final_scene} = await import('../scenes/book-1-final-scene');
- return book1_final_scene();
- }],
- ['Simple light', async () => {
- const {simple_light} = await import('../scenes/simple_light');
- return simple_light;
- }],
- ['Cornell box', async () => {
- const {cornell_box} = await import('../scenes/cornell_box');
- return cornell_box;
- }],
- ['Cornel box with smoke', async () => {
- const {cornell_box_with_smoke} = await import('../scenes/cornell_box_with_smoke');
- return cornell_box_with_smoke;
- }],
- ['Book 2. Final scene', async () => {
- const {book2_final_scene} = await import('../scenes/book-2-final-scene');
- return await book2_final_scene();
- }],
- ['simple.gltf', async () => {
- const {load_simple_gltf} = await import('../scenes/simple_gltf');
- return await load_simple_gltf();
- }],
- ['DamagedHelmet.gltf', async () => {
- const {load_damaged_helmet_gltf} = await import('../scenes/damaged_helmet_gltf');
- return await load_damaged_helmet_gltf();
- }]
- ];
-
- try {
- const ui = document.createElement('div');
- ui.innerHTML = [
- '',
- scenes.map(([name], i) => {
- return `${name} `;
- }).join('\n'),
- '
',
- ].join('\n');
-
- container.appendChild(ui);
-
- const select = document.getElementById('select-model-ui__select') as HTMLSelectElement;
- const load_button = document.getElementById('select-model-ui__load-button') as HTMLButtonElement;
-
-
- const model_index = await new Promise((resolve) => {
- for (let i = 0; i < scenes.length; i++) {
- const btn = document.getElementById(`selection_${i}`) as HTMLButtonElement;
- btn.onclick = () => resolve(i);
- }
- });
-
- container.removeChild(ui);
-
- const model_loader = scenes[model_index][1];
-
- if (model_loader === undefined) {
- throw new Error(`Unknown scene ${select.value}`);
- }
-
- return model_loader();
- }
- catch {
- alert('Failed to load model, reloading the page...');
- window.location.reload();
- }
-}
diff --git a/src/public/index.html b/src/public/index.html
index d2bf7b1..88ed2b0 100644
--- a/src/public/index.html
+++ b/src/public/index.html
@@ -3,14 +3,26 @@
Ray tracing in one weekend
-
-
-
-
-
+
+Ray tracing in one weekend demos
+Book 1
+
+
+Book 2
+
+
+Book 3
+
+
+Gltf
+
+
+
+
diff --git a/src/public/renderer.html b/src/public/renderer.html
new file mode 100644
index 0000000..2ca09e6
--- /dev/null
+++ b/src/public/renderer.html
@@ -0,0 +1,16 @@
+
+
+ Ray tracing in one weekend
+
+
+
+
+
+
+
+
+
+
diff --git a/todo.md b/todo.md
index b0253be..51ac12b 100644
--- a/todo.md
+++ b/todo.md
@@ -1,8 +1,8 @@
- **bug** Sometimes according to normal-map or/and vertex normals, combined with micro-facets distribution, reflected ray must go below the surface. This is currently rendered as black. Should probably do something smarter in that case.
+- **bug** with new scene selection phase everything is slow
- matrix-based camera
- hitting bvh with triangles is too slow. Need more efficient in-memory structure after loading gltf. This may also help with messy UV/vertex-normals related code.
- pdf mixer with explicit weights. When using image based importance sampling with PBR material, specular/diffuse rays will get only quarter priority instead of one third. More general mixer may fix that.
-- separate page for selecting a scene
- configuration UI
- schedule thread load more evenly. Think something like Masonry layout, but for threads and ray counts
- glTF