While under development, the Insight3D development team wrote a series of blog posts, which are now archived here to provide in-depth technical information about some of the underlying techniques and design decisions. These posts reflect the work-in-progress state of the product at the time.
The following blog post was originally published on the Insight3D blog on January 18, 2008 by Patrick Cozzi. At the time of publishing, Insight3D was referred to by its development code-name, "Point Break".
In Point Break, primitives form the building blocks of a 3D scene. Developers create primitives and initialize them with information, such as position and attitude. Primitives then take care of all the 3D rendering (drawing) and optimizations, allowing developers to focus on their application code.
An abridged set of primitives planned for Point Break include:
Markers - 2D images that always face the viewer and remain a constant pixel size. Markers are commonly used to visualize a large number of tracks moving in real-time.
Polylines - Used for rendering lines on the ground or in space. Polylines are used to visualize many things including country borders, drop lines, range rings, and access lines. Polyline variants can conform to terrain when drawn on the ground.
Triangle Meshes - The rendering workhorse for things such as area targets (e.g. states or countries), terrain and imagery extents, ground ellipses, and border walls. The surface mesh, a triangle mesh variant, can conform to terrain when drawn on the ground.
Other primitives, including text and points, are also planned.
Once created, primitives can change position and attitude over time. For example, an orbit's ground track, computed using DGL, can be visualized in Point Break by creating a polyline primitive. Real-time feeds can be visualized using model primitives.
Three major paradigms are used throughout primitives:
Unlike STK's Connect-based primitives, high level primitives, such as walls, ground ellipses, great arcs, and rhumb lines, do not exist in the lowest levels of Point Break. Instead, generic primitives, such as triangle meshes and polylines, provide visualization for a wide array of higher level constructs. Objects that compute vertices are input to these primitives. In the following C# example, a rectangular extent on the globe is computed then used as input to the generic triangle mesh.
IAgGxTriangulatorSurfaceExtent extent = new AgGxTriangulatorSurfaceExtent(); // [west, south, east, north] in radians extent.Initialize(earth, 0, 0, 0.017, 0.017); IAgGx3dPrimitiveTriangleMesh mesh = new AgGx3dPrimitiveTriangleMesh(); mesh.InitializeFromTriangulator(earth, AgEGxReferenceFrame.eGxReferenceFrameFixed, AgEGxMovement.eGxMovementStationary, extent); primitives.Add(mesh);
Point Break supports stationary primitives, i.e. a model representing a ground station, and moving primitives, i.e. an aircraft rapidly changing position and attitude over time. To provide high frame rates, primitives are rendered using many real-time rendering optimizations including:
Hierarchical view frustum culling
Sorting and batching
Level of detail
Hierarchical view frustum culling quickly discards primitives that will not be visible during a given frame, saving both CPU and graphics processing unit (GPU) resources.
Sorting and batching allow primitives to efficiently use the GPU. For example, primitives can be sorted by texture to minimize front side bus traffic.
With level of detail, primitives farther away from the viewer render with less detail. Less detail can mean less geometry, lower resolution textures or many other things. Since the number of pixels the primitive occupies on the screen is minimal, this results in less work for the GPU with almost no loss in visual quality.
These optimizations are largely performed behind the scenes. Developers using primitives only need to provide simple hints, for example, whether a primitive is stationary or if it will move.
In addition to the primitives that come with Point Break, developers will be able to implement their own primitives. For example, a primitive to efficiently render cities could be implemented as a primitive plugin.
Although some graphics experience will help, developers do not need to be fluent in OpenGL to write a primitive. We've abstracted OpenGL into a renderer interface, which is used to interact with the GPU. Major components of the renderer include vertex buffers, textures, and state blocks. By the time Point Break is released, the renderer may also contain shaders and frame buffers.
In addition to providing a simple, object-oriented interface to the GPU, the renderer provides optimizations including rearranging triangles for the GPU's vertex caches, choosing optimal vertex layouts, and minimizing state changes, such as color and line width.
To implement a primitive plugin, a developer writes a class that implements the primitive interface. The Render() method is called when the primitive is deemed visible and is ready to be rendered. It is in this method that the renderer interface is used to interact with the GPU.
In closing, the goal of primitives is to provide flexible, efficient objects to visualize both static and dynamic data. In follow-up entries, I will discuss the rationale behind many of the design decisions.