2012-12-30 26 views
6

这不是一个OpenGL问题,而是更多的C++组织问题。OpenGL状态集

我将会有一个简单的场景图(一棵n-tree),其节点(为了这个问题)将会渲染一些几何图形。更具体地说,它们在draw()方法中都有一些OpenGL绘图命令。

出于优化的原因,我想将相似的对象批量打包并一次绘制。出于这个原因,我想要表达一种我称之为“状态集”的OpenGL。一个状态集只是一堆OpenGL绑定,或者是在X对象上调用绘制之前获取的命令,之后会被取消设置。

所以一个状态集至少有set()unset(),并且在渲染系统使用这个状态集的节点的绘制命令之前和之后将被调用系统调用。

我的问题是如何表达所说的状态集?肯定有一堆功能可以做,但我宁愿能够命名一组并记住它。与节点A相似,状态集为LIGHTING_AND_SHADOW,节点B为CEL_SHADING

由于这个原因,抽象类叫做stateSet,它本质上是一个接口,用于set()unset()方法,并且每个状态集都从它继承,这似乎是一个好主意。但是,它需要创建一堆对象才能获得名称。似乎可能会有更好的方法。

理想情况下,我想列出所有可以轻松召回的stateSets。例如,在渲染开始之前,能够按照它们的stateSet对场景图中的所有节点进行排序会很好。

任何想法?

+0

这听起来像是你要求我们给你的状态集命名。你能否详细说明你正在寻找哪种帮助。 – Mads

+0

如果活动的GLSL程序是你的“状态集”的一部分,那么你可以让这些“状态集”成为你的GLSL程序类的一部分:每个人都知道它需要设置什么状态以及它需要激活的程序。然后在渲染之前按节目对节点进行分组。 – Gigi

回答

2

您可以使用单例模式实现您的状态。然后,另一个单身状态跟踪器类管理这些状态类的实例,并且只在尚未设置的状态下设置状态。请参阅下面的粗略实施。这应该给你一个想法如何去了解它:

class SingletonStateClass1 { 
public: 
    static SingletonStateClass1* getInstance() { 
     if(! instanceFlag) { 
      single = new SingletonStateClass1(); 
      instanceFlag = true; 
      return single; 
     } 
     else { 
      return single; 
     } 
    } 
    void set() { 
     // Do setting stuff 
    } 
    void unset() { 
     // Do unsetting stuff 
    } 
    ~SingletonStateClass1() { 
     instanceFlag = false; 
    } 

private: 
    static bool instanceFlag; 
    static SingletonStateClass1 *single; 
    SingletonStateClass1() {} //private constructor 
}; 
bool SingletonStateClass1::instanceFlag = false; //needs to be set so that the first call to getInstance() works ok. 


//ASSUME THERE IS ANOTHER CLASS WITH SIMILAR DESIGN, NAMED: SingletonStateClass2 

class SingletonStateTracker { 
public: 
    static SingletonStateTracker* getInstance() { 
     if(! instanceFlag) { 
      single = new SingletonStateTracker(); 
      state1 = SingletonStateClass1::getInstance(); 
      state2 = SingletonStateClass2::getInstance(); 
      instanceFlag = true; 
      isSetState1 = false; 
      isSetState2 = false; 
      return single; 
     } 
     else { 
      return single; 
     } 
    } 

    // Only setting a state unsets the other states 
    void set1() { 
     if (!isSetState1) { 
      if (isSetState2) { 
       state2->unset(); 
       isSetState2 = false; 
      } 
      state1->set(); 
      isSetState1 = true; 
     } 
    } 
    void set2() { 
     if (!isSetState2) { 
      if (isSetState1) { 
       state1->unset(); 
       isSetState1 = false; 
      } 
      state2->set(); 
      isSetState2 = true; 
     } 
    } 

private: 
    static bool instanceFlag; 
    static bool isSetState1; 
    static bool isSetState2; 
    static SingletonStateTracker *single; 
    static SingletonStateClass1 *state1; 
    static SingletonStateClass2 *state2; 
    SingletonStateTracker() {} //private constructor 
}; 
bool SingletonStateTracker::instanceFlag = false; //needs to be set so that the first call to getInstance() works ok. 




class DrawableObject1 { 
public: 
    DrawableObject1() { 
     tracker = SingletonStateTracker::getInstance(); 
    } 
    void draw() const 
    { 
     tracker->set1(); 
     //DO YOUR OBJECT SPECIFIC OPENGL DRAW STUFF HERE 
    } 
private: 
    SingletonStateTracker* tracker; 
}; 

class DrawableObject2 { 
public: 
    DrawableObject2() { 
     tracker = SingletonStateTracker::getInstance(); 
    } 
    void draw() const 
    { 
     tracker->set2(); 
     //DO YOUR OBJECT SPECIFIC OPENGL DRAW STUFF HERE 
    } 
private: 
    SingletonStateTracker* tracker; 
}; 


/* Below two classes show a crude usage of the above design */ 
class Scene { 
    // ... other stuff ... 

public: 
    DrawableObject1 obj1a; 
    DrawableObject1 obj1b; 
    DrawableObject2 obj2; 
    // ... other stuff ... 
}; 

class Viewer { 
    // ... other stuff ... 
public: 
    void draw() { 
     scene->obj1a.draw(); //This call unsets state2, sets state1 
     scene->obj1b.draw(); //This call does not set any state since state1 is already set 
     scene->obj2.draw(); //This call unsets state1, sets state2 
    } 
private: 
    Scene* scene; 
    // ... other stuff ... 
}; 

请注意,上述设计是非常简单的。您可以让多个State Classes继承您的问题中提到的通用界面。关键是要有一个跟踪器(a.k.a Manager)类,通过这个类来设置对象的状态。 Tracker类的工作是消除不必要的状态设置,如果它已经设置,则取消设置其他状态。

+0

这是非常好的,我可以运行这个。正如你所建议的,我将继续使用set()unset()和getInstance()创建一个StateSet接口,并且管理器可以有各种各样的好东西。我特别喜欢管理员在设置了另一个StateSet时调用unset()的能力,因此不需要用户调用unset。再次感谢! -Cody –

+0

进一步的想法:从安全设计的角度来看,您可能想要私下将状态类的定义封装在管理器类中。这样,这些类甚至不需要单身,因为管理器确保每个这些状态类只有一个静态实例,并且没有公共访问其构造函数的权限。尽管经理仍然是辛格尔顿。这是一个记录类,其中Singleton建议并广泛使用。 – meyumer

1

为此作出的叫stateSet抽象类,这是 本质上是set()unset()方法的接口,并具有 每个国家规定继承它似乎是一个不错的主意。但是,它本质上需要创建一堆对象才能获得名称 。

您的虚拟抽象类实现通过虚函数表工作,虚函数表通常作为函数指针数组实现。在这种情况下你实例化的显然没有意义的对象确实保持有意义的状态 - 那些函数指针。

作为创建两个函数指针的许多数组的替代方法,可能应该创建两个函数指针数组,名称索引到数组中,保留最后使用的索引,并检查state_name是否不同在通过阵列进行间接处理之前。我建议通过static关键字从其他编译单元隐藏所有全局状态。

这种方法提供的自动安全性较低 - 语义上没有任何东西阻止您将任何整数传递到stateSet(uint),并且除非您自己将它放在那里,否则没有范围检查原始数组。

这两个选项基本上非常相似;所以给你自己的想法应有的考虑。