From 269eedfd59b383a4dc54a2ecf092bb2fafbbe660 Mon Sep 17 00:00:00 2001 From: Fabio Pellacini Date: Wed, 31 Jan 2024 08:56:28 +0100 Subject: [PATCH] updated --- libs/yocto/yocto_raytracing.cpp | 514 ++++++++++++++++++++++++++++++++ libs/yocto/yocto_raytracing.h | 244 +++++++++------ 2 files changed, 660 insertions(+), 98 deletions(-) diff --git a/libs/yocto/yocto_raytracing.cpp b/libs/yocto/yocto_raytracing.cpp index 841fa6973..591837eb3 100644 --- a/libs/yocto/yocto_raytracing.cpp +++ b/libs/yocto/yocto_raytracing.cpp @@ -939,6 +939,520 @@ void overlap_bvh_elems(const bvh_data& bvh1, const bvh_data& bvh2, } // namespace yocto +// ----------------------------------------------------------------------------- +// PARALLEL HELPERS +// ----------------------------------------------------------------------------- +namespace yocto { + +// Simple parallel for used since our target platforms do not yet support +// parallel algorithms. `Func` takes the two integer indices. +template +inline void parallel_for_batch(vec2i num, Func&& func) { + auto futures = vector>{}; + auto nthreads = std::thread::hardware_concurrency(); + std::atomic next_idx(0); + std::atomic has_error(false); + for (auto thread_id = 0; thread_id < (int)nthreads; thread_id++) { + futures.emplace_back( + std::async(std::launch::async, [&func, &next_idx, &has_error, num]() { + try { + while (true) { + auto j = next_idx.fetch_add(1); + if (j >= num[1]) break; + if (has_error) break; + for (auto i = 0; i < num[0]; i++) func(vec2i{i, j}); + } + } catch (...) { + has_error = true; + throw; + } + })); + } + for (auto& f : futures) f.get(); +} + +} // namespace yocto + +// ----------------------------------------------------------------------------- +// IMPLEMENTATION FOR SHADING UTILITIES +// ----------------------------------------------------------------------------- +namespace yocto { + +// Schlick approximation of the Fresnel term +static inline vec3f fresnel_schlick( + vec3f specular, vec3f normal, vec3f outgoing) { + if (specular == vec3f{0, 0, 0}) return {0, 0, 0}; + auto cosine = dot(normal, outgoing); + return specular + + (1 - specular) * pow(clamp(1 - abs(cosine), 0.0f, 1.0f), 5.0f); +} + +// Sample an hemispherical direction with cosine distribution. +static inline vec3f sample_hemisphere_cos(vec3f normal, vec2f ruv) { + auto z = sqrt(ruv.y); + auto r = sqrt(1 - z * z); + auto phi = 2 * pif * ruv.x; + auto local_direction = vec3f{r * cos(phi), r * sin(phi), z}; + return transform_direction(basis_fromz(normal), local_direction); +} + +// Sample an hemispherical direction with cosine power distribution. +inline vec3f sample_hemisphere_cospower( + float exponent, vec3f normal, vec2f ruv) { + auto z = pow(ruv.y, 1 / (exponent + 1)); + auto r = sqrt(1 - z * z); + auto phi = 2 * pif * ruv.x; + auto local_direction = vec3f{r * cos(phi), r * sin(phi), z}; + return transform_direction(basis_fromz(normal), local_direction); +} + +} // namespace yocto + +// ----------------------------------------------------------------------------- +// IMPLEMENTATION FOR SCENE EVALUATION +// ----------------------------------------------------------------------------- +namespace yocto { + +// Generates a ray from a camera for yimg::image plane coordinate uv and +// the lens coordinates luv. +static ray3f eval_camera(const camera_data& camera, const vec2f& uv) { + auto film = camera.aspect >= 1 + ? vec2f{camera.film, camera.film / camera.aspect} + : vec2f{camera.film * camera.aspect, camera.film}; + auto q = transform_point(camera.frame, + {film.x * (0.5f - uv.x), film.y * (uv.y - 0.5f), camera.lens}); + auto e = transform_point(camera.frame, {0, 0, 0}); + return {e, normalize(e - q)}; +} + +} // namespace yocto + +// ----------------------------------------------------------------------------- +// IMPLEMENTATION FOR PATH TRACING +// ----------------------------------------------------------------------------- +namespace yocto { + +// Raytrace renderer. +static vec4f shade_raytrace(const scene_data& scene, const scene_bvh& bvh, + const ray3f& ray, int bounce, rng_state& rng, + const raytrace_params& params) { + // intersect next point + auto intersection = intersect_scene_bvh(bvh, scene, ray); + if (!intersection.hit) { + auto radiance = eval_environment(scene, ray.d); + return {radiance.x, radiance.y, radiance.z, + scene.environments.empty() ? 0.0f : 1.0f}; + } + + // evaluate geometry + auto& instance = scene.instances[intersection.instance]; + auto position = eval_position( + scene, instance, intersection.element, intersection.uv); + auto normal = eval_normal( + scene, instance, intersection.element, intersection.uv); + auto texcoord = eval_texcoord( + scene, instance, intersection.element, intersection.uv); + auto outgoing = -ray.d; + + // normal corrections + auto& shape = scene.shapes[instance.shape]; + if (!shape.triangles.empty() && dot(normal, outgoing) < 0) normal = -normal; + if (!shape.lines.empty()) normal = orthonormalize(outgoing, normal); + + // evaluate material + auto& material = scene.materials[instance.material]; + auto emission_tex = eval_texture(scene, material.emission_tex, texcoord); + auto color_tex = eval_texture(scene, material.color_tex, texcoord); + auto emission = material.emission * xyz(emission_tex); + auto color = material.color * xyz(color_tex); + auto roughness = material.roughness * material.roughness; + auto opacity = material.opacity * color_tex.w; + + // handle opacity + if (rand1f(rng) >= opacity) { + return shade_raytrace( + scene, bvh, {position, ray.d}, bounce + 1, rng, params); + } + + // accumulate emission + auto radiance = emission; + + // exit if ray is done + if (bounce >= params.bounces) return {radiance.x, radiance.y, radiance.z, 1}; + + // compute illumination + if (material.type == material_type::matte) { + auto incoming = sample_hemisphere_cos(normal, rand2f(rng)); + auto lighting = xyz(shade_raytrace( + scene, bvh, {position, incoming}, bounce + 1, rng, params)); + radiance += color * lighting; + } else if (material.type == material_type::transparent) { + auto fresnel = fresnel_schlick(vec3f{0.04, 0.04, 0.04}, normal, outgoing); + if (rand1f(rng) < mean(fresnel)) { + auto incoming = reflect(outgoing, normal); + auto lighting = xyz(shade_raytrace( + scene, bvh, {position, incoming}, bounce + 1, rng, params)); + radiance += lighting; + } else { + auto incoming = -outgoing; + auto lighting = xyz(shade_raytrace( + scene, bvh, {position, incoming}, bounce + 1, rng, params)); + radiance += color * lighting; + } + } else if (material.type == material_type::reflective && roughness == 0) { + auto incoming = reflect(outgoing, normal); + auto lighting = xyz(shade_raytrace( + scene, bvh, {position, incoming}, bounce + 1, rng, params)); + radiance += fresnel_schlick(color, normal, outgoing) * lighting; + } else if (material.type == material_type::reflective && roughness > 0) { + auto exponent = 2 / (roughness * roughness); + auto microfacet = sample_hemisphere_cospower(exponent, normal, rand2f(rng)); + auto incoming = reflect(outgoing, microfacet); + auto lighting = xyz(shade_raytrace( + scene, bvh, {position, incoming}, bounce + 1, rng, params)); + radiance += color * lighting; + } else if (material.type == material_type::glossy && roughness == 0) { + auto fresnel = fresnel_schlick(vec3f{0.04, 0.04, 0.04}, normal, outgoing); + if (rand1f(rng) < mean(fresnel)) { + auto incoming = reflect(outgoing, normal); + auto lighting = xyz(shade_raytrace( + scene, bvh, {position, incoming}, bounce + 1, rng, params)); + radiance += lighting; + } else { + auto incoming = sample_hemisphere_cos(normal, rand2f(rng)); + auto lighting = xyz(shade_raytrace( + scene, bvh, {position, incoming}, bounce + 1, rng, params)); + radiance += color * lighting; + } + } else if (material.type == material_type::glossy && roughness > 0) { + auto fresnel = fresnel_schlick(vec3f{0.04, 0.04, 0.04}, normal, outgoing); + if (rand1f(rng) < mean(fresnel)) { + auto exponent = 2 / (roughness * roughness); + auto microfacet = sample_hemisphere_cospower( + exponent, normal, rand2f(rng)); + auto incoming = reflect(outgoing, microfacet); + auto lighting = xyz(shade_raytrace( + scene, bvh, {position, incoming}, bounce + 1, rng, params)); + radiance += lighting; + } else { + auto incoming = sample_hemisphere_cos(normal, rand2f(rng)); + auto lighting = xyz(shade_raytrace( + scene, bvh, {position, incoming}, bounce + 1, rng, params)); + radiance += color * lighting; + } + } + + // done + return {radiance.x, radiance.y, radiance.z, 1}; +} + +// Matte renderer. +static vec4f shade_matte(const scene_data& scene, const scene_bvh& bvh, + const ray3f& ray, int bounce, rng_state& rng, + const raytrace_params& params) { + // intersect next point + auto intersection = intersect_scene_bvh(bvh, scene, ray); + if (!intersection.hit) { + auto radiance = eval_environment(scene, ray.d); + return {radiance.x, radiance.y, radiance.z, + scene.environments.empty() ? 0.0f : 1.0f}; + } + + // evaluate geometry + auto& instance = scene.instances[intersection.instance]; + auto position = eval_position( + scene, instance, intersection.element, intersection.uv); + auto normal = eval_normal( + scene, instance, intersection.element, intersection.uv); + auto texcoord = eval_texcoord( + scene, instance, intersection.element, intersection.uv); + + // evaluate material + auto& material = scene.materials[instance.material]; + auto emission_tex = eval_texture(scene, material.emission_tex, texcoord); + auto color_tex = eval_texture(scene, material.color_tex, texcoord); + auto emission = material.emission * xyz(emission_tex); + auto color = material.color * xyz(color_tex); + + // accumulate emission + auto radiance = emission; + + // exit if ray is done + if (bounce >= params.bounces) return {radiance.x, radiance.y, radiance.z, 1}; + + // compute illumination + auto incoming = sample_hemisphere_cos(normal, rand2f(rng)); + auto lighting = xyz(shade_raytrace( + scene, bvh, {position, incoming}, bounce + 1, rng, params)); + radiance += color * lighting; + + // done + return {radiance.x, radiance.y, radiance.z, 1}; +} + +// Pointlight for quick previewing. +static vec4f shade_pointlight(const scene_data& scene, const scene_bvh& bvh, + const ray3f& ray, int bounce, rng_state& rng, + const raytrace_params& params) { + // intersect next point + auto intersection = intersect_scene_bvh(bvh, scene, ray); + if (!intersection.hit) { + auto radiance = eval_environment(scene, ray.d); + return {radiance.x, radiance.y, radiance.z, + scene.environments.empty() ? 0.0f : 1.0f}; + } + + // evaluate geometry + auto& instance = scene.instances[intersection.instance]; + auto position = eval_position( + scene, instance, intersection.element, intersection.uv); + auto normal = eval_normal( + scene, instance, intersection.element, intersection.uv); + auto texcoord = eval_texcoord( + scene, instance, intersection.element, intersection.uv); + auto outgoing = -ray.d; + + // evaluate material + auto& material = scene.materials[instance.material]; + auto emission = material.emission * + xyz(eval_texture(scene, material.emission_tex, texcoord)); + auto color = material.color * + xyz(eval_texture(scene, material.color_tex, texcoord)); + + // emission + auto radiance = emission + abs(dot(normal, outgoing)) * color; + + // ambient illumination + radiance += color * vec3f{0.1, 0.1, 0.1}; + + // compute illumination + for (auto linstance : scene.instances) { + if (scene.shapes[linstance.shape].points.empty()) continue; + auto& lemission = scene.materials[linstance.material].emission; + if (lemission == zero3f) continue; + auto& lpositions = scene.shapes[linstance.shape].positions; + for (auto& lposition_ : lpositions) { + auto lposition = transform_point(linstance.frame, lposition_); + auto incoming = normalize(lposition - position); + if (!intersect_scene_bvh(bvh, scene, + ray3f{position, incoming, ray_eps, + length(lposition - position) - ray_eps}) + .hit) { + radiance += lemission * color * max(dot(normal, incoming), 0.0f) / + length_squared(lposition - position); + } + } + } + + // done + return {radiance.x, radiance.y, radiance.z, 1}; +} + +// Arealight for quick previewing. +static vec4f shade_arealight(const scene_data& scene, const scene_bvh& bvh, + const ray3f& ray, int bounce, rng_state& rng, + const raytrace_params& params) { + // intersect next point + auto intersection = intersect_scene_bvh(bvh, scene, ray); + if (!intersection.hit) { + auto radiance = eval_environment(scene, ray.d); + return {radiance.x, radiance.y, radiance.z, + scene.environments.empty() ? 0.0f : 1.0f}; + } + + // evaluate geometry + auto& instance = scene.instances[intersection.instance]; + auto position = eval_position( + scene, instance, intersection.element, intersection.uv); + auto normal = eval_normal( + scene, instance, intersection.element, intersection.uv); + auto texcoord = eval_texcoord( + scene, instance, intersection.element, intersection.uv); + + // evaluate material + auto& material = scene.materials[instance.material]; + auto emission_tex = eval_texture(scene, material.emission_tex, texcoord); + auto color_tex = eval_texture(scene, material.color_tex, texcoord); + auto emission = material.emission * xyz(emission_tex); + auto color = material.color * xyz(color_tex); + + // accumulate emission + auto radiance = bounce == 0 ? emission : zero3f; + + // exit if ray is done + if (bounce >= params.bounces) return {radiance.x, radiance.y, radiance.z, 1}; + + // area light + for (auto& linstance : scene.instances) { + if (scene.shapes[linstance.shape].quads.empty()) continue; + auto& lemission = scene.materials[linstance.material].emission; + if (lemission == zero3f) continue; + auto& lpositions = scene.shapes[linstance.shape].positions; + for (auto& lquad : scene.shapes[linstance.shape].quads) { + auto lposition = transform_point(linstance.frame, + interpolate_quad(lpositions[lquad.x], lpositions[lquad.y], + lpositions[lquad.z], lpositions[lquad.w], rand2f(rng))); + auto larea = quad_area(lpositions[lquad.x], lpositions[lquad.y], + lpositions[lquad.z], lpositions[lquad.w]); + auto lnormal = transform_normal( + linstance.frame, quad_normal(lpositions[lquad.x], lpositions[lquad.y], + lpositions[lquad.z], lpositions[lquad.w])); + auto incoming = normalize(lposition - position); + if (!intersect_scene_bvh(bvh, scene, + ray3f{position, incoming, ray_eps, + length(lposition - position) - ray_eps}) + .hit) { + radiance += lemission * (color / pif) * larea * + max(dot(normal, incoming), 0.0f) * + abs(dot(lnormal, incoming)) / + length_squared(lposition - position); + } + } + } + + // compute illumination + auto incoming = sample_hemisphere_cos(normal, rand2f(rng)); + auto lighting = xyz(shade_raytrace( + scene, bvh, {position, incoming}, bounce + 1, rng, params)); + radiance += color * lighting; + + // done + return {radiance.x, radiance.y, radiance.z, 1}; +} + +// Eyelight for quick previewing. +static vec4f shade_eyelight(const scene_data& scene, const scene_bvh& bvh, + const ray3f& ray, int bounce, rng_state& rng, + const raytrace_params& params) { + // intersect next point + auto intersection = intersect_scene_bvh(bvh, scene, ray); + if (!intersection.hit) { + auto radiance = eval_environment(scene, ray.d); + return {radiance.x, radiance.y, radiance.z, + scene.environments.empty() ? 0.0f : 1.0f}; + } + + // evaluate geometry + auto& instance = scene.instances[intersection.instance]; + auto normal = eval_normal( + scene, instance, intersection.element, intersection.uv); + auto texcoord = eval_texcoord( + scene, instance, intersection.element, intersection.uv); + auto outgoing = -ray.d; + + // evaluate material + auto& material = scene.materials[instance.material]; + auto emission = material.emission * + xyz(eval_texture(scene, material.emission_tex, texcoord)); + auto color = material.color * + xyz(eval_texture(scene, material.color_tex, texcoord)); + + auto radiance = emission + abs(dot(normal, outgoing)) * color; + + // add simple shading + return {radiance.x, radiance.y, radiance.z, 1}; +} + +static vec4f shade_normal(const scene_data& scene, const scene_bvh& bvh, + const ray3f& ray, int bounce, rng_state& rng, + const raytrace_params& params) { + // intersect next point + auto intersection = intersect_scene_bvh(bvh, scene, ray); + if (!intersection.hit) return {0, 0, 0, 0}; + + // prepare shading point + auto& instance = scene.instances[intersection.instance]; + auto normal = eval_normal( + scene, instance, intersection.element, intersection.uv); + + auto radiance = normal * 0.5f + 0.5f; + + // return color + return {radiance.x, radiance.y, radiance.z, 1}; +} + +static vec4f shade_texcoord(const scene_data& scene, const scene_bvh& bvh, + const ray3f& ray, int bounce, rng_state& rng, + const raytrace_params& params) { + // intersect next point + auto intersection = intersect_scene_bvh(bvh, scene, ray); + if (!intersection.hit) return {0, 0, 0, 0}; + + // prepare shading point + auto& instance = scene.instances[intersection.instance]; + auto texcoord = eval_texcoord( + scene, instance, intersection.element, intersection.uv); + texcoord = {fmod(texcoord.x, 1), fmod(texcoord.y, 1)}; + + // return color + return {texcoord.x, texcoord.y, 0, 1}; +} + +static vec4f shade_color(const scene_data& scene, const scene_bvh& bvh, + const ray3f& ray, int bounce, rng_state& rng, + const raytrace_params& params) { + // intersect next point + auto intersection = intersect_scene_bvh(bvh, scene, ray); + if (!intersection.hit) return {0, 0, 0, 0}; + + // prepare shading point + auto& instance = scene.instances[intersection.instance]; + auto& material = scene.materials[instance.material]; + auto color = material.color; + + // return color + return {color.x, color.y, color.z, 1}; +} + +// Trace a single ray from the camera using the given algorithm. +using raytrace_shader = vec4f (*)(const scene_data& scene, const scene_bvh& bvh, + const ray3f& ray, int bounce, rng_state& rng, + const raytrace_params& params); +static raytrace_shader get_shader(const raytrace_params& params) { + switch (params.type) { + case raytrace_type::raytrace: return shade_raytrace; + case raytrace_type::matte: return shade_matte; + case raytrace_type::eyelight: return shade_eyelight; + case raytrace_type::pointlight: return shade_pointlight; + case raytrace_type::arealight: return shade_arealight; + case raytrace_type::normal: return shade_normal; + case raytrace_type::texcoord: return shade_texcoord; + case raytrace_type::color: return shade_color; + default: { + throw std::runtime_error("sampler unknown"); + return nullptr; + } + } +} + +// Progressively computes an image. +image_t raytrace_image(const scene_data& scene, const scene_bvh& bvh, + const raytrace_params& params, raytrace_callback callback) { + auto& camera = scene.cameras[params.camera]; + auto shader = get_shader(params); + + auto imresolution = camera_resolution(camera, params.resolution); + auto render = image_t{imresolution, zero4f}; + auto rngs = image_t{ + imresolution, make_rngs(prod(imresolution)).data()}; + + for (auto sample : range(params.samples)) { + parallel_for_batch(render.size(), [&](vec2i ij) { + auto uv = params.samples == 1 + ? ((vec2f)ij + 0.5f) / (vec2f)render.size() + : ((vec2f)ij + rand2f(rngs[ij])) / (vec2f)render.size(); + auto ray = eval_camera(camera, uv); + auto radiance = shader(scene, bvh, ray, 0, rngs[ij], params); + if (!isfinite(radiance)) radiance = {0, 0, 0, 0}; + render[ij] = lerp(render[ij], radiance, 1.0f / (sample + 1)); + }); + if (callback) callback(sample, params.samples); + } + return render; +} + +} // namespace yocto + // ----------------------------------------------------------------------------- // EMBREE WRAPPER // ----------------------------------------------------------------------------- diff --git a/libs/yocto/yocto_raytracing.h b/libs/yocto/yocto_raytracing.h index c3f7e5b75..1cabf9384 100644 --- a/libs/yocto/yocto_raytracing.h +++ b/libs/yocto/yocto_raytracing.h @@ -67,10 +67,31 @@ using std::vector; #endif // ----------------------------------------------------------------------------- -// INTERSECTIONS +// ELEMENTS BVH AND INTERSECTION FUNCTIONS // ----------------------------------------------------------------------------- namespace yocto { +// BVH tree node containing its bounds, indices to the BVH arrays of either +// primitives or internal nodes, the node element type, +// and the split axis. Leaf and internal nodes are identical, except that +// indices refer to primitives for leaf nodes or other nodes for internal nodes. +struct bvh_node { + bbox3f bbox = invalidb3f; + int32_t start = 0; + int16_t num = 0; + int8_t axis = 0; + bool internal = false; +}; + +// BVH tree stored as a node array with the tree structure is encoded using +// array indices. BVH nodes indices refer to either the node array, +// for internal nodes, or the primitive arrays, for leaf nodes. +// Application data is not stored explicitly. +struct bvh_tree { + vector nodes = {}; + vector primitives = {}; +}; + // Results of intersect_xxx and overlap_xxx functions that include hit flag, // instance id, shape element id, shape element uv and intersection distance. // The values are all set for scene intersection. Shape intersection does not @@ -100,102 +121,6 @@ struct intersection3f { , hit{true} {} }; -} // namespace yocto - -// ----------------------------------------------------------------------------- -// RAY-PRIMITIVE INTERSECTION FUNCTIONS -// ----------------------------------------------------------------------------- -namespace yocto { - -// Intersect a ray with a point (approximate) -inline intersection3f intersect_point(const ray3f& ray, vec3f p, float r); - -// Intersect a ray with a line -inline intersection3f intersect_line( - const ray3f& ray, vec3f p0, vec3f p1, float r0, float r1); - -// Intersect a ray with a triangle -inline intersection3f intersect_triangle( - const ray3f& ray, vec3f p0, vec3f p1, vec3f p2); - -// Intersect a ray with a quad. -inline intersection3f intersect_quad( - const ray3f& ray, vec3f p0, vec3f p1, vec3f p2, vec3f p3); - -// Intersect a ray with a sphere -inline intersection3f intersect_sphere(const ray3f& ray, vec3f p, float r); - -// Intersect a ray with a axis-aligned bounding box -inline bool intersect_bbox(const ray3f& ray, const bbox3f& bbox); - -// Intersect a ray with a axis-aligned bounding box -inline bool intersect_bbox( - const ray3f& ray, vec3f ray_dinv, const bbox3f& bbox); - -} // namespace yocto - -// ----------------------------------------------------------------------------- -// POINT-PRIMITIVE DISTANCE FUNCTIONS -// ----------------------------------------------------------------------------- -namespace yocto { - -// Check if a point overlaps a position pos withint a maximum distance dist_max. -inline intersection3f overlap_point( - vec3f pos, float dist_max, vec3f p, float r); - -// Compute the closest line uv to a give position pos. -inline float closestuv_line(vec3f pos, vec3f p0, vec3f p1); - -// Check if a line overlaps a position pos withint a maximum distance dist_max. -inline intersection3f overlap_line( - vec3f pos, float dist_max, vec3f p0, vec3f p1, float r0, float r1); - -// Compute the closest triangle uv to a give position pos. -inline vec2f closestuv_triangle(vec3f pos, vec3f p0, vec3f p1, vec3f p2); - -// Check if a triangle overlaps a position pos withint a maximum distance -// dist_max. -inline intersection3f overlap_triangle(vec3f pos, float dist_max, vec3f p0, - vec3f p1, vec3f p2, float r0, float r1, float r2); - -// Check if a quad overlaps a position pos withint a maximum distance dist_max. -inline intersection3f overlap_quad(vec3f pos, float dist_max, vec3f p0, - vec3f p1, vec3f p2, vec3f p3, float r0, float r1, float r2, float r3); - -// Check if a bbox overlaps a position pos withint a maximum distance dist_max. -inline bool overlap_bbox(vec3f pos, float dist_max, const bbox3f& bbox); - -// Check if two bboxes overlap. -inline bool overlap_bbox(const bbox3f& bbox1, const bbox3f& bbox2); - -} // namespace yocto - -// ----------------------------------------------------------------------------- -// BVH FOR SHAPE ELEMENTS -// ----------------------------------------------------------------------------- -namespace yocto { - -// BVH tree node containing its bounds, indices to the BVH arrays of either -// primitives or internal nodes, the node element type, -// and the split axis. Leaf and internal nodes are identical, except that -// indices refer to primitives for leaf nodes or other nodes for internal nodes. -struct bvh_node { - bbox3f bbox = invalidb3f; - int32_t start = 0; - int16_t num = 0; - int8_t axis = 0; - bool internal = false; -}; - -// BVH tree stored as a node array with the tree structure is encoded using -// array indices. BVH nodes indices refer to either the node array, -// for internal nodes, or the primitive arrays, for leaf nodes. -// Application data is not stored explicitly. -struct bvh_tree { - vector nodes = {}; - vector primitives = {}; -}; - // Make shape bvh bvh_tree make_points_bvh(const vector& points, const vector& positions, const vector& radius, @@ -259,7 +184,7 @@ intersection3f overlap_quads_bvh(const bvh_tree& bvh, } // namespace yocto // ----------------------------------------------------------------------------- -// SHAPE AND SCENE BVH +// SHAPE AND SCENE BVH AND INTERSECTION FUNCTIONS // ----------------------------------------------------------------------------- namespace yocto { @@ -332,6 +257,110 @@ inline bool is_volumetric( } // namespace yocto +// ----------------------------------------------------------------------------- +// SIMPLE RAY TRACER +// ----------------------------------------------------------------------------- +namespace yocto { + +// Type of tracing algorithm +enum struct raytrace_type { + raytrace, // path tracing + matte, // matte only rendering + eyelight, // eyelight rendering + pointlight, // pointlight rendering + arealight, // arealight rendering + normal, // normals + texcoord, // texcoords + color, // colors +}; + +// Options for trace functions +struct raytrace_params { + int camera = 0; + int resolution = 1440; + raytrace_type type = raytrace_type::raytrace; + int samples = 512; + int bounces = 8; + bool noparallel = false; +}; + +// Raytrace callback +using raytrace_callback = std::function; + +// Progressively computes an image. +image_t raytrace_image(const scene_data& scene, const scene_bvh& bvh, + const raytrace_params& params, raytrace_callback callback = {}); + +} // namespace yocto + +// ----------------------------------------------------------------------------- +// RAY-PRIMITIVE INTERSECTION FUNCTIONS +// ----------------------------------------------------------------------------- +namespace yocto { + +// Intersect a ray with a point (approximate) +inline intersection3f intersect_point(const ray3f& ray, vec3f p, float r); + +// Intersect a ray with a line +inline intersection3f intersect_line( + const ray3f& ray, vec3f p0, vec3f p1, float r0, float r1); + +// Intersect a ray with a triangle +inline intersection3f intersect_triangle( + const ray3f& ray, vec3f p0, vec3f p1, vec3f p2); + +// Intersect a ray with a quad. +inline intersection3f intersect_quad( + const ray3f& ray, vec3f p0, vec3f p1, vec3f p2, vec3f p3); + +// Intersect a ray with a sphere +inline intersection3f intersect_sphere(const ray3f& ray, vec3f p, float r); + +// Intersect a ray with a axis-aligned bounding box +inline bool intersect_bbox(const ray3f& ray, const bbox3f& bbox); + +// Intersect a ray with a axis-aligned bounding box +inline bool intersect_bbox( + const ray3f& ray, vec3f ray_dinv, const bbox3f& bbox); + +} // namespace yocto + +// ----------------------------------------------------------------------------- +// POINT-PRIMITIVE DISTANCE FUNCTIONS +// ----------------------------------------------------------------------------- +namespace yocto { + +// Check if a point overlaps a position pos withint a maximum distance dist_max. +inline intersection3f overlap_point( + vec3f pos, float dist_max, vec3f p, float r); + +// Compute the closest line uv to a give position pos. +inline float closestuv_line(vec3f pos, vec3f p0, vec3f p1); + +// Check if a line overlaps a position pos withint a maximum distance dist_max. +inline intersection3f overlap_line( + vec3f pos, float dist_max, vec3f p0, vec3f p1, float r0, float r1); + +// Compute the closest triangle uv to a give position pos. +inline vec2f closestuv_triangle(vec3f pos, vec3f p0, vec3f p1, vec3f p2); + +// Check if a triangle overlaps a position pos withint a maximum distance +// dist_max. +inline intersection3f overlap_triangle(vec3f pos, float dist_max, vec3f p0, + vec3f p1, vec3f p2, float r0, float r1, float r2); + +// Check if a quad overlaps a position pos withint a maximum distance dist_max. +inline intersection3f overlap_quad(vec3f pos, float dist_max, vec3f p0, + vec3f p1, vec3f p2, vec3f p3, float r0, float r1, float r2, float r3); + +// Check if a bbox overlaps a position pos withint a maximum distance dist_max. +inline bool overlap_bbox(vec3f pos, float dist_max, const bbox3f& bbox); + +// Check if two bboxes overlap. +inline bool overlap_bbox(const bbox3f& bbox1, const bbox3f& bbox2); + +} // namespace yocto + // ----------------------------------------------------------------------------- // EMBREE WRAPPER // ----------------------------------------------------------------------------- @@ -739,6 +768,25 @@ inline bool is_volumetric( } // namespace yocto +// ----------------------------------------------------------------------------- +// ENUM LABELS +// ----------------------------------------------------------------------------- +namespace yocto { + +// trace sampler labels +inline const auto raytrace_type_labels = vector>{ + {raytrace_type::raytrace, "raytrace"}, + {raytrace_type::matte, "matte"}, + {raytrace_type::eyelight, "eyelight"}, + {raytrace_type::pointlight, "pointlight"}, + {raytrace_type::arealight, "arealight"}, + {raytrace_type::normal, "normal"}, + {raytrace_type::texcoord, "texcoord"}, + {raytrace_type::color, "color"}, +}; + +} // namespace yocto + // ----------------------------------------------------------------------------- // CUDA SUPPORT // -----------------------------------------------------------------------------