Vector Field Rendering¶
libvfrendering is a C++ library for rendering vectorfields using OpenGL. Originally developed for spirit and based on WegGLSpins.js, it has an extendable architecture and currently offers renderer implementations for: - glyph-based vector field representations as arrows - colormapped surface and isosurface rendering - mapping vector directions onto a sphere
The library is still very much a work-in-progress, so its API is not yet stable and there are still several features missing that will be added in later releases. If you miss a feature or have another idea on how to improve libvfrendering, please open an issue or pull request!
Getting Started¶
To use libvfrendering, you need to perform the following steps:
- Include <VFRendering/View.hxx>
- Create a VFRendering::Geometry
- Read or calculate the vector directions
- Pass geometry and directions to a VFRendering::View
- Draw the view in an existing OpenGL context
1. Include <VFRendering/View.hxx>¶
When using libvfrendering, you will mostly interact with View
objects, so it should be enough to #include <VFRendering/View.hxx>
.
2. Create a VFRendering::Geometry¶
The geometry describes the positions on which you evaluated the vector field and how they might form a grid (optional, e.g. for isosurface and surface rendering). You can pass the positions directly to the constructor or call one of the class’ static methods.
As an example, this is how you could create a simple, cartesian 30x30x30 geometry, with coordinates between -1 and 1:
auto geometry = VFRendering::Geometry::cartesianGeometry(
{30, 30, 30},
{-1.0, -1.0, -1.0},
{1.0, 1.0, 1.0}
);
3. Read or calculate the vector directions¶
This step highly depends on your use case. The directions are stored as a ``std::vector<glm::vec3>``, so they can be created in a simple loop:
std::vector<glm::vec3> directions;
for (int iz = 0; iz < 10; iz++) {
for (int iy = 0; iy < 10; iy++) {
for (int ix = 0; ix < 10; ix++) {
// calculate direction for ix, iy, iz
directions.push_back(glm::normalize({ix-4.5, iy-4.5, iz-4.5}));
}
}
}
As shown here, the directions should be in C order when using the
VFRendering::Geometry
static methods. If you do not know
glm, think of a glm::vec3
as a struct
containing three floats x, y and z.
4. Create a VFRendering::VectorField¶
This class simply contains geometry and directions.
VFRendering::VectorField vf(geometry, directions);
To update the VectorField data, use VectorField::update
. If the
directions changed but the geometry is the same, you can use the
VectorField::updateVectors
method or VectorField::updateGeometry
vice versa.
5. Create a VFRendering::View and a Renderer¶
The view object is what you will interact most with. It provides an interface to the various renderers and includes functions for handling mouse input.
You can create a new view and then initialize the renderer(s)
(as an example, we use the VFRendering::ArrowRenderer
):
VFRendering::View view;
auto arrow_renderer_ptr = std::make_shared<VFRendering::ArrowRenderer>(view, vf);
view.renderers( {{ arrow_renderer_ptr, {0, 0, 1, 1} }} );
5. Draw the view in an existing OpenGL context¶
To actually see something, you need to create an OpenGL context using a toolkit of your choice, e.g. Qt or GLFW. After creating the context, pass the framebuffer size to the setFramebufferSize method. You can then call the draw method of the view to render the vector field, either in a loop or only when you update the data.
view.draw();
For a complete example, including an interactive camera, see demo.cxx.
Python Package¶
The Python package has bindings which correspond directly to the C++ class and function names. To use pyVFRendering, you need to perform the following steps:
import pyVFRendering as vfr
- Create a
vfr.Geometry
- Read or calculate the vector directions
- Pass geometry and directions to a
vfr.View
- Draw the view in an existing OpenGL context
1. import¶
In order to import pyVFRendering as vfr
, you can either
pip install pyVFRendering
or download and build it yourself.
You can build with python3 setup.py build
, which will generate a
library somewhere in your build
subfolder, which you can import
in python. Note that you may need to add the folder to your
PYTHONPATH
.
2. Create a pyVFRendering.Geometry¶
As above:
geometry = vfr.Geometry.cartesianGeometry(
(30, 30, 30), # number of lattice points
(-1.0, -1.0, -1.0), # lower bound
(1.0, 1.0, 1.0) ) # upper bound
3. Read or calculate the vector directions¶
This step highly depends on your use case. Example:
directions = []
for iz in range(n_cells[2]):
for iy in range(n_cells[1]):
for ix in range(n_cells[0]):
# calculate direction for ix, iy, iz
directions.append( [ix-4.5, iy-4.5, iz-4.5] )
4. Pass geometry and directions to a pyVFRendering.View¶
You can create a new view and then pass the geometry and directions by calling the update method:
view = vfr.View()
view.update(geometry, directions)
If the directions changed but the geometry is the same, you can use the updateVectors method.
5. Draw the view in an existing OpenGL context¶
To actually see something, you need to create an OpenGL context using a framework of your choice, e.g. Qt or GLFW. After creating the context, pass the framebuffer size to the setFramebufferSize method. You can then call the draw method of the view to render the vector field, either in a loop or only when you update the data.
view.setFramebufferSize(width*self.window().devicePixelRatio(), height*self.window().devicePixelRatio())
view.draw()
For a complete example, including an interactive camera, see demo.py.
Renderers¶
libvfrendering offers several types of renderers, which all inherit
from VFRendering::RendererBase
. The most relevant are the
VectorFieldRenderer
s:
- VFRendering::ArrowRenderer, which renders the vectors as colored arrows
- VFRendering::SphereRenderer, which renders the vectors as colored spheres
- VFRendering::SurfaceRenderer, which renders the surface of the geometry using a colormap
- VFRendering::IsosurfaceRenderer, which renders an isosurface of the vectorfield using a colormap
- VFRendering::VectorSphereRenderer, which renders the vectors as dots on a sphere, with the position of each dot representing the direction of the vector
In addition to these, there also the following renderers which do not
require a VectorField
: - VFRendering::CombinedRenderer, which can be
used to create a combination of several renderers, like an isosurface
rendering with arrows - VFRendering::BoundingBoxRenderer, which is used
for rendering bounding boxes around the geometry rendered by an
VFRendering::ArrorRenderer, VFRendering::SurfaceRenderer or
VFRendering::IsosurfaceRenderer - VFRendering::CoordinateSystemRenderer,
which is used for rendering a coordinate system, with the axes colored
by using the colormap
To control what renderers are used, you can use
VFRendering::View::renderers
, where you can pass it a
std::vector
s of std::pair
s of renderers as
std::shared_ptr<VFRendering::RendererBase>
(i.e. shared pointers)
and viewports as glm::vec4
.
Options¶
To modify the way the vector field is rendered, libvfrendering offers a variety of options. To set these, you can create an VFRendering::Options object.
As an example, to adjust the vertical field of view, you would do the following:
VFRendering::Options options;
options.set<VFRendering::View::Option::VERTICAL_FIELD_OF_VIEW>(30);
view.updateOptions(options);
If you want to set only one option, you can also use View::setOption:
view.setOption<VFRendering::View::Option::VERTICAL_FIELD_OF_VIEW>(30);
If you want to set an option for an individual Renderer, you can use the methods RendererBase::updateOptions and RendererBase::setOption in the same way.
Whether this way of setting options should be replaced by getters/setters will be evaluated as the API becomes more stable.
Currently, the following options are available:
Index | Type | Default value | Header file | Documentation |
---|---|---|---|---|
View::Op tion::BO UNDING_ BOX_MIN | glm::vec 3 | {-1, -1, -1} | View.hxx | VFRendering::Uti lities::Options: :Option< View::Option::BO UNDING_BOX_MIN > |
View::Op tion::BO UNDING_ BOX_MAX | glm::vec 3 | {1, 1, 1} | View.hxx | VFRendering::Uti lities::Options: :Option< View::Option::BO UNDING_BOX_MAX > |
View::Op tion::SY STEM_CE NTER | glm::vec 3 | {0, 0, 0} | View.hxx | VFRendering::Uti lities::Options: :Option< View::Option::SY STEM_CENTER > |
View::Op tion::VE RTICAL_ FIELD_O F_VIEW | float | 45.0 | View.hxx | VFRendering::Uti lities::Options: :Option< View::Option::VE RTICAL_FIELD_O F_VIEW > |
View::Op tion::BA CKGROUND _COLOR | glm::vec 3 | {0, 0, 0} | View.hxx | VFRendering::Uti lities::Options: :Option< View::Option::BA CKGROUND_COLOR > |
View::Op tion::CO LORMAP_ IMPLEMEN TATION | std::str ing | VFRendering::Uti lities::getColor mapImplementatio n(VFRendering::U tilities::Colorm ap::DEFAULT) | View.hxx | VFRendering::Uti lities::Options: :Option< View::Option::CO LORMAP_IMPLEMEN TATION > |
View::Op tion::IS _VISIBL E_IMPLE MENTATIO N | std::str ing | bool is_visible(vec3 position, vec3 direction) { return true; } | View.hxx | VFRendering::Uti lities::Options: :Option< View::Option::IS _VISIBLE_IMPLE MENTATION > |
View::Op tion::CA MERA_PO SITION | glm::vec 3 | {14.5, 14.5, 30} | View.hxx | VFRendering::Uti lities::Options: :Option< View::Option::CA MERA_POSITION > |
View::Op tion::CE NTER_PO SITION | glm::vec 3 | {14.5, 14.5, 0} | View.hxx | VFRendering::Uti lities::Options: :Option< View::Option::CE NTER_POSITION > |
View::Op tion::UP _VECTOR | glm::vec 3 | {0, 1, 0} | View.hxx | VFRendering::Uti lities::Options: :Option< View::Option::UP _VECTOR > |
ArrowRen derer::O ption::C ONE_RAD IUS | float | 0.25 | ArrowRenderer. hxx | VFRendering::Uti lities::Options: :Option< ArrowRenderer::O ption::CONE_RAD IUS > |
ArrowRen derer::O ption::C ONE_HEI GHT | float | 0.6 | ArrowRenderer. hxx | VFRendering::Uti lities::Options: :Option< ArrowRenderer::O ption::CONE_HEI GHT > |
ArrowRen derer::O ption::C YLINDER_RADIUS | float | 0.125 | ArrowRenderer. hxx | VFRendering::Uti lities::Options: :Option< ArrowRenderer::O ption::CYLINDER_RADIUS > |
ArrowRen derer::O ption::C YLINDER_HEIGHT | float | 0.7 | ArrowRenderer. hxx | VFRendering::Uti lities::Options: :Option< ArrowRenderer::O ption::CYLINDER_HEIGHT > |
ArrowRen derer::O ption::L EVEL_OF _DETAIL | unsigned int | 20 | ArrowRenderer. hxx | VFRendering::Uti lities::Options: :Option< ArrowRenderer::O ption::LEVEL_OF _DETAIL > |
Bounding BoxRende rer::Opt ion::COL OR | glm::vec 3 | {1.0, 1.0, 1.0} | BoundingBoxRen derer.hxx | VFRendering::Uti lities::Options: :Option< BoundingBoxRende rer::Option::COL OR > |
Coordina teSystem Renderer ::Option ::AXIS_ LENGTH | glm::vec 3 | {0.5, 0.5, 0.5} | CoordinateSyst emRenderer.hxx | VFRendering::Uti lities::Options: :Option< CoordinateSystem Renderer::Option ::AXIS_LENGTH > |
Coordina teSystem Renderer ::Option ::ORIGIN | glm::vec 3 | {0.0, 0.0, 0.0} | CoordinateSyst emRenderer.hxx | VFRendering::Uti lities::Options: :Option< CoordinateSystem Renderer::Option ::ORIGIN > |
Coordina teSystem Renderer ::Option ::NORMAL IZE | bool | false | CoordinateSyst emRenderer.hxx | VFRendering::Uti lities::Options: :Option< CoordinateSystem Renderer::Option ::NORMALIZE > |
Coordina teSystem Renderer ::Option ::LEVEL_OF_DET AIL | unsigned int | 100 | CoordinateSyst emRenderer.hxx | VFRendering::Uti lities::Options: :Option< CoordinateSystem Renderer::Option ::LEVEL_OF_DET AIL > |
Coordina teSystem Renderer ::Option ::CONE_ HEIGHT | float | 0.3 | CoordinateSyst emRenderer.hxx | VFRendering::Uti lities::Options: :Option< CoordinateSystem Renderer::Option ::CONE_HEIGHT > |
Coordina teSystem Renderer ::Option ::CONE_ RADIUS | float | 0.1 | CoordinateSyst emRenderer.hxx | VFRendering::Uti lities::Options: :Option< CoordinateSystem Renderer::Option ::CONE_RADIUS > |
Coordina teSystem Renderer ::Option ::CYLIND ER_HEIG HT | float | 0.7 | CoordinateSyst emRenderer.hxx | VFRendering::Uti lities::Options: :Option< CoordinateSystem Renderer::Option ::CYLINDER_HEIG HT > |
Coordina teSystem Renderer ::Option ::CYLIND ER_RADI US | float | 0.07 | CoordinateSyst emRenderer.hxx | VFRendering::Uti lities::Options: :Option< CoordinateSystem Renderer::Option ::CYLINDER_RADI US > |
Isosurfa ceRender er::Opti on::ISOV ALUE | float | 0.0 | IsosurfaceRend erer.hxx | VFRendering::Uti lities::Options: :Option< IsosurfaceRender er::Option::ISOV ALUE > |
Isosurfa ceRender er::Opti on::LIGH TING_IM PLEMENTA TION | std::str ing | float lighting(vec3 position, vec3 normal) { return 1.0; } | IsosurfaceRend erer.hxx | VFRendering::Uti lities::Options: :Option< IsosurfaceRender er::Option::LIGH TING_IMPLEMENTA TION > |
Isosurfa ceRender er::Opti on::VALU E_FUNCT ION | std::fun ction | [] (const glm::vec3& position, const glm::vec3& direction) { return direction.z; } | IsosurfaceRend erer.hxx | VFRendering::Uti lities::Options: :Option< IsosurfaceRender er::Option::VALU E_FUNCTION > |
VectorSp hereRend erer::Op tion::PO INT_SIZ E_RANGE | glm::vec 2 | {1.0, 4.0} | VectorSphereRe nderer.hxx | VFRendering::Uti lities::Options: :Option< VectorSphereRend erer::Option::PO INT_SIZE_RANGE > |
VectorSp hereRend erer::Op tion::IN NER_SPH ERE_RAD IUS | float | 0.95 | VectorSphereRe nderer.hxx | VFRendering::Uti lities::Options: :Option< VectorSphereRend erer::Option::IN NER_SPHERE_RAD IUS > |
VectorSp hereRend erer::Op tion::US E_SPHER E_FAKE_PERSPEC TIVE | bool | true | VectorSphereRe nderer.hxx | VFRendering::Uti lities::Options: :Option< VectorSphereRend erer::Option::US E_SPHERE_FAKE_PERSPECTIVE > |
ToDo¶
- A EGS plugin for combining libvfrendering with existing EGS plugins.
- Methods for reading geometry and directions from data files
- Documentation
See the issues for further information and adding your own requests.