@@ -50,18 +50,7 @@ sphere2 = Tesselation(Sphere(Point3f(2, -1.5 + 0.6, 1), 0.6f0), 64)
5050# Build our BVH acceleration structure
5151scene_geometry = [cat_mesh, floor, back_wall, left_wall, sphere1, sphere2]
5252bvh = Raycore. BVH (scene_geometry)
53- f, ax, pl = plot (bvh; axis= (; show_axis= false ))
54- ```
55- Set the camera to something better:
56-
57- ``` julia (editor=true, logging=false, output=true)
58- cam = cameracontrols (ax. scene)
59- cam. eyeposition[] = [0 , 1.0 , - 4 ]
60- cam. lookat[] = [0 , 0 , 2 ]
61- cam. upvector[] = [0.0 , 1 , 0.0 ]
62- cam. fov[] = 45.0
63- update_cam! (ax. scene, cam)
64- nothing
53+ md " **BVH built with $(length(bvh.primitives)) triangles**"
6554```
6655## Part 2: Helper Functions - Building Blocks
6756
@@ -212,7 +201,11 @@ trace((args...)-> shadow_kernel(args...; shadow_samples=8), bvh, samples=8)
212201
213202## Part 7: Materials and Multiple Lights
214203
215- Time to add color and multiple lights:
204+ Time to add color and multiple lights! To associate materials with geometry, we need to rebuild the BVH with ** metadata** that links each triangle to its material.
205+
206+ ### Triangle Metadata
207+
208+ When building a BVH, you can pass a ` metadata_fn(mesh_idx, tri_idx) ` that assigns custom data to each triangle. This metadata is stored in ` triangle.metadata ` and returned with every ray hit. For materials, we use the mesh index as metadata:
216209
217210``` julia (editor=true, logging=false, output=true)
218211struct PointLight
@@ -233,25 +226,36 @@ struct RenderContext
233226 ambient:: Float32
234227end
235228
236- # Create lights and materials
229+ # Define materials for each mesh (order matches scene_geometry)
230+ materials = [
231+ Material (RGB (0.8f0 , 0.6f0 , 0.4f0 ), 0.0f0 , 0.8f0 ), # 1: cat
232+ Material (RGB (0.3f0 , 0.5f0 , 0.3f0 ), 0.0f0 , 0.9f0 ), # 2: floor
233+ Material (RGB (0.8f0 , 0.6f0 , 0.5f0 ), 0.8f0 , 0.05f0 ), # 3: back wall
234+ Material (RGB (0.7f0 , 0.7f0 , 0.8f0 ), 0.0f0 , 0.8f0 ), # 4: left wall
235+ Material (RGB (0.9f0 , 0.9f0 , 0.9f0 ), 0.8f0 , 0.02f0 ), # 5: sphere1 - metallic
236+ Material (RGB (0.3f0 , 0.6f0 , 0.9f0 ), 0.5f0 , 0.3f0 ), # 6: sphere2 - semi-metallic
237+ ]
238+
239+ # Rebuild BVH with material indices as metadata
240+ # The metadata_fn receives (mesh_idx, tri_idx) and returns data stored per-triangle
241+ bvh = Raycore. BVH (scene_geometry, (mesh_idx, tri_idx) -> UInt32 (mesh_idx))
242+
243+ # Create lights
237244lights = [
238245 PointLight (Point3f (3 , 4 , - 2 ), 50.0f0 , RGB (1.0f0 , 0.9f0 , 0.8f0 )),
239246 PointLight (Point3f (- 3 , 2 , 0 ), 20.0f0 , RGB (0.7f0 , 0.8f0 , 1.0f0 )),
240247 PointLight (Point3f (0 , 5 , 5 ), 15.0f0 , RGB (1.0f0 , 1.0f0 , 1.0f0 ))
241248]
242249
243- materials = [
244- Material (RGB (0.8f0 , 0.6f0 , 0.4f0 ), 0.0f0 , 0.8f0 ), # cat
245- Material (RGB (0.3f0 , 0.5f0 , 0.3f0 ), 0.0f0 , 0.9f0 ), # floor
246- Material (RGB (0.8f0 , 0.6f0 , 0.5f0 ), 0.8f0 , 0.05f0 ), # back wall
247- Material (RGB (0.7f0 , 0.7f0 , 0.8f0 ), 0.0f0 , 0.8f0 ), # left wall
248- Material (RGB (0.9f0 , 0.9f0 , 0.9f0 ), 0.8f0 , 0.02f0 ), # sphere1 - metallic
249- Material (RGB (0.3f0 , 0.6f0 , 0.9f0 ), 0.5f0 , 0.3f0 ), # sphere2 - semi-metallic
250- ]
251-
252250ctx = RenderContext (lights, materials, 0.1f0 )
253251nothing
254252```
253+ Now when a ray hits a triangle, we can look up its material using ` triangle.metadata ` :
254+
255+ ``` julia
256+ mat = ctx. materials[triangle. metadata] # Get material for hit triangle
257+ ```
258+
255259``` julia (editor=true, logging=false, output=true)
256260# Compute lighting from all lights - reusing our compute_light function!
257261function compute_multi_light (bvh, ctx, point, normal, mat; shadow_samples= 1 )
269273function material_kernel (bvh, ctx, tri, dist, bary, ray)
270274 hit_point = ray. o + ray. d * dist
271275 normal = compute_normal (tri, bary)
272- mat = ctx. materials[tri. material_idx]
276+ # Look up material using triangle's metadata (mesh index we stored during BVH construction)
277+ mat = ctx. materials[tri. metadata]
273278
274279 color = compute_multi_light (bvh, ctx, hit_point, normal, mat, shadow_samples= 2 )
275280 return to_rgb (color)
@@ -287,7 +292,7 @@ Add simple reflections for metallic surfaces:
287292function reflective_kernel (bvh, ctx, tri, dist, bary, ray, sky_color)
288293 hit_point = ray. o + ray. d * dist
289294 normal = compute_normal (tri, bary)
290- mat = ctx. materials[tri. material_idx ]
295+ mat = ctx. materials[tri. metadata ]
291296
292297 # Direct lighting with soft shadows
293298 direct_color = compute_multi_light (bvh, ctx, hit_point, normal, mat, shadow_samples= 8 )
@@ -310,7 +315,7 @@ function reflective_kernel(bvh, ctx, tri, dist, bary, ray, sky_color)
310315 reflection_color = if refl_hit
311316 refl_point = reflect_ray. o + reflect_ray. d * refl_dist
312317 refl_normal = compute_normal (refl_tri, refl_bary)
313- refl_mat = ctx. materials[refl_tri. material_idx ]
318+ refl_mat = ctx. materials[refl_tri. metadata ]
314319 compute_multi_light (bvh, ctx, refl_point, refl_normal, refl_mat, shadow_samples= 1 )
315320 else
316321 to_vec3f (sky_color)
@@ -387,16 +392,30 @@ We built a complete ray tracer with:
387392
388393** Key Raycore Functions:**
389394
390- * ` Raycore.BVH(meshes) ` - Build acceleration structure
395+ * ` Raycore.BVH(meshes) ` - Build acceleration structure (default metadata = primitive index)
396+ * ` Raycore.BVH(meshes, metadata_fn) ` - Build with custom per-triangle metadata
391397 * ` Raycore.Ray(o=origin, d=direction) ` - Create ray
392- * ` Raycore.closest_hit(bvh, ray) ` - Find nearest intersection
393- * ` Raycore.any_hit(bvh, ray) ` - Test for any intersection
398+ * ` Raycore.closest_hit(bvh, ray) ` - Find nearest intersection, returns ` (hit, triangle, distance, bary_coords) `
399+ * ` Raycore.any_hit(bvh, ray) ` - Test for any intersection (fast shadow test)
394400 * ` Raycore.reflect(wo, normal) ` - Compute reflection direction
401+ * ` triangle.metadata ` - Access custom data stored per-triangle
402+
403+ ** Key Patterns:**
404+
405+ 1 . ** Material Scene Pattern** - Associate materials with geometry using metadata:
406+
407+ ``` julia
408+ # Build BVH with mesh index as metadata
409+ bvh = Raycore. BVH (meshes, (mesh_idx, tri_idx) -> UInt32 (mesh_idx))
410+
411+ # In your shader, look up material from hit triangle
412+ mat = materials[triangle. metadata]
413+ ```
395414
396- ** Key Pattern: ** The ` compute_light ` function is reusable across the entire tutorial :
415+ 2 . ** Reusable Lighting ** - The ` compute_light ` function handles both hard and soft shadows :
397416
398417 * ` shadow_samples=1 ` → hard shadows
399- * ` shadow_samples=4 ` → soft shadows
418+ * ` shadow_samples>1 ` → soft shadows
400419
401- This shows how a well-designed function can handle multiple use cases cleanly!
420+ This shows how well-designed functions can handle multiple use cases cleanly!
402421
0 commit comments