Skip to main content

Rendering Engine

A RenderingEngine allows the user to create Viewports, associate these Viewports with onscreen HTML elements, and render data to these elements using an offscreen WebGL canvas.

It should be noted that RenderingEngine is capable of rendering multiple viewports, and you don't need to create multiple engines. However, multiple RenderingEngine instances can be created, e.g., if you wish to have a multiple monitor setup, and use a separate WebGL context to render each monitor’s viewports.

In Cornerstone3D we have built the RenderingEngine from ground up, and we are utilizing vtk.js as the backbone of the rendering. vtk.js is a 3D rendering library capable of using WebGL for GPU-accelerated rendering.

OnScreen and Offscreen Rendering

Previously in Cornerstone (legacy), we processed data in each viewport with a WebGL canvas. This doesn't scale well, as the number of viewports increases and for complex imaging use cases (e.g., synced viewports), we will end up with lots of updates to onscreen canvases and performance degrades as the number of viewports increases.

In Cornerstone3D, we process data in an offscreen canvas. This means that we have a big invisible canvas (offscreen) that includes all the onscreen canvases inside itself. As the user manipulates the data, the corresponding pixels in the offscreen canvas get updated, and at render time, we copy from offscreen to onscreen for each viewport. Since the copying process is much faster than re-rendering each viewport upon manipulation, we have addressed the performance degradation problem.

Shared Volume Mappers

vtk.js provides standard rendering functionalities which we use for rendering. In addition, in Cornerstone3D we have introduced Shared Volume Mappers to enable re-using the texture for any viewport that might need it without duplicating the data.

For instance for PET-CT fusion which has 3x3 layout which includes CT (Axial, Sagittal, Coronal), PET (Axial, Sagittal, Coronal) and Fusion (Axial, Sagittal, Coronal), we create two volume mappers for CT and PET individually, and for the Fusion viewports we re-use both created textures instead of re-creating a new one.

General usage

After creating a renderingEngine, we can assign viewports to it for rendering. There are two main approach for creating Stack or Volume viewports which we will discuss now.

Instantiating a RenderingEngine

You can instantiate a RenderingEngine by calling the new RenderingEngine() method.

import { RenderingEngine } from '@cornerstonejs/core';

const renderingEngineId = 'myEngine';
const renderingEngine = new RenderingEngine(renderingEngineId);

Viewport Creation

You can then use two methods to create viewports: setViewports or enable/disable APIs. For both methods, a ViewportInput object is passed as an argument.

PublicViewportInput = {
/** HTML element in the DOM */
element: HTMLDivElement
/** unique id for the viewport in the renderingEngine */
viewportId: string
/** type of the viewport VolumeViewport or StackViewport*/
type: ViewportType
/** options for the viewport */
defaultOptions: ViewportInputOptions
}

setViewports API

setViewports method is suitable for creation of a set of viewports at once. After setting the array of viewports, the renderingEngine will adapt its offScreen canvas size to the size of the provided canvases, and triggers the corresponding events.

const viewportInput = [
// CT Volume Viewport - Axial
{
viewportId: 'ctAxial',
type: ViewportType.ORTHOGRAPHIC,
element: htmlElement1,
defaultOptions: {
orientation: Enums.OrientationAxis.AXIAL,
},
},
// CT Volume Viewport - Sagittal
{
viewportId: 'ctSagittal',
type: ViewportType.ORTHOGRAPHIC,
element: htmlElement2,
defaultOptions: {
orientation: Enums.OrientationAxis.SAGITTAL,
},
},
// CT Axial Stack Viewport
{
viewportId: 'ctStack',
type: ViewportType.STACK,
element: htmlElement3,
defaultOptions: {
orientation: Enums.OrientationAxis.AXIAL,
},
},
];

renderingEngine.setViewports(viewportInput);

Enable/Disable API

For having a full control over enabling/disabling each viewport separately, you can use the enableElement and disableElement API. After enabling the element, renderingEngine adapts its size and state with the new element.

const viewport = {
viewportId: 'ctAxial',
type: ViewportType.ORTHOGRAPHIC,
element: element1,
defaultOptions: {
orientation: Enums.OrientationAxis.AXIAL,
},
};

renderingEngine.enableElement(viewport);

You can disable any viewport by using its viewportId, after disabling, renderingEngine will resize its offScreen canvas.

renderingEngine.disableElement(viewportId: string)