Skip to content

Ogre Backend

Jeremiah van Oosten edited this page Oct 19, 2026 · 3 revisions

Ogre backend

The Ogre backend (vgl/ogre.hpp, CMake target vgl::ogre, sources in backends/ogre) puts the globe into a classic Ogre 14 scene as an Ogre MovableObject, vgl::ogre::globe. This page covers the supported Ogre versions and render systems, enabling it with VGL_BACKEND_OGRE, how Ogre is found or downloaded, creating the globe and attaching it to a scene, its materials and unified shaders, render queue groups, the depth conventions including reversed-Z in Ogre, the Ogre sample, and installing and consuming vgl::ogre. Like the other backends it is a consumer of the render interface, using vgl through the C API only; its own interface is C++, because Ogre's is.

Contents

Status and supported render systems

Item Status
Ogre line Ogre 14, the classic line (not Ogre-Next). The design settled on 14.6.0; the build downloads 14.6.1 when no Ogre is installed
OpenGL 3+ render system Supported on Windows and Linux, checked by the sample in CI (Mesa under Xvfb)
Direct3D 11 render system Supported on Windows, checked by the sample
Vulkan render system After version 1 (milestone M3b)
macOS (Metal) After version 1 (milestone M3b)
OpenGL ES 3 and Metal, Android and iOS After version 1 (milestone M3b)

Version 1 supports the Ogre backend on OpenGL 3+ and Direct3D 11, on Windows and Linux only.

The shaders are written with Ogre's unified shader macros, so they are meant to compile for GLSL, GLSL ES, HLSL, Vulkan's GLSL and Metal; the remaining work for the other render systems is mostly build and CI. Like the C API, the Ogre interface stays compatible within major version 1.

Enabling and building

The backend is off by default, because building Ogre takes a while. Turn it on when configuring:

cmake --preset linux-gcc-shared -DVGL_BACKEND_OGRE=ON
cmake --build --preset linux-gcc-shared-release --target vgl_ogre_sample
cmake --preset windows-msvc-static -DVGL_BACKEND_OGRE=ON
cmake --build --preset windows-msvc-static-release

How Ogre is found, in backends/ogre/CMakeLists.txt:

  1. An installed Ogre 14, if find_package(OGRE 14 CONFIG) finds one: point OGRE_DIR at its CMake directory or add its prefix to CMAKE_PREFIX_PATH. Configure prints vgl: using installed Ogre <version>.
  2. Otherwise, with VGL_FETCH_DEPENDENCIES (on by default), Ogre 14.6.1 is downloaded from GitHub at a pinned SHA-256 and built statically inside vgl's build, with only what the backend and its sample need: the OpenGL 3+ render system, the Direct3D 11 render system on Windows, the RTShader system, Bites, the overlay with Dear ImGui (vgl's own pinned copy) and the stb image codec. Ogre's own dependency download is off; none of it is needed. Configure prints vgl: using downloaded Ogre 14.6.1, built statically.
  3. Otherwise configure warns "Ogre 14 was not found and VGL_FETCH_DEPENDENCIES is off; skipping the Ogre backend".

vgl_ogre is always a static library, whatever BUILD_SHARED_LIBS says, so that it shares your Ogre instead of holding a second copy of Ogre's singletons. It links OgreMain publicly. Unlike the core and the other backends it is compiled with exceptions, because Ogre throws; it catches them at its interface, so no exception crosses into your code, and failures come back as vgl::result values with a message in the context's log.

Installing and consuming vgl::ogre

vgl::ogre is installed (and exported in vgl's CMake package) only when the backend was built against an installed Ogre. A downloaded Ogre is part of vgl's build tree, not installed, so the package could not name it. With an installed Ogre, the package's config calls find_dependency(OGRE 14 CONFIG), so consumers find Ogre the way vgl's build did:

find_package(vgl CONFIG REQUIRED)
target_link_libraries(my_app PRIVATE vgl::vgl vgl::ogre)
Route Ogre backend
Source build, installed Ogre Built and installed as vgl::ogre
Source build, downloaded Ogre Built and usable inside the same build (add_subdirectory), not installed
vcpkg port (packaging/vcpkg/ports/vgl) Feature ogre, which depends on vcpkg's ogre port: vcpkg install vgl[ogre] --overlay-ports=<vgl>/packaging/vcpkg/ports
Conan recipe (packaging/conan) Not available: ConanCenter has no Ogre 14, so the recipe leaves the backend out

The release archives leave the Ogre backend out; until the port and recipe are in the vcpkg registry and ConanCenter, use them from the repository as above. See Installing vgl and packaging/README.md.

When embedding vgl with add_subdirectory, set VGL_BACKEND_OGRE before adding it; vgl then looks for Ogre with find_package(OGRE 14 CONFIG) itself.

Quick start

#include <vgl/ogre.hpp>
#include <vgl/vgl.hpp>

// After Ogre::Root::initialise (the globe needs a render system), with Ogre's Media/Main in
// the resource group the globe uses:
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
    ogre_media_dir + "/Main", "FileSystem", Ogre::RGN_INTERNAL);
Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();

vgl::ogre::globe_create_info info;
info.depth_convention = VGL_DEPTH_CONVENTION_LOGARITHMIC;
info.resource_group = Ogre::RGN_INTERNAL;
vgl::expected<vgl::ogre::globe*> globe = vgl::ogre::globe::create(context, info);
if (!globe)
{
    std::fprintf(stderr, "globe: %s\n", vgl::result_name_get(globe.error()));
}
// At the origin, which is ECEF's, unrotated and unscaled.
scene_manager->getRootSceneNode()->attachObject(*globe);

// Each frame:
static_cast<void>(context.view_set(&view));  // view.view_matrix: ECEF to camera, in double
static_cast<void>(context.update());
camera->setAspectRatio(static_cast<Ogre::Real>(aspect)); // match the projection in `view`
vgl::ogre::globe_settings settings;
settings.has_view_matrix = true;
settings.view_matrix = view.view_matrix;
settings.sun_direction = sun;                      // ECEF, optional
settings.features = VGL_DRAW_FEATURE_ATMOSPHERE;
if ((*globe)->settings_set(settings) != vgl::result::success)
{
    // invalid settings; the previous ones stay
}
root->renderOneFrame();

// At the end, before the vgl context and before Ogre's render system goes:
scene_manager->getRootSceneNode()->detachObject(*globe);
vgl::ogre::globe::destroy(*globe);

The globe object

vgl::ogre::globe derives from Ogre::MovableObject (movable type "vgl_globe"). Create it with one of:

static vgl::expected<globe*> globe::create(vgl::context& context, const globe_create_info& info) noexcept;
static vgl::expected<globe*> globe::create(vgl_context* context, const globe_create_info& info) noexcept;

globe_create_info:

Field Meaning Default
depth_convention How depth is written and compared VGL_DEPTH_CONVENTION_LOGARITHMIC
resource_group The resource group of the globe's programs, materials, buffers and textures. It must see Ogre's Media/Main Ogre::RGN_DEFAULT
name Ogre's name for the globe, unique among movable objects; also the prefix of its resources' names "vgl/globe"

Creation must come after Ogre::Root::initialise, since it builds programs on the current render system. It picks the first shading language the render system supports among hlsl, glsl, glsles, glslang and metal, compiles the terrain, sky and stars programs, creates the materials, and tells the context it samples RGBA8. It fails with:

Result Cause
invalid_argument Null context, or an unknown depth convention
not_supported Reversed-Z without the render system's "Reversed Z-Buffer" option, or a render system that compiles none of the shading languages
backend_error Ogre failed: no render system yet, or a program failed to compile (Ogre's log has the error)
out_of_memory Allocation failed

The context's log says which. The context must outlive the globe, and the globe uses the context's allocator for its own records (Ogre's objects go through Ogre).

Other members:

Member Purpose
static void destroy(globe* g) noexcept Destroys it; detach it from its scene node first. Null does nothing. Resets the context's render resources, so a later consumer gets them all again
vgl::result settings_set(const globe_settings&) noexcept How it draws from the next frame on
vgl::result stars_set(std::span<const vgl_star>) noexcept The stars for VGL_DRAW_FEATURE_STARS (Atmosphere, Stars and Clouds)
vgl::result clouds_set(const std::uint8_t* coverage, std::uint32_t width, std::uint32_t height) noexcept The cloud coverage for VGL_DRAW_FEATURE_CLOUDS; null with 0 x 0 removes it
void device_reset() noexcept Forgets its Ogre objects and the context's render resources after Ogre lost its device

Its bounds are infinite (getBoundingBox returns BOX_INFINITE), so Ogre never culls it; the core culls the tiles. It casts no shadows. Call its functions on Ogre's render thread, one at a time with the calls on its context: in particular, vgl_context_update must not run while Ogre renders.

How a frame works

The globe runs one frame of the render interface in _updateRenderQueue, once per Ogre frame (keyed on Ogre::Root::getNextFrameNumber): it applies the resource lists (index lists become HardwareIndexBuffers, meshes HardwareVertexBuffers with the vertex layout of the render interface plus a samples texture, imagery textures with their mip chains, all through Ogre's managers), fills a pool of renderables from the draw list, and ends the frame before Ogre draws. Further viewports in the same Ogre frame reuse the renderables, all drawn from the same view. Each mesh has its own material, cloned from the globe's, whose texture units hold its samples, its draw item's layers and the clouds: Ogre binds textures by material, not by renderable.

Settings per frame

globe_settings takes effect from the next frame on. settings_set checks it and keeps the previous settings on invalid_argument (a non-finite matrix or direction, unknown feature or debug bits, or a negative or non-finite distance, interval or height).

Field Meaning Default
has_view_matrix, view_matrix The camera to draw from, in double precision, when it differs from the context's view false: the context's view
sun_direction Towards the sun along the ECEF axes Zero: light from the camera (with the atmosphere, a sun above the camera)
logarithmic_far Far distance of the logarithmic convention, metres 0: VGL_LOGARITHMIC_DEPTH_FAR_DEFAULT
upload_budget Bytes of resources to upload per frame 0: VGL_UPLOAD_BUDGET_DEFAULT
features vgl_draw_feature bits 0
debug_views vgl_debug_view bits 0
earth_rotation_angle Radians, for the stars 0
contour_interval Metres 0: 500
cloud_height Metres above the ellipsoid 0: 6000

The shading, features and debug views are the OpenGL backend's (OpenGL Backend).

Camera and transforms

Ogre holds its camera's view in single precision, which is too coarse at the Earth's radius. The globe therefore does not use it:

  • Each tile renderable reports the draw item's model_view (composed by the core in double precision, from the context's view or globe_settings::view_matrix) as its world transform, with setUseIdentityView(true), so Ogre multiplies only its camera's projection. No OGRE_DOUBLE_PRECISION build is needed.
  • The renderables' transforms replace the scene node's, so the node's transform has no effect; keep the globe on a node at the origin, unrotated and unscaled, as the header asks.
  • Keep Ogre's camera's projection matching the one you give the context: the same vertical field of view (setFOVy), aspect ratio (setAspectRatio) and suitable clip distances for the convention. Tile selection uses the context's view; drawing uses Ogre's projection.
  • Your own scene objects are still drawn with Ogre's camera view. If you place them in ECEF near the globe, mind Ogre's single precision there too (for example, keep them relative to a local origin near the camera).

The sample sets the camera like this:

void camera_set(Ogre::Camera* camera, vgl_depth_convention depth, double altitude, double aspect)
{
    camera->setAspectRatio(static_cast<Ogre::Real>(aspect));
    switch (depth)
    {
    case VGL_DEPTH_CONVENTION_REVERSED_Z:
        camera->setNearClipDistance(1.0f);
        camera->setFarClipDistance(0.0f); // infinite
        break;
    case VGL_DEPTH_CONVENTION_CONVENTIONAL:
        camera->setNearClipDistance(static_cast<Ogre::Real>(std::max(1.0, altitude * 0.05)));
        camera->setFarClipDistance(static_cast<Ogre::Real>(altitude + 2.0e7));
        break;
    default: // logarithmic: the planes do not matter
        camera->setNearClipDistance(1.0f);
        camera->setFarClipDistance(1.0e9f);
        break;
    }
}

Depth conventions and reversed-Z in Ogre

Convention Ogre setup
Logarithmic (default) Nothing special: the shaders write depth from the camera distance and move the clip-space depth onto the same curve, for OpenGL (-w to w) and Direct3D (0 to w) alike
Reversed-Z Set the render system's "Reversed Z-Buffer" option to "Yes" before Root::initialise; Ogre then applies it to its projections and depth tests itself. Use a far clip distance of 0 (infinite). globe::create fails with not_supported without the option
Conventional Ogre's default; small scenes only
Ogre::RenderSystem* rs = root->getRenderSystemByName("OpenGL 3+ Rendering Subsystem");
// or "Direct3D11 Rendering Subsystem"
root->setRenderSystem(rs);
rs->setConfigOption("Reversed Z-Buffer", "Yes");
root->initialise(false);

Render windows keep a 24-bit integer depth buffer even with reversed-Z, which is too coarse at planetary scale; Ogre gives render textures a 32-bit floating-point depth buffer instead. So for reversed-Z, draw the scene into a render texture the size of the window and show it, as the sample does, or use the logarithmic convention:

Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().createManual(
    "vgl/scene", Ogre::RGN_INTERNAL, Ogre::TEX_TYPE_2D, width, height, 0,
    Ogre::PF_BYTE_RGBA, Ogre::TU_RENDERTARGET);
Ogre::Viewport* viewport = texture->getBuffer()->getRenderTarget()->addViewport(camera);
// Show `texture` in the window with a screen-space rectangle in a second scene manager.

Because reversed-Z is a render-system option, switching conventions means a new Ogre root, which is what the sample's check does for each convention.

Materials and unified shaders

The globe creates its programs and materials in code, so you ship no vgl resource files. The shaders (backends/ogre/shaders.hpp) are one source per stage, written with Ogre's unified shader macros (#include <OgreUnifiedShader.h>), with a branch per language only where the macros do not reach (reading the samples texel by texel, and the depth output). The atmosphere code (backends/common/atmosphere.glsl) is inserted with Ogre's definitions. GLSL programs get Ogre's native version directive; HLSL programs compile for vs_4_0 and ps_4_0.

What this means for your setup:

  • Ogre's Media/Main must be a resource location of the globe's resource group, for OgreUnifiedShader.h, as in any Ogre application with the shader system. With a downloaded Ogre, it is in the source tree (<build>/_deps/vgl_ogre_source-src/Media; the CMake cache variable VGL_OGRE_MEDIA_DIR names it); with an installed Ogre, the media directory of the installation (OGRE_MEDIA_DIR from its CMake package).
  • The globe does not need the RTShader system. The sample initializes it anyway, for Ogre's own materials such as the overlay's.
  • Materials are named after globe_create_info::name ("vgl/globe/terrain", "vgl/globe/sky", "vgl/globe/stars", and per-mesh clones); keep the name unique if you create several globes.
  • Mesh samples are stored as a two-channel 32-bit float texture, the normal packed into a float, because Ogre's Direct3D 11 render system has no integer textures.
  • Culling is CULL_CLOCKWISE (counter-clockwise front faces, as the data has them); Ogre turns it over for mirrored and flipped targets itself, so there is no mirrored setting.

Render queue groups

What Group
Terrain tiles The globe's own render queue group: RENDER_QUEUE_MAIN unless you call setRenderQueueGroup on it
Stars RENDER_QUEUE_SKIES_LATE - 1
Sky (the atmosphere) RENDER_QUEUE_SKIES_LATE

The sky and stars draw at the far depth where nothing was drawn before them, so your opaque objects in earlier groups keep them out. Objects that write no depth and should appear in front of the sky (transparent effects, for example) belong in a group after RENDER_QUEUE_SKIES_LATE; the overlay's group comes after it anyway.

Device loss and shutdown

  • After Ogre's render system lost its device and recreated its resources, call globe->device_reset(): the globe forgets its Ogre objects and the context's render resources, which upload again over the next frames. Stars and clouds are kept on the CPU.
  • Shut down in this order: detach the globe, vgl::ogre::globe::destroy, then the packs and the vgl context, then Ogre (Root). The globe removes its materials, programs and textures from Ogre's managers when destroyed.

The Ogre sample

samples/ogre/main.cpp, executable vgl-ogre, built when VGL_BACKEND_OGRE is on and GLFW and Ogre's overlay and Bites are available. GLFW owns the window (created with GLFW_NO_API) and the input; Ogre renders into it through externalWindowHandle on Windows, or a child window under parentWindowHandle on X11. The panel is the samples' shared Dear ImGui overlay, drawn by Ogre's ImGui overlay. It has the same navigation and keys as the OpenGL sample.

vgl-ogre [--data <directory> | --pack <uri>...] [--cache <directory> [--cache-budget <MiB>]]
         [--rs gl|d3d11] [--depth logarithmic|reversed|conventional] [--check]
         [--save <directory>] [--stars <catalog>]
  • --rs picks the render system: gl ("OpenGL 3+ Rendering Subsystem", the default) or d3d11 ("Direct3D11 Rendering Subsystem", Windows).
  • --depth defaults to reversed-Z; the globe is drawn into a render texture and shown in the window, for the 32-bit depth buffer.
  • --check runs hidden: it draws a few views offscreen with each convention, with a new Ogre root for each, checks the images and that the conventions agree, and exits with 1 on failure. --save writes the images as PPM files.
  • Ogre's log goes to vgl-ogre.log in the working directory.

The checks are the CTest tests ogre_gl and, on Windows, ogre_d3d11, added with -DVGL_TEST_GL=ON since they need a display. CI runs ogre_gl on Linux with Mesa under Xvfb, outside pushes, as building Ogre takes a while.

Troubleshooting

Symptom Likely cause
Configure skips the backend No Ogre 14 found and VGL_FETCH_DEPENDENCIES off; set OGRE_DIR or turn fetching on
vgl::ogre missing from an installed vgl vgl was built with a downloaded Ogre; build it against an installed Ogre 14
create fails with backend_error, "Ogre has no render system yet" Create the globe after Root::initialise
A program fails to compile, OgreUnifiedShader.h not found Media/Main is not in the globe's resource group
create fails with not_supported Reversed-Z without "Reversed Z-Buffer" set to "Yes"
Distant terrain z-fights with reversed-Z Drawing into a render window's 24-bit depth buffer; use a render texture
The globe is offset or stretched Ogre's camera's field of view or aspect does not match the context's projection

See also

Render Interface, OpenGL Backend, Direct3D 11 Backend, Atmosphere, Stars and Clouds, Building vgl, Installing vgl, Samples, and the header backends/ogre/include/vgl/ogre.hpp.

Clone this wiki locally