RTC_GEOMETRY_TYPE_SUBDIVISION(3)
β¨ RTC_GEOMETRY_TYPE_SUBDIVISION β subdivision geometry type β¨
okay bestie, sit down, hydrate, because we're talking about Embree Ray Tracing Kernels 4 and specifically the subdivision geometry type, and honestly? it's giving main character energy. this is a <pink>3embree4</pink> situation. let's go. π¦
πΈ SYNOPSIS πΈ
#include <embree4/rtcore.h>
RTCGeometry geometry =
rtcNewGeometry(device, RTC_GEOMETRY_TYPE_SUBDIVISION);
like. that's it. that's the whole vibe to get one of these bad boys into existence. π
π DESCRIPTION π
So. Catmull-Clark subdivision meshes? Supported. Fully. And not just the basic version β we're talking support for edge creases, vertex creases, holes, non-manifold geometry, and face-varying interpolation. the whole buffet, no cap. π
The number of vertices per face can be anywhere in the range of 3 to 15 vertices β triangles, quadrilaterals, pentagons, etc. little shapes, big shapes, weird shapes. all welcome. β¨
You create subdivision meshes by passing RTC_GEOMETRY_TYPE_SUBDIVISION to the rtcNewGeometry function. Then you (the application, bestie, that's you) need to set up a bunch of buffers. Want the deets on buffer setting? Peep rtcSetGeometryBuffer and rtcSetSharedGeometryBuffer. π«Ά
Here's the buffer rundown, and I need you to focus because there's a lot:
- face buffer β type
RTC_BUFFER_TYPE_FACE, formatRTC_FORMAT_UINT. Holds the number of edges/indices of each face (that's your 3 to 15). The number of faces is inferred from the size of this buffer. sneaky but iconic. - index buffer β type
RTC_BUFFER_TYPE_INDEX. Holds multiple (3 to 15) 32-bit vertex indices inRTC_FORMAT_UINTformat, for each face. The number of edges is inferred from the size of this buffer. - vertex buffer β type
RTC_BUFFER_TYPE_VERTEX. Stores an array of single precision x, y, z floating point coordinates inRTC_FORMAT_FLOAT3format. The number of vertices is inferred from the size of this buffer.
not me pointing out that literally every count here is inferred from the buffer size. that's the pattern. learn it, love it. π
Optionally, you can set additional index buffers using different buffer slots if you need multiple topologies for face-varying interpolation. The standard vertex buffers (RTC_BUFFER_TYPE_VERTEX) are always bound to the geometry topology (topology 0), which means they use RTC_BUFFER_TYPE_INDEX with buffer slot 0. User vertex data interpolation may use different topologies β more on that later, keep reading. π
Optionally, you can set up the hole buffer (RTC_BUFFER_TYPE_HOLE), which contains an array of 32-bit indices in RTC_FORMAT_UINT format of faces that should be considered non-existing in all topologies. The number of holes is inferred from the size of this buffer.
Optionally, you can fill the level buffer (RTC_BUFFER_TYPE_LEVEL) with a tessellation rate for each of the edges of each face. Heads up: this buffer must have the same size as the index buffer. The tessellation level is a positive floating point value in RTC_FORMAT_FLOAT format, and it says how many quads along the edge should be generated during tessellation. If you don't specify a level buffer, a level of 1 is used. The maximally supported edge level is 4096, and larger levels get clamped to that value. π
Now the important bit: edges may be shared between (typically 2) faces. To guarantee a watertight tessellation, the level of these shared edges should be identical. If you want a uniform tessellation rate for an entire subdivision mesh, use the rtcSetGeometryTessellationRate function. And the existence of a level buffer has precedence over the uniform tessellation rate. periodt.
Optionally, you can fill the sparse edge crease buffers to make edges appear sharper. π₯
- edge crease index buffer (
RTC_BUFFER_TYPE_EDGE_CREASE_INDEX) β an array of pairs of 32-bit vertex indices inRTC_FORMAT_UINT2format that specify unoriented edges in the geometry topology. - edge crease weight buffer (
RTC_BUFFER_TYPE_EDGE_CREASE_WEIGHT) β for each of these crease edges, a positive floating point weight inRTC_FORMAT_FLOATformat.
The number of edge creases is inferred from the size of these buffers, which has to be identical. The larger a weight, the sharper the edge. Specifying a weight of infinity is supported and marks an edge as infinitely sharp. Storing an edge multiple times with the same crease weight is allowed, but has lower performance. Storing an edge multiple times with different crease weights results in undefined behavior. π
For a stored edge (i,j), the reverse direction edges (j,i) do not have to be stored, since both are considered the same unoriented edge. Edge crease features are shared between all topologies.
Optionally, you can fill the sparse vertex crease buffers to make vertices appear sharper. β
- vertex crease index buffer (
RTC_BUFFER_TYPE_VERTEX_CREASE_INDEX) β an array of 32-bit vertex indices inRTC_FORMAT_UINTformat specifying a set of vertices from the geometry topology. - vertex crease weight buffer (
RTC_BUFFER_TYPE_VERTEX_CREASE_WEIGHT) β for each of these vertices, a positive floating point weight inRTC_FORMAT_FLOATformat.
The number of vertex creases is inferred from the size of these buffers, and has to be identical. The larger a weight, the sharper the vertex. Specifying a weight of infinity is supported and makes the vertex infinitely sharp. Storing a vertex multiple times with the same crease weight is allowed, but has lower performance. Storing a vertex multiple times with different crease weights results in undefined behavior. π Vertex crease features are shared between all topologies.
Subdivision modes can be used to force linear interpolation for parts of the subdivision mesh β see rtcSetGeometrySubdivisionMode for more details.
For multi-segment motion blur, the number of time steps must be first specified using the rtcSetGeometryTimeStepCount call. Then a vertex buffer for each time step can be set using different buffer slots, and all these buffers have to have the same stride and size. no mixing and matching, bestie.
Also, go look at the tutorial [Subdivision Geometry] for an example of how to create subdivision surfaces. it's cute, it'll help. π
π¦ Parametrization π¦
okay so the parametrization for subdivision faces is different for quadrilaterals vs non-quadrilateral faces. two whole different worlds, lowkey.
Quads are the chill ones. The parametrization of a quadrilateral face uses the first vertex p0 as base point, and the vector p1 - p0 as u-direction and p3 - p0 as v-direction. simple. elegant. β¨
Everything else (any face type where the number of vertices is not equal 4) gets a special parametrization, where the subpatch ID n (of the n-th quadrilateral that would be obtained by a single subdivision step) and the local hit location inside this quadrilateral are encoded in the UV coordinates. so you're not getting plain UVs, you're getting a little encoded package. π
The following code extracts the sub-patch ID i and local UVs of this subpatch:
unsigned int l = floorf(0.5f*U);
unsigned int h = floorf(0.5f*V);
unsigned int i = 4*h+l;
float u = 2.0f*fracf(0.5f*U)-0.5f;
float v = 2.0f*fracf(0.5f*V)-0.5f;
This encoding allows local subpatch UVs to be in the range [-0.5,1.5[ β yes, that bracket is doing its job there β thus negative subpatch UVs can be passed to rtcInterpolate to sample subpatches slightly out of bounds. Which is actually useful if you need to calculate derivatives using finite differences. π§
The encoding further has the property that one can just move the value u (or v) on a subpatch by adding du (or dv) to the special UV encoding, as long as it does not fall out of the [-0.5,1.5[ range. iconic behaviour honestly.
To smoothly interpolate vertex attributes over the subdivision surface, we recommend using the rtcInterpolate function, which will apply the standard subdivision rules for interpolation and automatically takes care of the special UV encoding for non-quadrilaterals. let it do the hard work for you, bestie. π
π Face-Varying Data π
Face-varying interpolation is supported through multiple topologies per subdivision mesh, plus binding such topologies to vertex attribute buffers to interpolate. The way this works: texture coordinates may use a different topology with additional boundaries, so you can construct separate UV regions inside one subdivision mesh. multiple little zones, one mesh. obsessed. π«Ά
Each such topology i has:
- a separate index buffer (specified using
RTC_BUFFER_TYPE_INDEXwith buffer sloti) - a separate subdivision mode, which you can set using
rtcSetGeometrySubdivisionMode
A vertex attribute buffer RTC_BUFFER_TYPE_VERTEX_ATTRIBUTE bound to a buffer slot j can be assigned to use a topology for interpolation using the rtcSetGeometryVertexAttributeTopology call.
The face buffer (RTC_BUFFER_TYPE_FACE type) is shared between all topologies, which means that the n-th primitive always has the same number of vertices (e.g. being a triangle or a quad) for each topology. However, the indices of the topologies themselves may be different. plot twist, but a consistent one. π
π₯ EXIT STATUS π₯
On failure NULL is returned and an error code is set that can be queried using rtcGetDeviceError. so if you get a NULL back, don't panic, just go check that error code. it's telling you what's up. π
π SEE ALSO π
[rtcNewGeometry]
and that's the whole page, bestie!! go make some unreasonably beautiful smooth surfaces, tag me in the group chat when it renders πβ¨
signing off from the Embree Ray Tracing Kernels 4 docs β the RTC_GEOMETRY_TYPE_SUBDIVISION(3) page, 3embree4 edition. mwah. π
π¦ where this page comes from
- ubuntu stonking: libembree-dev 4.4.1+dfsg-2ubuntu1 Β· Apache-2.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms