Skip to main content

History: ScreenRatioLodStrategy

Source of version: 1 (current)

Copy to clipboard
            !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.

!Status
*ScreenRatioLodStrategy is slight modification of PixelCountLodStrategy.
*Currently it is not included in Ogre SDK

!Inspiration
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
#ScreenRatioLodStrategy is inspired by PixelCountLodStrategy, but provide simpler and better control over LOD meshes.
#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)
#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
{CODE(wrap="1", colors="c++")}
// 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");
{CODE}


!!How to use LODed mesh
''NOTE: code needs testing''
{CODE(wrap="1", colors="c++")}
Entity* enty = sceneManager->createEntity("MyLodMesh", "something-with-lod.mesh");
enty->setLodStrategy(new Ogre::ScreenRatioLodStrategy()); // optional
{CODE}


!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

!CODE
!!.H
{CODE(wrap="0", colors="c++")}
#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
{
protected:
	virtual Real getValueImpl(const MovableObject *movableObject, const Camera *camera) const;

public:
	ScreenRatioLodStrategy();

	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;
};

#endif
{CODE}

!!.CPP
{CODE(wrap="0", colors="c++")}
#include "ScreenRatioLodStrategy.h"

ScreenRatioLodStrategy::ScreenRatioLodStrategy()
	: 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())
	{
	case PT_PERSPECTIVE:
		{
			// 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;
		}
	case PT_ORTHOGRAPHIC:
		{
			// 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;
		}
	default:
		{
			// This case is not covered for obvious reasons
			throw;
		}
	}
}
//---------------------------------------------------------------------
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
	sortDescending(meshLodUsageList);
}
//---------------------------------------------------------------------
bool ScreenRatioLodStrategy::isSorted(const Mesh::LodValueList& values) const
{
	// Check if values are sorted descending
	return isSortedDescending(values);
}
{CODE}

!References
http://www.ogre3d.org/tikiwiki/PixelCountLodStrategy