Screen Ratio LOD Strategy

ScreenRatioLodStrategy works by analyzing how much portion of screen a model occupies on screen on any given instance of time, and selects a LOD mesh/material accordingly. ScreenRatioLodStrategy is inspired by PixelCountLodStrategy.


  • ScreenRatioLodStrategy is slight modification of PixelCountLodStrategy.
  • Currently it is not included in Ogre SDK


I feel ScreenRatioLodStrategy does a little more justice to varying screen resolutions these days.

Say, I specify a PixelCountLodStrategy mesh with 50% complexity reduction below 500*500 pixel area. Now this might work very well on my 1080p monitor, but when I view the same model on a 1024*800 netbook screen or projector, model will switch to level 1 a little too soon.

ScreenRatioLodStrategy on the other hand specifies the Ratio below which lod will be reduced. We spacify lod values as 0.2(=20%), 0.1(=10%) and so on. So when a model takes below 20% of total screen area, it will switch to level 1. This is total independent of whether I am running a 800*600 projector or a 1080p monitor.

ScreenRatioLodStrategy VS PixelCountLodStrategy

  1. ScreenRatioLodStrategy is inspired by PixelCountLodStrategy, but provide simpler and better control over LOD meshes.
  2. Where PixelCountLodStrategy specifies LOD meshes when they occupy certain number pixels on screen (eg 500*500pixels, 200*200), ScreenRatioLodStrategy specifies LOD meshes when they occupy certain percent of total screen area (50% of screen, 20% of screen)
  3. Easier to specify lod values while defining LOD meshes

ScreenRatioLodStrategy Values

NOTE: values need testing

LOD values are specified as ratio, that is, area occupied by a model of total screen area. However please note that the values are approximations and generally higher than actual area. For example

  • LOD Value > 1.0 = majority of screen space (~50%)
  • LOD Value > 0.5 = significant amount of screen space (~25%)
  • LOD Value > 0.2 = small amount of screen space (~5%)
  • and so on

How To Programatically Prepare A Mesh For ScreenRatioLodStrategy ?

NOTE: code needs testing

For ScreenRatioLodStrategy , mesh lod values are stored in decreasing orger, starting with MAX_FLOAT value. Here is what you have to do

// define base mesh
MeshPtr mesh =   MeshManager::getSingleton().load("something-100p.mesh"); // level 0, 100 percent
// add LOD strategy
mesh->setLodStrategy(new Ogre::ScreenRatioLodStrategy()); // might need to reapply after load
// add level 1 mesh
mesh->createManualLodLevel(0.5, "something-50p.mesh"); // level 1, 50 percent
// add level 2 mesh, for pixel area below 200*200
mesh->createManualLodLevel(0.2, "something-25p.mesh"); // level 2, 25 percent
// export LODed mesh
MeshSerializer ser;
ser.exportMesh(mesh1.get(), "something-with-lod.mesh");

How to use LODed mesh

NOTE: code needs testing

Entity* enty = sceneManager->createEntity("MyLodMesh", "something-with-lod.mesh");
enty->setLodStrategy(new Ogre::ScreenRatioLodStrategy()); // optional

Help Needed

  • Currently it does not implement Singleton interface, if someone can do it
  • I could not decide if I need to do something in transformBias function



#ifndef __Screen_Ratio_Lod_Strategy_H__
#define __Screen_Ratio_Lod_Strategy_H__

#include "Ogre.h"
#include "OgreLodStrategy.h"

using namespace Ogre;

// TODO add singleton interface
class SUBMERGE_API ScreenRatioLodStrategy : public LodStrategy
	virtual Real getValueImpl(const MovableObject *movableObject, const Camera *camera) const;


	virtual Real getBaseValue() const;

	virtual Real transformBias(Real factor) const;

	virtual ushort getIndex(Real value, const Mesh::MeshLodUsageList& meshLodUsageList) const;

	virtual ushort getIndex(Real value, const Material::LodValueList& materialLodValueList) const;

	virtual void sort(Mesh::MeshLodUsageList& meshLodUsageList) const;

	virtual bool isSorted(const Mesh::LodValueList& values) const;



#include "ScreenRatioLodStrategy.h"

	: LodStrategy("ScreenRatio")
{ }

Real ScreenRatioLodStrategy::getValueImpl(const MovableObject *movableObject, const Ogre::Camera *camera) const
	// Get area of unprojected circle with object bounding radius
	Real boundingArea = Math::PI * Math::Sqr(movableObject->getBoundingRadius());

	// Base computation on projection type
	switch (camera->getProjectionType())
			// Get camera distance
			Real distanceSquared = movableObject->getParentNode()->getSquaredViewDepth(camera);

			// Check for 0 distance
			if (distanceSquared <= std::numeric_limits<Real>::epsilon())
				return getBaseValue();

			// Get projection matrix (this is done to avoid computation of tan(fov / 2))
			const Matrix4& projectionMatrix = camera->getProjectionMatrix();

			// Estimate pixel ratio
			return (boundingArea * projectionMatrix[0][0] * projectionMatrix[1][1]) / distanceSquared;
			// Compute orthographic area
			Real orthoArea = camera->getOrthoWindowHeight() * camera->getOrthoWindowWidth();

			// Check for 0 orthographic area
			if (orthoArea <= std::numeric_limits<Real>::epsilon())
				return getBaseValue();

			// Estimate pixel ratio
			return boundingArea / orthoArea;
			// This case is not covered for obvious reasons
Real ScreenRatioLodStrategy::getBaseValue() const
	// Use the maximum possible value as base
	return std::numeric_limits<Real>::max();
Real ScreenRatioLodStrategy::transformBias(Real factor) const
	// No transformation required for pixel ratio strategy
	return factor;
ushort ScreenRatioLodStrategy::getIndex(Real value, const Mesh::MeshLodUsageList& meshLodUsageList) const
	// Values are descending
	return getIndexDescending(value, meshLodUsageList);
ushort ScreenRatioLodStrategy::getIndex(Real value, const Material::LodValueList& materialLodValueList) const
	// Values are descending
	return getIndexDescending(value, materialLodValueList);
void ScreenRatioLodStrategy::sort(Mesh::MeshLodUsageList& meshLodUsageList) const
	// Sort descending
bool ScreenRatioLodStrategy::isSorted(const Mesh::LodValueList& values) const
	// Check if values are sorted descending
	return isSortedDescending(values);