Offscreen rendering


Visualize supports rendering the scene to an offscreen window. This is accomplished using an offscreen image buffer, and HPS::OffScreenWindowKey serves as a high-level handle to the buffer. It allows you to inspect or manipulate the image data without the user seeing it rendered on the screen.

After you have the key, simply include a root segment as you would with any normal window. Once a segment is included by a window key, its entire hierarchy will enter the rendering pipeline for that window.

If you want to choose a specific driver interface to use with the window you create, you must use an HPS::OffScreenWindowOptionsKit to specify it. If you do not specify a driver interface, Visualize will default to the HPS::Window::Driver::Default3D interface. Whatever hardware is used to draw into a conventional on-screen window is also used to draw into a memory buffer, so the scene should be identical in both contexts (Visualize will not use a software mode to do any offscreen rendering).

oswok.SetDriver(HPS.Window.Driver.OpenGL2);
HPS.OffScreenWindowKey offscreenWindowKey = HPS.Database.CreateOffScreenWindow(512, 512, oswok);
offscreenWindowKey.IncludeSegment(mySegmentKey);



The segment hierarchy in an offscreen window behaves the same way as a hierarchy in a normal window. So, you'll have to set all your cameras, lighting, and other attributes in the same way. If you need to render an existing scene into an offscreen window, it is recommended that you include the existing scene's root segment into the offscreen window using an include segment rather than trying to duplicate the scene by rebuilding it. Using an include segment saves memory, time, and all changes to the existing scene are automatically reflected in the other window.

Rendering to an offscreen window with a transparent background

It may be desirable to render images with a transparent background. This is only supported when the render target is an offscreen window.

To make the window background transparent, set the opacity level in the window options kit before creating the window:

// completely transparent window background
oswok.SetOpacity(0.0f);
// 50% transparent window background
oswok.SetOpacity(0.5f);
// no explicity transparency, but preserve alpha
oswok.SetOpacity(1.0f);
// completely transparent window background
oswok.SetOpacity(0.0f);
// 50% transparent window background
oswok.SetOpacity(0.5f);
// no explicity transparency, but preserve alpha
oswok.SetOpacity(1.0f);



Limitations: Rendering to a transparent background is only supported in the 3D shader drivers. Additionally, bloom is not supported.

Accessing the offscreen image buffer

Before accessing the image data, you have to make sure the rendering is complete. Normally, you would call HPS::WindowKey::Update to render a scene. However, due to the multithreaded nature of Visualize, the rendering may not be finished by the time you try to use the image. Therefore, it is appropriate to wait for the image to complete using an HPS::UpdateNotifier:

HPS::UpdateNotifier notifier = offscreenWindowKey.UpdateWithNotifier();
notifier.Wait();
// when you get to this point, the scene is rendered
HPS.UpdateNotifier notifier = offscreenWindowKey.UpdateWithNotifier();
notifier.Wait();
// when you get to this point, the scene is rendered



The raw image data is accessed through an HPS::ImageKit. Once you have the kit, you can manipulate the data in many ways. For example, you can write the image to a file, use it as a texture, or merely inspect it by iterating over its array. This code sample demonstrates how to write the image to a PNG:

HPS::ImageKit imageKit;
// fills the ImageKit with the contents of the offscreen window
ByteArray imageData;
// gets the data out of the kit and into a buffer for writing
imageKit.ShowData(imageData);
// at this point, you can use the data in 'imageData' any way you prefer
HPS.ImageKit imageKit;
HPS.OffScreenWindowOptionsControl oswoc = offscreenWindowKey.GetWindowOptionsControl();
// fills the ImageKit with the contents of the offscreen window
oswoc.ShowImage(HPS.Image.Format.RGB, out imageKit);
Byte[] imageData;
// gets the data out of the kit and into a buffer for writing
imageKit.ShowData(out imageData);
// at this point, you can use the data in 'imageData' any way you prefer



Alternatively, you may want to modify part of the image data before doing something else with it. In that case, you can get the raw image as 24-bit RGB data using an HPS::ImageKit with an HPS::OffScreenWindowOptionsControl:

HPS::ImageKit imageKit;
// fills the ImageKit with the contents of the offscreen window
oswoc.ShowImage(Image::Format::RGB, imageKit);
ByteArray imageData;
// gets the data out of the kit and into a buffer for modification
imageKit.ShowData(imageData);
// ...
// manipulate data
// ...
imageKit.SetData(imageData); // modified data reinserted into the ImageKit for display
HPS.ImageKit imageKit;
HPS.OffScreenWindowKey offscreenWindowKey = HPS.Database.CreateOffScreenWindow(800, 600);
HPS.OffScreenWindowOptionsControl oswoc = offscreenWindowKey.GetWindowOptionsControl();
// fills the ImageKit with the contents of the offscreen window
oswoc.ShowImage(HPS.Image.Format.RGB, out imageKit);
Byte[] imageData;
// gets the data out of the kit and into a buffer for modification
imageKit.ShowData(out imageData);
// ...
// manipulate data
// ...
imageKit.SetData(imageData); // modified data reinserted into the ImageKit for display



Making screenshots

It is easy to make a screenshot by rendering your scene into an HPS::OffscreenWindowKey and then showing the rendered image into an HPS::ImageKit as described above. However, if you don't need to inspect or alter the image data, there are more convenient and less memory-intensive ways of doing this. See our sections on rendered screenshots and file import screenshots for instructions on using alternate methods.

Rendering to a texture

It is possible to associate an offscreen window directly with an image definition. When doing so, rendering to the offscreen window will automatically update the image definition with new data. The image definiton can then be used as a texture, a window background, or exported for other uses. If you want to render to a texture, you must make the association between the offscreen window and the target image definition at the time you create the window.

NOTE: While there are other ways of rendering to a texture, the method described here is recommended because it writes to GPU memory. Other methods that do not use the HPS::ImageDefinition target will render the image to main memory before transferring it to GPU memory. This incurs a performance penalty when continuously updating the render target.

HPS::OffScreenWindowKey owc = HPS::Database::CreateOffScreenWindow(myImageDefinition, myOptionsKit);
// be sure to wait for the image to render before using it!
HPS::UpdateNotifier updateNotifier = owc.UpdateWithNotifier();
updateNotifier.Wait();
OffscreenWindowKey owc = Database.CreateOffScreenWindow(myImageDefinition);
// be sure to wait for the image to render before using it!
UpdateNotifier updateNotifier = owc.UpdateWithNotifier();
updateNotifier.Wait();



Assuming you have the image definition already created, the last step is to apply it to a piece of geometry as a texture. High-level steps are shown below:

HPS::TextureOptionsKit textureOptionsKit;
// see the table below about parameterization sources
// makes the image into a texture
portfolioKey.DefineTexture("my_texture", imageDefinition, textureOptionsKit);
// ... [set vertex parameters]
mySegmentKey.GetMaterialMappingControl().SetFaceTexture("my_texture");
myWindowKey.Update();
HPS.TextureOptionsKit textureOptionsKit = new HPS.TextureOptionsKit();
// see the table below about parameterization sources
// makes the image into a texture
portfolioKey.DefineTexture("my_texture", myImageDefinition, textureOptionsKit);
// ... [set vertex parameters]
mySegmentKey.GetMaterialMappingControl().SetFaceTexture("my_texture");
myWindowKey.Update();


Complete instructions on creating an image definition and applying it as a texture can be found in here.

Hardcopy

HOOPS Visualize offers the ability to print (see limitations) as well as export to PostScript and 2D PDF. Collectively, these are referred to as "hardcopy". HPS::Hardcopy is an extensible class for writing hardcopy versions of your model. The HPS::Hardcopy class has one purpose - to reproduce your scene as accurately as possible in the output medium.

Hardcopy works by rendering the faces in your model to an image in one pass, then doing a hidden line rendering over that image in the second pass. Text, lines, edges, curves, and other vector data are drawn in the second pass. This results in output which can be efficiently handled by a printer.

The background image, generated in the first pass, can be extremely large. For example, an 8.5" x 11" image at 600 dpi results in 128 MB of data. Visualize can't render an image that large, so HPS::Hardcopy divides that image up into smaller parts which it can handle. The smaller parts are compressed, written to temporary files, then reassembled and cropped before being sent to the output file. This enables HPS::Hardcopy to make arbitrarily large output files.

When printing, you must provide the window key from your scene hierarchy to HPS::Hardcopy. Additionally, there is an HPS::Hardcopy::File::ExportOptionsKit through which you can set size and resolution. For example:

try
{
float width, height;
myWindowKey.GetWindowInfoControl().ShowPhysicalSize(width, height);
exportOptionsKit.SetWYSIWYG(true);
exportOptionsKit.SetSize(width, height, HPS::Hardcopy::SizeUnits::Centimeters);
// export to PDF
HPS::IOResult PDFResult =
HPS::Hardcopy::File::Export("output.pdf", HPS::Hardcopy::File::Driver::PDF, myWindowKey, exportOptionsKit);
// export to Postscript
HPS::IOResult postScriptResult =
HPS::Hardcopy::File::Export("output.ps", HPS::Hardcopy::File::Driver::Postscript, myWindowKey, exportOptionsKit);
}
{
// handle Hardcopy exception
}
try
{
HPS.Hardcopy.File.ExportOptionsKit exportOptionsKit = new HPS.Hardcopy.File.ExportOptionsKit();
float width, height;
myWindowKey.GetWindowInfoControl().ShowPhysicalSize(out width, out height);
exportOptionsKit.SetWYSIWYG(true);
exportOptionsKit.SetSize(width, height, HPS.Hardcopy.SizeUnits.Centimeters);
exportOptionsKit.SetResolution(100, HPS.Hardcopy.ResolutionUnits.DPCM);
// export to PDF
HPS.IOResult PDFResult =
HPS.Hardcopy.File.Export("output.pdf", HPS.Hardcopy.File.Driver.PDF, myWindowKey, exportOptionsKit);
// export to Postscript
HPS.IOResult postScriptResult =
HPS.Hardcopy.File.Export("output.ps", HPS.Hardcopy.File.Driver.Postscript, myWindowKey, exportOptionsKit);
}
{
// handle Hardcopy exception
}


By default, HOOPS Visualize will print your scene with a white background. However, this can be controlled by using the HPS::Hardcopy::File::ExportOptionsKit::SetBackgroundPreference() function.

Printing to scale

In addition to the printing method described above, HOOPS Visualize also offers the ability to print to scale. HOOPS Visualize operates in a unitless measurement system. For example, if you insert a line of length 2, its length is not expressed in inches, centimeters or any other unit. '2' is simply a length which is relative to the size of other geometry in the scene - it does not correspond to any real unit of measurement.

Printing to scale allows you to assign a real-world unit value to Visualize's internal unitless measurements for the purpose of printing or exporting to PDF and Postscript. To enable print-to-scale, simply set a positive value for the scale:

exportOptionsKit.SetScale(2); // enables print-to-scale with a scale factor of 2
exportOptionsKit.UnsetScale(); // disables print-to-scale

As an example, if you wanted to export a model to PDF, you would first set the scale, and specify the output context in the export function:

exportOptionsKit.SetScale(2);
HPS::Hardcopy::File::Export("foo", Driver::PDF, window, exportOptionsKit); // this print is to scale

In the hardcopy output, each HOOPS Visualize internal unit would correspond to two real-world inches. Therefore, if line of length 2 was inserted in a Visualize scene, it will have a real-world length of 4 inches when printed or exported to PDF.

A side effect of printing to scale is that the printed output will not necessarily match what is visible on screen. The output will be scaled up or down and given a new aspect ratio so that it will fill the printable area of the specified output device (i.e. a printer or the size of the output file as specified in the export options kit), with the scene centered around the camera target.

Please note that the print to scale feature is not defined for a perspective camera. If the user tries to print to scale with a perspective camera, the camera projection will be temporarily changed to orthographic while the print takes place. When setting a scale, the scale value needs to be a positive number. If the user tries to set a zero or negative number, a warning will be generated and the request will be ignored. Print to scale is disabled by default.

Hardcopy limitations

Printing directly to paper only works in Windows C++ applications, and is not currently supported for C# applications. This is because printing to paper uses the Windows GDI. One of the main differences is you must use the options kit HPS::Hardcopy::GDI::ExportOptionsKit instead of a HPS::Hardcopy::File::ExportOptionsKit.

void Cmfc_simpleView::OnPrint(CDC* pDC, CPrintInfo* pInfo)
{
Hardcopy::GDI::ExportOptionsKit options;
Hardcopy::GDI::Export(
(long)pDC->m_hDC,
(long)pDC->m_hAttribDC,
_canvas.GetWindowKey(),
options);
}


On Linux and OS X, printing is a two-step process. First, export your scene to a 2D PDF or Postscript file. Next, you must make your own calls to CUPS (or the printing API of your choice) in order to send the data to the printer.