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
// 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
CODE
.H
#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
.CPP
#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); }