Rendering Point Data
A commonly used type of dataset is those containing a set of 3D positions. These can be used to spatially represent a vast variety of object types, where each object is represented by a point in space. In OpenSpace, such datasets are referred to as point clouds and include a set of features like coloring, adjusting the point size, fading in and out based on the camera distance, and attaching text labels to the positions. Coloring includes color mapping based on data columns, and there is also support for handling missing values.
This page describes how to load a point dataset and the options for controlling the visual of the points. It is also possible to add text labels to the points. See the separate Labels page for more details on labels.
Also see RenderablePointCloud asset component documentation for more details, available settings and examples of how to load and render point datasets.
Loading Point Datasets
In the simplest case, a point cloud is created by just loading a CSV or SPECK file using a renderable of the type RenderablePointCloud
. The example below shows a minimal scene graph node specification for an asset to load a point cloud from a CSV file, with default values for all the visual properties.
local Node = {
Identifier = "ExamplePoints",
Renderable = {
Type = "RenderablePointCloud",
-- Load the data file (must have an x, y and z column)
File = asset.resource("path/to/dataset.csv")
},
GUI = {
Name = "Example Points",
Path = "/Example/Point Clouds",
}
}
...
However, you will most likely want to update the size and coloring of the points, for example using a color map or by adding a sprite texture to use for rendering the points. See upcoming sections for details on how to configure the rendering and visuals of the points.
Data caching
Per default, the first time any dataset is loaded it will be cached in a version that is faster to load at the next startup. This may lead to changes in the dataset not being registered unless you first remove the cached version of the file from the OpenSpace/cache folder.
We recommend keeping caching on as it greatly speeds up the loading of the assets. However, if you want to temporarily disable caching you can use the setting UseCaching = false
in the renderable. That way, a fresh load will be done for the dataset every time the renderable is created.
Units
Per default, the X, Y and Z positions of the points are interpreted in meters, but it is also possible to specify a specific unit to match the one in the dataset. For example, if the positions in the data file are to be interpreted in parsec, add Unit = "pc"
to the Renderable specification, like so:
...
Renderable = {
Type = "RenderablePointCloud",
File = asset.resource("path/to/dataset.csv"),
Unit = "pc" -- short for "parsec"
},
...
Other options are for example "Mpc"
for Megaparsec, or Km
for kilometers. See RenderablePointCloud documentation for a list of supported units.
Data Mapping
There are cases where we need control of certain parameters in the data loading. One example is that for CSV files, we might want to specify what data columns to use for the X, Y and Z components of the position, rather than renaming the columns. Or, we might want to specify other details about the data loading, such as columns that are not relevant and can be excluded. This is done by providing a DataMapping
table in the asset when loading the dataset:
...
Renderable = {
Type = "RenderablePointCloud",
File = asset.resource("path/to/dataset.csv"),
DataMapping = {
-- Using the DataMapping, we can specify the name of the columns to use for the X,
-- Y and Z values of the point, without changing the dataset used for the rendering
X = "column name for X",
Y = "column name for Y",
Z = "column name for Z",
-- It is also possible to specify a numeric value that corresponds to missing
-- values in the dataset. These will be interpreted as NaN values
MissingDataValue = -9999,
-- And the names of some columns that we do not want to include in the loading
ExcludeColumns = { "IdontWantThisColumn", "this is not relevant either" }
}
},
...
Note
Note that updating the data mapping leads to another version of the data file being loaded and cached on disk.
Labels
Labels can either be added using an explicit labels file, as explained on this page, or directly from a column in a CSV file. To add labels from a column in the file, specify the name of that column in the data mapping:
...
Renderable = {
Type = "RenderablePointCloud",
File = asset.resource("path/to/dataset.csv"),
DataMapping = {
Name = "column name for Labels column"
}
},
...
it is also possible to control the size and color of the labels by adding a Labels
group in the asset. See Labels page for more details. Note that no File
property is required in the Labels
group when loading labels directly from the CSV file.
Coloring
The points can be colored either using a fixed color or by a color map (see separate page for details about color map data formats). These are set by adding a Coloring
component to the Renderable specification:
...
Renderable = {
Type = "RenderablePointCloud",
File = asset.resource("path/to/dataset.csv"),
-- Add a component for controlling the coloring
Coloring = {
-- Specify a fixed color for all the points:
FixedColor = { 1.0, 0.0, 0.0 }, -- red
-- Or apply a color map:
ColorMapping = {
File = asset.resource("path/to/colormap.cmap"),
-- Any other settings...
}
}
},
...
If a color map is added, it will be enabled by default. But it can also be disabled either in the asset or during runtime. If both a FixedColor
and ColorMapping
is specified, the color mapping will be used (as long as it is enabled).
Color Mapping
Alternatively, each point can be colored using a selected data variable and a color map. This is done by adding a ColorMapping
table in the asset, as shown above. With that setup, the point cloud dataset can be interactively colored by any data value in the dataset, which is useful when exploring or creating a new visualization. However, it is also possible to prepare a set of parameters and data ranges to choose from (see below).
The coloring is performed based on a color map, a choice of data parameter and a value range. Points with values within the range will be colored based on the normalized value of their datapoint, where the minimum value corresponds to the first color in the color map and the max value corresponds to the last color. Any color in between will be determined in a nearest-neighbor fashion.
Default behavior
By default, values outside the range will be clamped to either the first or last color, and missing values are excluded from the rendering. However, as explained below, there are settings to control the visual outcome for these cases.
The data parameter used for coloring at startup will be the last one that is loaded.
Predefine color parameters and value ranges
By default, all the variables in the dataset are loaded as possible options for color mapping, and the value range to use for the is generated. However, it is also possible to predefine a set of parameters that can be used for coloring, together with predefined value ranges that should be applied when selecting a parameter among the provided options.
...
Renderable = {
...
Coloring = {
ColorMapping = {
File = asset.resource("path/to/colormap.cmap"),
-- Add a few parameter options, that will be the available options in the drop down
-- menu in the user interface. The "name of parameter" is the name of the corresponding
-- column in the dataset
ParameterOptions = {
-- Add a parameter with a predefined value range that should be used
-- when this option is chosen
{ Key = "name of parameter", Range = { 1.0, 30.0 } },
-- Add another parameter, but with no specific range. The range used
-- when changing parameter will me the min and max value in the dataset
{ Key = "name of another parameter" }
},
-- Per default, the chosen parameter is set to the first one in the list above.
-- However, you can also set the default parameter in the asset
Parameter = "name of parameter",
-- It is also possible to set a value range that is different compared to the
-- one for the parameter option. This will be used at startup, but overwritten
-- if the parameter option is changed
ValueRange = { 5.0, 10.0 }
}
},
...
Missing values (NaN) and Values outside the range
Finally, it is also possible to color points outside the specified value range differently compared to the ones within the range, as well as showing missing data points in a specific color.
We refer to our example assets for more details on how to customize these color map settings, but here follows a summary of the properties for controlling what happens outside the data range or for missing values. All of these are optional but can be added to the ColorMapping
table in the asset.
Properties |
Description |
Type |
---|---|---|
|
Show missing data points in a specific color |
|
|
Hide any value that is outside the provided value range |
|
|
Show points with values larger than the max value in the range in a specific color |
|
|
Show points with values below than the min value range in a specific color |
|
* Note that these colors can also be set in the color map file itself. See the page on color map files for more details.
Blending
Per default, the colors of points that overlap are blended additively, resulting in a brighter color in the overlapping areas. This behavior is not always desired, as it might result in distorted colors in the overlaps. To disable the blending, set UseAdditiveBlending
to false
under Coloring
in the asset:
...
Renderable = {
...
Coloring = {
-- Disable additive blending, so that points will be rendered with their actual color.
-- Overlapping points will be sorted by depth
UseAdditiveBlending = false
}
},
...
This means that the point will be rendered without any blending and the point that is closest to the camera will be rendered on top.
Attention
Note disabling the blending does not work very well together with transparency. We therefore recommend using an opacity of 1 for the renderable whenever blending is disabled.
Adding a Texture
A sprite texture (i.e. an image) can be used to decide the shape of the points. To add a texture, simply provide a path to the image you want to use in the asset file:
...
Renderable = {
Type = "RenderablePointCloud",
File = asset.resource("path/to/dataset.csv"),
-- Add a texture to the points (a variety of image formats are supported, not only .png)
Texture = {
File = asset.resource("path/to/texture.png")
}
},
...
The points will look the best with textures that have a transparent background, i.e. with an alpha value of zero. Below is an example of a point cloud with a filled star shape. However, shapes that are transparent in the center (for example, a ring), or have more than one color would work as well. Note however that if you want to apply a color to your point using a color map or fixed color, we recommend using texture in white or bright grayscale.
Textures and Colors
Textures also work with color maps. In that case, the color of the texture is multiplied by that of the color. The same goes if a fixed color is applied to the points. See example below. Note that here we have also disabled the additive blending by setting the UseAdditiveBlending
property to false
, so that the color of overlapping points is not added and blended.
A note about file paths
asset.resource
can be used to specify a relative or full path to a data file. However, it is also possible to use the openspace.absPath
script to use some of the path tokens that are provided with OpenSpace. For example, the cat texture above is a test image that exists in the “OpenSpace/data” folder. To use this in your asset and avoid having to specify a path that is based on your file location, you can use the provided file token to the data folder to get the absolute path to the location of the file: openspace.absPath("${DATA}/test3.jpg")
.
The list of available path tokens and their corresponding locations are found in the openspace.cfg file.
Other Texture Settings
There are some additional settings that might be set related to the texture, like whether image compression should be allowed or if the alpha channel of images should be used (both these settings are enabled per default). For more details, see the documentation of the table parameters for the Texture
table of RenderablePointCloud
on the reference page about that renderable.
Outlines
When rendering points without additive blending, it might be difficult to distinguish the edges of the individual points. In this case, it might be useful to render a distinctly colored outline around the point.
To add an outline to your point cloud, set the Coloring.EnableOutline
setting to true
, and possibly add an outline color and/or width setting. See example:
...
Renderable = {
...
Coloring = {
FixedColor = { 0.5, 0.8, 1.0 }, -- light blue
-- Outline settings
EnableOutline = true,
OutlineColor = { 1.0, 1.0, 1.0 }, -- white
OutlineWidth = 0.1
}
},
...
Outline Style and Color From Color Map
By default, setting the outline will enforce a round shape for the rendered points, but this is not always desired. For example, when a texture is used, a square outline might be more suitable. This is controlled by the OutlineStyle
setting.
It is also possible to set the color of the outline from the color map. In this case, the color map will not be applied to the texture, but only to the outline. See an example in the figure below.
...
Renderable = {
...
Coloring = {
...
EnableOutline = true,
-- Set outline color to the one decided by the color map
ApplyColorMapToOutline = true,
-- Set the style of the outline. Will also affect the shape of the point
OutlineStyle = "Square" -- alternatively "Round" or "Bottom"
}
},
...
Controlling the Point Size
At the core, the size of the points is computed based on one parameter: a logarithmic exponent that decides the absolute size of the point. The exponential component exists to allow creating point clouds over very different scales and distances and should be set to match the scale of the dataset. It is used to compute the actual world scale size of the points, at their position in the 3D scene. For example, an exponent of 3 will lead to points with a size in the order of 1000 meters, while an exponent of 10 will lead to points with a size of about 10^10 = 100’000’000 meters.
If not included in the asset, a default exponent is computed based on the positional information in the dataset. However, this should be seen as a starting point and you will likely want to modify it so that the scale of the points looks good based on the density, number of points and the spread of your particular dataset, as well as the use case for which it is to be shown.
Secondly, a multiplicative factor that can be used to increase or decrease the visual size of the points also exists. This factor can be seen as a tool to quickly increase or decrease the appeared size of all points equally. This factor is applied at the last step, after the exponential scaling and any max size effects.
To update the scaling in the points, add a SizeSettings
table to your asset specification. See example:
...
Renderable = {
Type = "RenderablePointCloud",
File = asset.resource("path/to/dataset.csv"),
SizeSettings = {
-- Control the world-scale size of the points, here in the order of 10^15
ScaleExponent = 15.0,
-- Visually make all the points twice as large
ScaleFactor = 2.0
}
},
...
Note
Since the exponent affects the physical, world-scale, size of the points, this means that points that are closer to the camera will appear larger than those that are further away, and increasing the exponent also enhances this effect. In contrast, increasing the multiplicative factor will increase the visual size of the points equally for all points. This is because it is applied in the very last step, after the exponential scaling and any max size effects.
Limit to a Max Size
In addition to the world-size scale, it is also possible to limit the maximum size the points are allowed to take up of the view. This prevents the points from growing larger than a specified size when the camera is approaching the dataset. An example is shown in the images below.
To limit the maximum size of the points, add the EnableMaxSizeControl
and MaxSize
settings in the SizeSettings
table:
...
Renderable = {
Type = "RenderablePointCloud",
File = asset.resource("path/to/dataset.csv"),
SizeSettings = {
-- Limit the size of the points to a max size
MaxSize = 1.0, -- A value that looks good to you
EnableMaxSizeControl = true
}
},
...
The larger the MaxSize
is, the larger the points are allowed to become when the camera is approaching them. Exactly what the value means is not that important, but if you are interested, see note at the end of this section for an explanation of the meaning behind the value.
At the core, the size of the points is still determined by the exponential scaling. That is, as long as the camera is far enough away so that the points do not exceed the specified max size, the size will still be determined by the provided scale exponent. Also, the multiplicative ScaleFactor
is applied after the max size scaling. That is, if you specify a MaxSize
and a ScaleFactor
of 2, the max size of the points will be twice as large as with a ScaleFactor
of 1.
The MaxSize
value
When computing the maximum allowed size of the points, the MaxSize
value is interpreted as an angle (in degrees) that the point is allowed to take up of the view, from the perspective of the camera. In simplified terms, a value of 1 implies that the point is allowed to take up a maximum of 1 degree of the camera’s field of view.
Scale Based on Data
It is also possible to do some simple scaling based on data parameters. The size of the points is then scaled based on the value in the data column.
Similarly to the color mapping, the size mapping is added by specifying a list of options that can be used for setting the size of the points:
...
Renderable = {
Type = "RenderablePointCloud",
File = asset.resource("path/to/dataset.csv"),
SizeSettings = {
-- The options for the columns that the points can be scaled by. The first
-- alternative is chosen per default
SizeMapping = { "one parameter", "another parameter" }
}
},
...
Attention
The size mapping is currently a bit of an experimental feature. For now, the point size if directly multiplied with the data value is directly multiplied by the data value of the chosen SizeMapping
column. This is not always suitable though, depending on the range of the data values. The behavior may be subject to change in the future.
Summary of Final Size Computation
In summary, the order in which the settings affect the size of the points is the following:
10 ^
ScaleExponent
* Scale From Data => world scale sizeLimit point size to max size => prevent the points from growing larger than a certain size in view
Finally, multiply with
ScaleFactor
to increase or decrease the size, onscreen
Fading
A point cloud can also be set up so that it fades in and out based on the distance to the camera. The distance is computed based on the origin of the dataset. This can be useful when you want to reduce the visual overload of points when multiple datasets are shown, or when you want to show different datasets depending on the distance. For example, in the Default scene, we use it to make different datasets about the Universe appear and disappear depending on your distance to our Solar System.
To configure the fading for the point cloud, specify the distance over which the fading should occur in the asset file:
...
Renderable = {
Type = "RenderablePointCloud",
File = asset.resource("path/to/dataset.csv"),
Fading = {
-- Control at what distance the points fade in. The points will be *invisible*
-- when the camera is closer than the first value, and fully visible when the
-- camera is further away then the last value. In-between they will linearly
-- fade in or out
FadeInDistances = { 100, 1000 },
-- Optionally, invert the fading. If invert is set to true, the fading is
-- inverted so that the points are *visible* when the camera is closer than
-- the first distance and invisble when further away than the second
Invert = false
}
},
...
Note
The fading distances are specified in the same unit that is used to render the points. So if a Unit
is set, the
distances should match that unit.
Orientation
Per default, the planes that make up the points will be oriented to face the camera’s view direction in a way that is suited for planar displays. However, for non-planar displays, like domes or planetariums, this is not always appropriate. In this case, it is usually more suitable to orient the points to face the position of the audience. To accommodate for this, the renderable includes a few different options for the orientation of the points:
Orientation mode |
Description |
---|---|
|
Orient planes to face the view direction of the camera. A billboarded mode that is suitable for planar displays. |
|
Orient planes to face the position of the camera. A billboarded mode that is suitable for spherical displays. |
|
Apply no specific camera-based orientation to the planes. The planes will have a fixed orientation in space. It is also possible to set the orientation from the dataset. See this page for more details on that. |
The schematic image below illustrates the resulting orientation for the first two modes. The left image shows planes orientated using the Camera View Direction
option, and the right shows planes oriented to face the camera position using the Camera Position Normal
option. Note how the latter lays out the points in a spherical way around the camera, which works better for spherical displays.
And this is how the orientation is set from an asset file:
...
Renderable = {
...
-- Set the orientation render option to face the camera position instead of the
-- view direction, which is default
OrientationRenderOption = "Camera Position Normal"
},
...
Specializations of RenderablePointCloud
There are also other specializations of the RenderablePointCloud type, that add one or more specialized features for the points. The RenderablePointCloud type renderable should however be sufficient for most use cases.
Renderable type |
Description |
---|---|
A point cloud where each point is represented by a dynamically created uniform polygon (such as a triangle, hexagon, octagon, etc.). The number of sides of the polygon is configured in the asset. |
|
A point cloud that supports interpolation between a number of sets of positions. |