1.3.64
 
Loading...
Searching...
No Matches
Visualizer Plugin Documentation
Dependencies X11/xorg (Mac/Linux)
CMakeLists.txt set( PLUGINS "visualizer" )
Header File include "Visualizer.h"
Class Visualizer

Dependencies

Installing dependent packages
Package
td
td
td
X11/xorg $ brew install Caskroom/cask/xquartz Debian/Ubuntu:
$ sudo apt-get install libx11-dev xorg-dev libgl1-mesa-dev libglu1-mesa-dev libxrandr-dev
None (if using Visual Studio)

Known Issues

  • macOS OpenGL Warning in Headless Mode: When using headless rendering on macOS, you may see a warning like:
    UNSUPPORTED (log once): POSSIBLE ISSUE: unit 6 GLD_TEXTURE_INDEX_2D is unloadable and bound to sampler type (Float) - using zero texture because texture unloadable
    This is a harmless driver warning from the macOS OpenGL implementation and does not affect rendering functionality. It appears once per run and can be safely ignored.
  • Large point clouds are supported with automatic culling optimization.

Introduction

This plugin facilitates visualization of model geometry and data. It can visualize a number of different geometric shapes, including all of the primitive types supported by the Helios context. Individual geometric objects can be added though API commands, and there is a command to automatically add all geometric primitives from the Helios context.

Class Constructor(s)

Constructors
Visualizer( uint Wdisplay )
Visualizer( uint Wdisplay, uint Hdisplay )

The class associated with the visualization is called Visualizer. The class constructor takes one or two arguments that specifies the size of the graphics window. If only one argument is provided (see Visualizer( uint Wdisplay )), the argument is an integer that specifies the width of the diplay window in pixels, with the hight of the window specified according to the default aspect ratio. If two arguments are provided (see Visualizer( uint Wdisplay, uint Hdisplay )), the two arguments correspond respectively to the width and height of the display window in pixels. Below is an example program:

#include "Visualizer.h"
int main(){
Visualizer vis( 1200 ); //Opens a graphics window of width 1200 pixels with default aspect ratio
}

Using the Visualizer Plug-in

Coordinate Systems

The visualizer uses two types of coordinate systems to specify the locations of points in space during visualization:

  1. COORDINATES_WINDOW_NORMALIZED - Coordinates are normalized to unity and are window-aligned. The point (x,y)=(0,0) is in the bottom left corner of the window, and (x,y)=(1,1) is in the upper right corner of the window. The z-coordinate specifies the depth in the screen-normal direction, with values ranging from -1 to 1. For example, an object at z=0.5 would be behind an object at z=0. This coordinate system is typically used when annotating visualizations with text, adding watermarks, or adding objects to the dashboard.
  2. COORDINATES_CARTESIAN - Coordinates are specified in a 3D Cartesian system (right-handed), where +z is vertical. This coordinate system is typically used when adding model geometry or 3D objects.

Visualization Window

In order to actually display a window for visualization, we must issue a comand to plot the geometry. There are different comands to produce a visualization window depending on the intended output:

  • plotInteractive() - Open an interactive visualization window that allows for user input control. This type of visualization allows for one to, for example, rotate the view or zoom in/out. This will cause the program to wait until the window is closed by the user to continue.
  • plotUpdate() - Open a window and update it with the current visualization, then continue the program. This does not allow for any user input, since it continues on without checking for input. This is useful when generating a large number of visualization images for a movie.

If plotUpdate() is issued, another command printWindow() can be used to output the current visualization to file (JPEG files only).

The current window can be closed using the closeWindow() command.

Below is an example of opening a window (blank), exporting its contents to file, then closing the window:

#include "Visualizer.h"
int main(){
Visualizer vis( 1200 );
vis.plotUpdate(); //we have not added geometry, so window is blank
vis.printWindow("blank_window.jpg");
vis.closeWindow();
}

Headless Rendering

The Visualizer supports headless rendering for server environments or automated workflows where no display is available. Headless mode enables all visualization and image export functionality without requiring a graphics display or user interaction.

Enabling Headless Mode:

Headless mode is enabled through the constructor by setting the headless parameter to true:

#include "Visualizer.h"
int main(){
// Enable headless mode (5th parameter = true)
Visualizer vis( 1200, 800, 8, true, true );
// All visualization functions work normally
vis.buildContextGeometry(&context);
vis.setCameraPosition( make_vec3(0,0,5), make_vec3(0,0,0) );
vis.plotUpdate(true);
vis.printWindow("headless_output.jpg");
}

Key Features:

  • Full OpenGL rendering using offscreen framebuffers
  • All geometry types and visual effects supported
  • Image export via printWindow() and getWindowPixelsRGB()
  • No display or window manager required
  • Identical visual output to windowed mode

Important Notes:

  • When in headless mode, calling plotUpdate(false) (requesting visible window) will issue a warning and continue with offscreen rendering
  • Always use plotUpdate(true) for headless workflows to avoid warnings
  • plotInteractive() is not available in headless mode

Use Cases:

  • Automated visualization pipelines on servers
  • Batch processing of simulation results
  • Integration with cloud computing environments
  • Continuous integration testing of visualization features

Window background

The Visualizer provides multiple options for customizing the window background:

Default Gradient Background

By default, the Visualizer displays a subtle vertical gradient background that transitions from a neutral blue at the bottom to white at the top. This provides a clean, professional appearance without requiring any configuration.

Solid Color Background

The window background can be set to a constant color using the setBackgroundColor( RGBcolor color ) command:

#include "Visualizer.h"
int main(){
Visualizer vis( 1200 );
vis.setBackgroundColor( RGB::white ); // Set background to white
}

Transparent Background

For creating visualizations with transparent backgrounds (useful for overlaying on other images or presentations), use the setBackgroundTransparent() command. When rendering to the screen, a checkerboard pattern indicates transparency. When exporting to PNG format via printWindow(), the background will have true alpha channel transparency:

#include "Visualizer.h"
int main(){
Visualizer vis( 1200 );
vis.setBackgroundTransparent();
vis.plotUpdate();
vis.printWindow("output.png", "png"); // Saves with transparent background
}

Note: Transparent backgrounds only work with PNG output format. JPEG output will have an opaque background.

Custom Image Background

You can set a custom texture image as the background using the setBackgroundImage( const char* texture_file ) command:

#include "Visualizer.h"
int main(){
Visualizer vis( 1200 );
vis.setBackgroundImage( "path/to/custom_background.jpg" );
}

The image will be stretched to fill the window while maintaining the aspect ratio to avoid distortion. Both JPEG and PNG formats are supported.

Sky Texture Background

For outdoor scenes, a three-dimensional sky background can be added using the setBackgroundSkyTexture command. This creates a dynamically scaling sky sphere that uses shader-based transformations to keep the camera always inside, eliminating the need to manually specify a radius. The sky appears infinitely distant regardless of camera position or zoom level:

#include "Visualizer.h"
int main(){
Visualizer vis( 1200 );
// Use default sky texture (SkyDome_clouds.jpg)
vis.setBackgroundSkyTexture();
// Or specify a custom texture
// vis.setBackgroundSkyTexture( "path/to/custom_sky.jpg" );
}

The method automatically uses the default sky texture (plugins/visualizer/textures/SkyDome_clouds.jpg) if no texture file is specified. The sky can be toggled on/off like other background modes using setBackgroundColor, setBackgroundTransparent, or setBackgroundImage.

Note
The older addSkyDomeByCenter method is deprecated and will be removed in a future version. It required manual specification of radius and center, which could break when zooming out beyond the dome's boundaries. The new setBackgroundSkyTexture method provides a more robust solution that works correctly at all zoom levels.

Adding Geometry

Next, we will learn how to add objects to the visualization. The visualizer supports the following objects:

Primitive Description Add Functions
Rectangle Rectangular polygon with coplanar vertices.
Triangle Triangular polygon specified by its three vertices.
Voxel Parallelpiped or rectangular prism.
Sphere Tessellated sphere composed of triangles.
Line Linear segment between two points.

Point Single point in 3D space with configurable size.

Textbox Box of text.

Each object can be added by calling one of the associated functions listed in the table above. The syntax is usually fairly similar to how gometry is added in the Helios context, except that you must specify a coordinate system (see Coordinate Systems).

The code sample below gives an example of how to add a horizontal rectangle to the visualizer by providing the coordinates of its center:

#include "Visualizer.h"
int main(){
Visualizer vis( 1200 );
vec3 center = make_vec3(0,0,0);
vec2 size = make_vec2(1,1);
SphericalCoord rotation = make_SphericalCoord(0,0);
vis.addRectangleByCenter(center, size, rotation, RGB::red, Visualizer::COORDINATES_CARTESIAN);
}

Automatically importing Context geometry

The visualizer can automatically import some or all geometry from the Context. This is accomplished using the Visualizer::buildContextGeometry() command. To add all primitives in the Context, the Visualizer::buildContextGeometry() command would be issued, which is passed the Context. We can add a subset of the Context geometry through an additional argument which takes a vector of UUID's.

Command Description
buildContextGeometry( Context* context_ptr ) Add all primitives in the Context to the Visualizer.
buildContextGeometry( Context* context_ptr, std::vector<uint> UUIDs ) Add a subset of primitives in the Context to the Visualizer.

The example below shows how to add all Context geometry to the visualizer.

#include "Context.h"
#include "Visualizer.h"
using namespace helios;
int main(){
Context context;
vec3 center = make_vec3(0,0,0);
vec2 size = make_vec2(1,1);
context.addPatch(center,size);
Visualizer vis( 1200 );
vis.buildContextGeometry(&context);
}

Point Cloud Performance Optimization

For large point clouds (tens of millions of points), the visualizer includes automatic culling optimization to maintain interactive performance. This feature is enabled by default when the number of points exceeds a configurable threshold.

Point Culling Methods

The visualizer supports two types of automatic culling:

  • View Frustum Culling - Points outside the camera's field of view are automatically excluded from rendering
  • Distance-based Culling - Distant points are culled with level-of-detail scaling and adaptive sizing to maintain visual coverage

Point Culling Configuration

Point cloud culling can be configured using the following functions:

Function Description
setPointCullingEnabled( bool enabled ) Enable or disable automatic point culling.
setPointCullingThreshold( size_t threshold ) Set the minimum number of points required to trigger culling optimization.
setPointMaxRenderDistance( float distance ) Set the maximum distance for point rendering in distance-based culling.
setPointLODFactor( float factor ) Set the level-of-detail scaling factor for distant points.
getPointRenderingMetrics(...) Retrieve real-time performance metrics including total points, rendered points, and culling computation time.

Example code showing how to configure point cloud culling:

#include "Visualizer.h"
int main(){
Visualizer vis( 1200 );
// Configure culling for large point clouds
vis.setPointCullingEnabled(true);
vis.setPointCullingThreshold(1000000); // Enable for point clouds > 1M points
vis.setPointMaxRenderDistance(500.0f); // Cull points beyond 500 units
vis.setPointLODFactor(10.0f); // Aggressive LOD scaling
// Add large point cloud...
for(int i = 0; i < 10000000; ++i) {
vis.addPoint(make_vec3(random_x, random_y, random_z), RGB::red, 1.0f, Visualizer::COORDINATES_CARTESIAN);
}
// Check performance metrics
size_t total, rendered;
float culling_time;
vis.getPointRenderingMetrics(total, rendered, culling_time);
std::cout << "Rendering " << rendered << "/" << total << " points in " << culling_time << "ms" << std::endl;
}

Plotting Geometry

To this point, we have not actually plotted anything in the Visualizer window. A final command is neede to display all of the geometry added to the Visualizer in the window we have opened. There are two functions for doing this, which are detailed below.

plotInteractive()

The Visualizer::plotInteractive() function can be used to generate an interactive plot of the geometry in the Visualizer. This means that the code will pause to produce the plot/visualization until the window is closed by the user. The user can interact with the plot by issuing keystrokes to, e.g., zoom. An example is given below to generate an interactive plot.

#include "Context.h"
#include "Visualizer.h"
using namespace helios;
int main(){
Context context;
vec3 center = make_vec3(0,0,0);
vec2 size = make_vec2(1,1);
context.addRectangleByCenter(center,size);
Visualizer vis( 1200 );
vis.buildContextGeometry(&context);
vis.plotInteractive();
}

View Controls

In an interactive plot, the view can be modified via the mouse or keyboard. Note that for very large scenes, mouse-based controls may be laggy.

Mouse camera controls are given by:

Mouse Button Action
scroll wheel zoom in/out relative to look-at position
left mouse button (+drag) rotate camera view about look-at position
right mouse button (+drag) move look-at position

Keyboard camera controls are given by:

Key Action
up arrow increase the viewing elevation angle
down arrow decrease the viewing elevation angle
left arrow rotate camera left (clockwise)
right arrow rotate camera right (counter-clockwise)
spacebar+up arrow move the camera position upward
spacebar+down arrow move the camera position downward
+ zoom in
- zoom out

plotUpdate()

The Visualizer::plotUpdate() function simply updates the plot window based on current geometry, and continues on to the next lines of code. This can be useful if only a still image is to be written to file, as illustrated below.

#include "Context.h"
#include "Visualizer.h"
using namespace helios;
int main(){
Context context;
vec3 center = make_vec3(0,0,0);
vec2 size = make_vec2(1,1);
context.addPatch(center,size);
Visualizer vis( 1200 );
vis.buildContextGeometry(&context);
vis.plotUpdate();
vis.printWindow("rectangle.jpg");
vis.closeWindow();
}

Colors and Shading

Coloring by r-g-b code

The surfaces of primitives are most commonly colored by specifying an r-g-b/r-g-b-a color code when adding the geometry, and thus this is the default behavior.

Coloring by texture map

Rectangles and disks have the cabability of coloring their surface according to a texture map. A texture map can be specified by providing the path to either a JPEG or PNG image file. The image will be mapped onto the surface of the primitive element.

Coloring by pseudocolor map

Primitives can be colored by mapping associated data values to a color table. Given some range of data values, each value is normalized by this range and used to look up an associated color in the color table. The available predefined color tables are shown below. There is also the capability of defining custom color tables.

Enumeration Example
Visualizer::COLORMAP_HOT
td
Visualizer::COLORMAP_COOL
Visualizer::COLORMAP_RAINBOW
Visualizer::COLORMAP_LAVA
Visualizer::COLORMAP_PARULA
Visualizer::COLORMAP_GRAY
Visualizer::COLORMAP_CUSTOM N/A

The colormap to be used is set using the following command setColormap( Ctable colormap_name ), where ‘colormap_name’ is one of the enumerations in the table above. For example, to set the ‘hot’ colormap one would use:

vis.setColormap( Visualizer::COLORMAP_HOT );

To set a custom color table, the same command is used with the Visualizer::COLORMAP_CUSTOM argument, plus additional arguments that define the custom color table. These arguments are 1) a vector of r-g-b colors defining various colors in the color table, and 2) a vector of values between 0 and 1 defining the relative positions of the colors in the table. The first position value must always be 0, and the last value must be 1. The visualizer interpolates between colors to populate the full color table. The table below shows how the ‘hot’ color table is defined:

RGBcolor relative position
(0, 0, 0)
0
(0.5, 0, 0.5)
0.25
(1, 0, 0)
0.5
(1, 0.5, 0)
0.75
(1, 1, 0)
1

Example code on how to implement the color table creation is given below:

Visualizer vis(1000);
std::vector<RGBcolor> ctable_colors;
ctable_colors.push_back( make_RGBcolor( 0.f, 0.f, 0.f ) );
ctable_colors.push_back( make_RGBcolor( 0.5f, 0.f, 0.5f ) );
ctable_colors.push_back( make_RGBcolor( 1.f, 0.f, 0.f ) );
ctable_colors.push_back( make_RGBcolor( 1.f, 0.5f, 0.f ) );
ctable_colors.push_back( make_RGBcolor( 1.f, 1.f, 0.f ) );
std::vector<float> ctable_positions;
ctable_positions.push_back( 0.f );
ctable_positions.push_back( 0.25f );
ctable_positions.push_back( 0.5f );
ctable_positions.push_back( 0.75f );
ctable_positions.push_back( 1.f );
vis.setColormap( Visualizer::COLORMAP_CUSTOM, ctable_colors, ctable_positions );

Colorbar

The colorbar is the legend showing how values are mapped to the color table. The table below gives functions for customizing colorbar behavior, including for example its position, size, and visibility.

Function Description
enableColorbar(void) Make the colorbar visible.
disableColorbar(void) Make the colorbar invisible.
setColorbarPosition( vec3 position ) Set the position of the colorbar. Note that position.z gives the depth of the colorbar.
setColorbarSize( vec2 size ) Set the size of the colorbar.
setColorbarRange( float cmin, float cmax ) Set the range of data values for the colorbar/colormap.
setColorbarTicks( const std::vector<float> &ticks ) Set locations of data tick along colorbar.
setColorbarTitle( const char* title ) Set the title text displayed above the colorbar.
setColorbarFontColor( RGBcolor color ) Set the color of text in the colorbar.
setColorbarFontSize( uint font_size ) Set the size of the colorbar text in points.

Automatic Tick Generation

When custom ticks are not specified using Visualizer::setColorbarTicks, the visualizer automatically generates tick positions and labels using Heckbert's "nice numbers" algorithm. This algorithm selects tick values that are "clean" multiples of 1, 2, or 5 times a power of 10, making the colorbar easier to read.

Adaptive Tick Count: The number of ticks is automatically determined based on the colorbar size and font size to prevent overcrowding. Smaller colorbars will have fewer ticks to maintain readability.

For example:

  • A colorbar ranging from 0 to 1 will generate ticks at 0.0, 0.2, 0.4, 0.6, 0.8, 1.0
  • A colorbar ranging from 0 to 600 (with limited space) will generate ticks at 0, 200, 400, 600
  • A colorbar ranging from 0 to 20 with integer data will generate ticks at 0, 5, 10, 15, 20

The tick label formatting automatically adapts based on the data type and range:

  • Integer data: Tick labels are formatted as integers with no decimal places (e.g., 0, 5, 10). For values ≥10,000, scientific notation is used (e.g., 1e+04)
  • Floating-point data: Tick labels show an appropriate number of decimal places based on the tick spacing (e.g., 0.0, 0.2, 0.4). Scientific notation is used for values ≥10,000 or <0.001
  • Scientific notation: Automatically used for very large (≥10,000) or very small (<0.001) values to maintain readability

The data type (integer vs. float) is automatically detected when calling Visualizer::colorContextPrimitivesByData or Visualizer::colorContextPrimitivesByObjectData.

Shading

To allow for more realistic visualizations, the Phong shading model can be enabled. The Phong shading model can be enabled with or without shadows. The shading options and their enumeration for specifying them are detailed in the table below. The appropriate enumeration is passed to the Visualizer::setLightingModel command to enable the specified shading model.

Shading Model Enumeration Example
No shading is applied. Objects are colored only according to the r-g-b(-a) color code or texture map. Visualizer::LIGHTING_NONE
Phong shading model Visualizer::LIGHTING_PHONG
Phong shading model with shadows. Visualizer::LIGHTING_PHONG_SHADOWED

If the Phong shading model is used, the position of the light source should be specified. This is accomplished through the Visualizer::setLightDirection( const helios::vec3 &direction ) command, which takes a unit vector pointing toward the light source. The example below shows how to enable the Phong lighting model with shadows, with the light position set according to the position of the sun.

#include "Visualizer.h"
int main(){
Visualizer vis( 1200 );
vec3 center = make_vec3(0,0,0);
vec2 size = make_vec2(1,1);
SphericalCoord rotation = make_SphericalCoord(0,0);
vis.addRectangleByCenter(center,size,rotation,RGB::red,Visualizer::COORDINATES_CARTESIAN,0);
vec3 light_direction = make_vec3(1,1,1);
vis.setLightingModel( Visualizer::LIGHTING_PHONG_SHADOWED );
vis.setLightDirection( light_direction );
}

View Options

The default camera position is at an elevation angle of 20 degrees and to the North, with the camera looking toward the origin. The distance of the camera from the origin is automatically adjusted to fit all primitives in view.

There are multiple ways of specifying custom camera views. One method involves specifying the (x,y,z) position of the camera, and the (x,y,z) position that the camera is looking at. This is accomplished using the command setCameraPosition( vec3 cameraPosition, vec3 lookAt ).

The other method involves specifying the spherical coordinates of the camera with respect to the (x,y,z) position the camera is looking at. This is accomplished using the setCameraPosition( SphericalCoord cameraAngle, vec3 lookAt ).

Acknowledgements

This plug-in uses all or parts of the following open-sourced software libraries:

The OpenGL Extension Wrangler Library: Copyright (C) 2008-2016, Nigel Stewart <nigels[]users sourceforge net> Copyright (C) 2002-2008, Milan Ikits <milan ikits[]ieee org> Copyright (C) 2002-2008, Marcelo E. Magallon <mmagallo[]debian org> Copyright (C) 2002, Lev Povalahev All rights reserved.

The FreeType Project: Portions of this software are copyright © 2019 The FreeType Project (www.freetype.org). All rights reserved.

GLFW: Copyright © 2002-2006 Marcus Geelnard Copyright © 2006-2019 Camilla Löwy

OpenGL Mathematics (GLM): Copyright (c) 2005 - 2014 G-Truc Creation

libjpeg: This software is copyright (C) 1991-2016, Thomas G. Lane, Guido Vollbeding. All Rights Reserved except as specified below.

libpng: Copyright (c) 1995-2019 The PNG Reference Library Authors. Copyright (c) 2018-2019 Cosmin Truta. Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson. Copyright (c) 1996-1997 Andreas Dilger. Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.