Skip to content

Yocto/Geometry: Geometry operations

Yocto/Geometry defines basic geometry operations, including computation of basic geometry quantities, ray-primitive intersection, point-primitive distance, primitive bounds, and several interpolation functions. Yocto/Geometry also defines rays, bounding boxes and their transforms. Yocto/Geometry is implemented in yocto_geometry.h.

Primitive parametrization

Yocto/Geometry supports operations on lines, triangles, quads and beziers. In these functions, triangles are parameterized with barycentric coordinates uv written with respect to the p1-p0 and p2-p0 axes respectively. Quads are internally handled as pairs of triangles p0,p1,p3 and p2,p3,p1, with the uv coordinates of the second triangle corrected as 1-u and 1-v to produce a quad parametrization where u and v go from 0 to 1. Degenerate quads with p2==p3 represent triangles correctly. This parametrization is equivalent to Intel's Embree.

Geometric properties

For lines, Yocto/Geometry supports the computation of line lengths, with line_length(p0,p1), and line tangents, with line_tangent(p0,p1). For triangles, Yocto/Geometry supports the computation of triangle areas, with triangle_area(p0,p1,p2), and triangle normals, triangle_normal(p0,p1,p2). Similarly for quads, use quad_area(p0,p1,p2,p3), quad_normal(p0,p1,p2,p3) for areas and normals.

For triangles and quads, Yocto/Geometry also supports computing tangents and bitangents from texture coordinates. This is helpful for applying normal or bump mapping during rendering. Use triangle_tangents_fromuv(p0,p1,p2,uv0,uv1,uv2) for triangles and quad_tangents_fromuv(p0,p1,p2,p3,uv0,uv1,uv2,uv3,uv) for quads.

For triangles, tangents and bitangents are defined with respect to the first vertex as the origin. For quads, we define the vectors on the two triangles and do not compute the average. For this pass an additional texture coordinate since internally we split the triangle into two and we need to known where to do it.

auto p0 = vec3f{0,0,0}, p1 = vec3f{1,0,0}, p2 = vec3f{1,1,0}, p3=vec3f{0,1,0};
auto ta = triangle_area(p0,p1,p2);   // triangle area
auto qa = quad_area(p0,p1,p2,p3);    // quad area
auto tn = triangle_normal(p0,p1,p2); // triangle normal
auto qn = quad_normal(p0,p1,p2,p3);  // quad normal
auto uv0 = vec2f{0,0}, uv1 = vec2f{1,0}, uv2 = vec2f{1,1}, uv3 = vec2f{0,1};
auto [tu, tv] = triangle_tangents_fromuv(p0,p1,p2,uv0,uv1,uv2); // tangents

Interpolation on primitives

For all primitives, Yocto/Geometry defines interpolation functions that take values defined at the primitive vertices and compute the interpolate value at the parametrized point. Lines and beziers are parametrized with their natural parameter, while triangles and quads use barycentric interpolation as defined above.

Use interpolate_line(p0,p1,u) for lines, interpolate_triangle(p0,p1,p2,uv) for triangles, interpolate_quad(p0,p1,p2,p3,uv) for quads and interpolate_bezier(p0,p1,p2,p3,u) for cubic Bezier segments, whose derivatives can be computed with interpolate_bezier_derivative(p0,p1,p2,p3,u).

auto p0 = vec3f{0,0,0}, p1 = vec3f{1,0,0}, p2 = vec3f{1,1,0}, p3=vec3f{0,1,0};
auto tp = interpolate_triangle(p0,p1,p2,{0.3,0.5});  // triangle point
auto qp = interpolate_quad(p0,p1,p2,p3,{0.3,0.5});   // quad point
auto lp = interpolate_line(p0,p1,0.3);               // line point
auto bp = interpolate_bezier(p0,p1,p2,p3,0.3);       // bezier point

Bounding boxes

Yocto/Math defines axies-aligned bounding boxes in 2 to 3 dimensions as bbox2f and bbox3f. Bounding boxes store the minimum and maximum coordinate values, that can be accessed with b.min and b.max. Bounding boxes are default-initialized to an invalid state that contains no points, or they are constructed by specifying the min and max values directly.

To build bounds for complex primitives, bounding boxes are very initialized to empty bounds, that can be done by using the constants like invalidabXf, and then grown to encompass either points or other bounding boxes with merge(b,p). To transform bounding boxes use transform_bbox(frame, bbox).

auto bbox = invalidb3f;
for(auto point : points) bbox = merge(bbox, point);
auto transform = frame3f{...};
auto transformed = transform_bbox(tranform, bbox);

Primitive bounding boxes

Yocto/Geometry provides functions to compute bounding boxes for all primitives types. For points and lines, vertices might have a thickness associate with them. Use point_bounds(p0,r0) for points, line_bounds(p0,p1,r0,r1) for lines, triangle_bounds(p0,p1,p2) for triangles, quad_bounds(p0,p1,p2,p3) for quads.

auto p0 = vec3f{0,0,0}, p1 = vec3f{1,0,0}, p2 = vec3f{1,1,0}, p3=vec3f{0,1,0};
auto tb = triangle_bounds(p0,p1,p2);  // triangle bounding box
auto qb = quad_bounds(p0,p1,p2,p3);   // quad bounding box
auto r0 = 0.01, r1 = 0.01;
auto lb = line_bounds(p0,p1,r0,r1);   // line bounding box
auto pb = point_bounds(p0,r0);        // point bounding box

Rays

Yocto/Math defines rays in 2-3 dimensions as ray2f and ray3f. Rays are defined as an origin o, a direction d and minimum and maximum values for the distance along a ray, namely tmin and tmax. To compute a point in a ray, use ray_point(ray,t). To transform rays use transform_ray(frame, ray).

auto ray = ray3f{origin, direction};
auto p = ray_point(ray, 0.5);
auto transform = frame3f{...};
auto transformed = transform_ray(tranform, ray);

Ray-primitive intersections

Yocto/Geometry defines functions for ray-primitive intersection. Each function returns wether the primitive was hit and, if so, sets the primitive parameters and the intersection distance as output variables. Triangle intersection are computed using the Moller-Trombone intersection algorithm. Quad intersections are computed by treating quads as two triangles. Point intersections are compute approximately, by treating points as ray-oriented disks. Line intersections are computed approximately, by treating lines as ray-oriented ribbons. Use intersect_point(ray,p0,r0,uv,d) for points, intersect_line(ray,p0,p1,r0,r1,uv,d) for lines, intersect_triangle(ray,p0,p1,p2,uv,d) for triangles, intersect_quad(ray,p0,p1,p2,p3,uv,d) for quads.

auto p0 = vec3f{0,0,0}, p1 = vec3f{1,0,0}, p2 = vec3f{1,1,0}, p3=vec3f{0,1,0};
auto r0 = 0.01, r1 = 0.01;
auto ray = ray3f{{0,0,0.5},{0,0,-1}};                 // ray
auto uv = vec2f{0,0}; auto dist = float{0};           // hit distance and uvs
auto th = intersect_triangle(ray,p0,p1,p2,uv,dist));  // triangle intersection
auto qh = intersect_quad(ray,p0,p1,p2,p3,uv,dist));   // quad intersection
auto lh = intersect_line(ray,p0,p1,r0,r1,uv,dist));   // line intersection
auto ph = intersect_point(ray,p0,r0,uv,dist));        // point intersection

Yocto/Geometry defines two functions to test whether a ray hits a bounding box. In this case, we do not return the ray distance or hit, but just check for intersection, which is useful when defining BVH hierarchies. Use intersect_bbox(ray,bbox) as a simple alternative and intersect_bbox(ray,ray_dinv,bbox) for a faster one.

auto p0 = vec3f{0,0,0}, p1 = vec3f{1,0,0}, p2 = vec3f{1,1,0}, p3=vec3f{0,1,0};
auto bbox = quad_bounds(p0,p1,p2,p3);
auto bh = intersect_bbox(ray, bbox);           // bbox intersection check
auto ray_dinv = 1 / ray.d;                     // ray direction inverse
auto bf = intersect_bbox(ray, ray_dinv, bbox); // fast bbox intersection

Point-primitive overlaps

Yocto/Geometry defines functions for point-primitive distance and overlap. Each function returns wether the primitive was hit and, if so, sets the primitive parameters and the overlap distance as output variables. Each function takes a position and a maximum distance to test within, together with primitive vertices and thickness. Use overlap_point(pt,md,p0,r0,uv,d) for points, overlap_line(pt,md,p0,p1,r0,r1,uv,d) for lines, overlap_triangle(pt,md,p0,p1,p2,r0,r1,r2,uv,d) for triangles, overlap_quad(pt,md,p0,p1,p2,p3,r0,r1,r2,r3,uv,d) for quads.

auto p0 = vec3f{0,0,0}, p1 = vec3f{1,0,0}, p2 = vec3f{1,1,0}, p3=vec3f{0,1,0};
auto r0 = 0.01, r1 = 0.01, r2 = 0.01, r3 = 0.01;
auto pt = vec3f{0,0,0.5}; auto md = float{1};         // point and max dist
auto uv = vec2f{0,0}; auto dist = float{0};           // hit distance and uv
auto th = overlap_triangle(pt,md,p0,p1,p2,uv,dist));  // triangle overlap
auto qh = overlap_quad(pt,md,p0,p1,p2,p3,uv,dist));   // quad overlap
auto lh = overlap_line(pt,md,p0,p1,r0,r1,uv,dist));   // line overlap
auto ph = overlap_point(pt,md,p0,r0,uv,dist));        // point overlap

Yocto/Geometry defines a function to test whether a point is contained within a bounding bbox within a certain distance. Just like before, we do not return the ray distance or hit, but just check for overlap, which is useful when defining BVH hierarchies. Use overlap_bbox(pt,md,bbox) to test for overlap between a point and a bounding box and overlap_bbox(bbox1,bbox2) to test whether two bounding boxes overlap.

auto p0 = vec3f{0,0,0}, p1 = vec3f{1,0,0}, p2 = vec3f{1,1,0}, p3=vec3f{0,1,0};
auto bbox = quad_bounds(p0,p1,p2,p3);
auto bbox2 = bbox3f{{0,0,0}, {1,1,1}};
auto pt = vec3f{0,0,0.5}; auto md = float{1};   // point and max dist
auto bh  = overlap_bbox(pt, md, bbox);          // bbox overlap check
auto bh2 = overlap_bbox(bbox, bbox2);           // bbox overlap check