Embree API Example
On this page you find a simple example application (a Embree 'hello world', if you will), to demonstrate how easy it is to use Embree. The application creates a triangle and a ray and performs a ray-triangle intersection using the Embree API. The full source code is shown below and afterwards small snippets of the code will be discussed in more detail. Build instructions can be found at the bottom of the page. Please refer to the documentation for more information.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | // Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include <embree3/rtcore.h> #include <limits> #include <iostream> int main() { RTCDevice device = rtcNewDevice(NULL); RTCScene scene = rtcNewScene(device); RTCGeometry geom = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_TRIANGLE); float* vb = (float*) rtcSetNewGeometryBuffer(geom, RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, 3*sizeof(float), 3); vb[0] = 0.f; vb[1] = 0.f; vb[2] = 0.f; // 1st vertex vb[3] = 1.f; vb[4] = 0.f; vb[5] = 0.f; // 2nd vertex vb[6] = 0.f; vb[7] = 1.f; vb[8] = 0.f; // 3rd vertex unsigned* ib = (unsigned*) rtcSetNewGeometryBuffer(geom, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, 3*sizeof(unsigned), 1); ib[0] = 0; ib[1] = 1; ib[2] = 2; rtcCommitGeometry(geom); rtcAttachGeometry(scene, geom); rtcReleaseGeometry(geom); rtcCommitScene(scene); RTCRayHit rayhit; rayhit.ray.org_x = 0.f; rayhit.ray.org_y = 0.f; rayhit.ray.org_z = -1.f; rayhit.ray.dir_x = 0.f; rayhit.ray.dir_y = 0.f; rayhit.ray.dir_z = 1.f; rayhit.ray.tnear = 0.f; rayhit.ray.tfar = std::numeric_limits<float>::infinity(); rayhit.hit.geomID = RTC_INVALID_GEOMETRY_ID; RTCIntersectContext context; rtcInitIntersectContext(&context); rtcIntersect1(scene, &context, &rayhit); if (rayhit.hit.geomID != RTC_INVALID_GEOMETRY_ID) { std::cout << "Intersection at t = " << rayhit.ray.tfar << std::endl; } else { std::cout << "No Intersection" << std::endl; } rtcReleaseScene(scene); rtcReleaseDevice(device); } |
Step by step
We first create several Embree objects: A Embree device with a default configuration (see sec. 5.1 and 7.1 in the documention for details), a Embree scene, and a Embree geometry to represent our triangle.
1 2 3 | RTCDevice device = rtcNewDevice(NULL); RTCScene scene = rtcNewScene(device); RTCGeometry geometry = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_TRIANGLE); |
A scene is a collection of geometry objects. Scenes are what the intersect / occluded API functions work on. You can think of a scene as an acceleration structure, e.g. a bounding-volume hierarchy.
Next, we instruct Embree to create vertex and index buffers into which we copy the geomety data. For complex scenes, shared buffers are often better choice but special care must be taken to ensure proper alignment and padding. This is described in more detail in the API documentation.
1 2 3 4 5 6 7 8 9 | float* vb = (float*) rtcSetNewGeometryBuffer(geom, RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, 3*sizeof(float), 3); vb[0] = 0.f; vb[1] = 0.f; vb[2] = 0.f; // 1st vertex vb[3] = 1.f; vb[4] = 0.f; vb[5] = 0.f; // 2nd vertex vb[6] = 0.f; vb[7] = 1.f; vb[8] = 0.f; // 3rd vertex unsigned* ib = (unsigned*) rtcSetNewGeometryBuffer(geom, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, 3*sizeof(unsigned), 1); ib[0] = 0; ib[1] = 1; ib[2] = 2; |
After setting the geometry data the
geometry can be committed and attached to the scene. Embree objects such as
RTCDevice
, RTCScene
and RTCGeometry
are reference-counted. This means, that the scene takes ownership of the
geometry when we attach it and we can release the geometry handle. The API
function rtcAttachGeometry
returns a geometry ID which can be
used identify intersected objects when the scene contains multiple geometry
objects. We finish the setup by committing the scene, after which the scene
can be intersected.
1 2 3 4 | rtcCommitGeometry(geometry); rtcAttachGeometry(scene, geometry); rtcReleaseGeometry(geometry); rtcCommitScene(scene); |
To perform a ray intersect, we obviously have to create a ray first. Embree also expects an intersection context which can be used for advanced features such as intersection filters and instancing.
1 2 3 4 5 6 7 8 9 | RTCRayHit rayhit; rayhit.ray.org_x = 0.f; rayhit.ray.org_y = 0.f; rayhit.ray.org_z = -1.f; rayhit.ray.dir_x = 0.f; rayhit.ray.dir_y = 0.f; rayhit.ray.dir_z = 1.f; rayhit.ray.tnear = 0.f; rayhit.ray.tfar = std::numeric_limits<float>::infinity(); rayhit.hit.geomID = RTC_INVALID_GEOMETRY_ID; RTCIntersectContext context; rtcInitIntersectContext(&context); |
We set the ray values tnear
to
0 and tfar
to infinity to indicate that the ray starts at
origin (org_x, org_y, org_z)
and is unbounded. We also set
geomID
to RTC_INVALID_GEOMETRY_ID
. If an
intersection occured, geomID
will contain the ID of the
geometry that has been intersected. In this case, the value
tfar
will contain the ray parameter which we can compute the
point at which the ray and triangle intersect as
(org_x, org_y, org_z) + t * (dir_x, dir_y, dir_z)
.
Now we can call Embree's intersect function and check if the ray intersects the triangle.
1 2 3 4 5 6 7 | rtcIntersect1(scene, &context, &rayhit); if (rayhit.hit.geomID != RTC_INVALID_GEOMETRY_ID) { std::cout << "Intersection at t = " << rayhit.ray.tfar << std::endl; } else { std::cout << "No Intersection" << std::endl; } |
At the end of an application all resources that where allocated through Embree should be released. Note that the geometry will be released automatically, when the (last) scene the geometry is attached to is released.
1 2 | rtcReleaseScene(scene); rtcReleaseDevice(device); |
Building the example
To build and run the
example application you can download the latest Embree
release, the hello_embree.cpp
source code, and a simple
CMakeLists.txt
file.
After extracting the Embree release archive and placing hello_embree.cpp
and
CMakeLists.txt
into the same folder, you can run
1 2 3 | cmake -B build -Dembree_DIR=/path/to/extracted/embree/release/lib/cmake/embree-VERSION cmake --build build ./build/hello_embree |
on Unix-based systems or
1 2 3 | cmake -B build -Dembree_DIR=C:\path\to\extracted\embree\release\lib\cmake\embree-VERSION cmake --build build build\Debug\hello_embree.exe |
in a developer command prompt for Visual Studio on windows.
The CMake variable embree_DIR
has to be
set to the directory which contains the embree-config.cmake
file
in the extracted release archive.
If everything went smoothly you should get the following output
1 | Intersection at t = 1 |
Next steps
Some suggestions on how to continue from here:
- Modify the ray such that it does not intersect the triangle.
- Add another triangle to the scene.
- Add a
RTC_GEOMETRY_TYPE_QUAD
geometry to the scene and check if a ray intersects the correct geometry by inspecting the geomerty IDs. - Take a look at the API documentation.
- Take a look at the Embree tutorials.