Carna
Version 3.0.1
|
Carna provides classes for simple and fast visualization of CT data. It is named after the greek god of organs (yup, they really did have even one for organs). It is based on OpenGL 3.3 and Eigen 3.
Jump to: Quick Start | API 3.x Version Log | Test Suite Documentation
GEOMETRY_TYPE_
variables can be chosen arbitrary, but must be distinct.data
and know the voxel spacings
, build the scene graph, e.g. like this: Now lets take a closer look at what the code presented above actually does.
The FrameRenderer
consists of multiple components, each of which defines a particular aspect of the rendering logic. Those components are called rendering stages because their rendering logic is executed sequentially. The rendering results depends heavily on the stages order. There is no generally "correct" order, because this always depends on what one actually expects. The FrameRendererHelper
class assumes a default order that will lead to the desired results in presumably almost all cases. Of course one could also leave out this helper and add the stages to the renderer in any desired order manually.
In the example code above, several typical rendering stages are used:
At the moment the following other stages are available out-of-the-box:
As one may guess from this list, each scene might contain multiple types of renderable objects. At least one could distinguish between polygonal and volumetric objects. Planes are certainly a third type: They are neither polygonal because they are infinitely extended, nor they are volumetric. This is the very breakdown that was used in the example, but it is up to the user to choose a more detailed classification if required. Note that each rendering stage expects to be told which geometry type it should render. By using two CuttingPlanesStage
instances with different geometry types for example, one could render multiple cutting planes with different windowing settings.
Carna represents spatial entities with instances of the Spatial
class. Such entities can be renderable objects, as well as imaginary points in space. The location of each spaital entity is determined relatively to another one that is called its parent. This parent-child relationship induces a tree structure, that is commonly referred to as scene graph. Such a scene graph represents a scene. This has two implications: First, that each scene contains exactly one node that has no parent, namely the tree's root. Second, that it is sufficient to specify an arbitrary node in order to reach any other Spatial
of the scene.
The specific type of a Spatial
decides upon whether it is an inner node or a leaf of the scene graph. If it is allowed to have children, the spatial entity will be realized by an instance of the base::Node class, even if it has no children in a particular situation. In contrast, visible scene elements, i.e. such that can be rendered, must always be leafs. They will be realized by instances of the base::Geometry class usually. Another leaf type is the Camera
that not only has a location within the scene, but also specifies how the 3D space is to be projected into 2D.
It should be clear from the above why the root of a scene graph always is a base::Node instance. The coordinate system of the root is often called world space. You can read more on the different coordinate systems and how they are related to each other here.
Camera
handling: presets::PerspectiveControl, presets::CameraShowcaseControl, presets::CameraNavigationControlGeometry
objects very easy.The VolumeGridHelper
class takes care of two things. First, it partitions the volumetric data
into multiple smaller, box-shaped volumes. This partitioning is done according to an upper limit of each volume's memory size. It reduces the probability of out-of-memory exceptions due to memory fragmentation. In the example no specific limit is set, thus the default is used. Second, the VolumeGridHelper
class creates a scene Node
that represents the partitioned data
within the scene. This Node
has one Geometry
child per volume partition.
Each Geometry
node is rendered by the rendering stage with a matching geometry type. Usually that rendering stage will query particular features from this Geometry
object: Features are like components that make up the Geometry
object in its entirety, but the Geometry
object aggregates them, i.e. does not take their possession. This allows the same feature to be reused many times across the scene, or even across many scenes. Rendering stages identify features through the roles they take when associated with a Geometry
object.
The geometry features API is documented here.
Because of their shared-use nature, the lifecycle of geometry features is worth taking a look at. They have neither a public constructor, nor a public destructor:
::create
function. Conventionally, Carna uses pointers in method interfaces when the object possession is transferred and C++ references when it is not. Each feature possess itself, hence that functions return references.release
on the feature right after its creation, it will be deleted immediately because its unused.Geometry
object, the feature notices that it got attached. You may release
it now, but it will not be deleted until the Geometry
object it is attached to also dies.The idea is that the user invokes release
on the feature as soon as it is sure that the feature is not going to be attached to any further Geometry
objects. The feature object is leaked when it never is released, in which case you will also get an error message on the log output.
Materials determine how polygonal geometries are rendered. The core part of each material is a shader. Besides that, it can also have a set of parameters that will be uploaded to the shader when the material is applied, and it could enforce a particular OpenGL render state.
There are a few material shaders available out-of-the-box:
unshaded
material shader colors the rendered mesh in a single color uniformly.pointmarker
material shader to create simple markers in 3D space.The creation of custom materials is explained here.
Documentation generated by Doxygen