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

OpenGL状态集 这不是OpenGL问题,更是C++的组织问题。,c++,opengl,C++,Opengl,我将有一个简单的场景图(一个n-树),其中的节点(为了这个问题)都将渲染一些几何体。更具体地说,它们在draw()方法中都有一些OpenGL绘图命令 出于优化的原因,我希望将类似的对象批处理在一起,并一次性绘制它们。出于这个原因,我想用一种方式来表达我称之为OpenGL的“状态集”。状态集只是一组OpenGL绑定,或者在X对象上调用draw之前设置的命令,然后取消设置 因此,状态集至少有set()和unset(),并且在使用该状态集的节点的绘制命令之前和之后,渲染系统将调用该状态集 我的问题是如

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

出于优化的原因,我希望将类似的对象批处理在一起,并一次性绘制它们。出于这个原因,我想用一种方式来表达我称之为OpenGL的“状态集”。状态集只是一组OpenGL绑定,或者在X对象上调用draw之前设置的命令,然后取消设置

因此,状态集至少有
set()
unset()
,并且在使用该状态集的节点的绘制命令之前和之后,渲染系统将调用该状态集

我的问题是如何表达所说的状态集?当然,一堆函数都可以,但我更希望能够命名一个集合并调用它。与节点A类似,节点A的状态集为
照明和阴影
,节点B的状态集为
CEL\u阴影

因此,创建一个名为
stateSet
的抽象类,它本质上是
set()
unset()
方法的接口,并让每个状态集从中继承似乎是一个好主意。但是,它需要创建一组对象才能从本质上获得一个名称。似乎有更好的办法

理想情况下,我希望有一个可以轻松调用的所有
状态集的列表。例如,在渲染开始之前,最好能够按照其
状态集对场景图中的所有节点进行排序


有什么想法吗?

您可以使用单例模式实现您的状态。然后,另一个单例状态跟踪器类管理这些状态类的实例,并且仅在尚未设置状态时设置状态。请参见下面的粗略实现。这应该给你一个如何进行的想法:

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 ...
};
请注意,上述设计非常简单。正如您在问题中提到的,您可以有多个状态类来继承一个公共接口。关键是要有一个跟踪器(又称管理器)类,对象通过该类设置状态。Tracker类的任务是消除不必要的状态设置(如果已经设置),并取消设置当前设置的其他状态

因此,创建一个名为
stateSet
的抽象类 本质上是
set()
unset()
方法和 从中继承每个状态集似乎是个好主意。但是,它 需要创建一组对象才能获得名称 基本上

假设的抽象类实现通过虚函数表工作,虚函数表通常作为函数指针数组实现。在本例中,您将实例化的显然毫无意义的对象确实持有有意义的状态——那些函数指针

作为创建两个函数指针的多个数组的替代方法,也许您应该创建两个函数指针数组,将索引命名到数组中,保留最后使用的索引,并在通过数组间接定向之前,让您的
状态集(uint state\u name)
检查
状态名称是否不同。我建议通过
static
关键字对其他编译单元隐藏所有全局状态

这种方法提供的自动安全性较低-从语义上讲,没有任何东西可以阻止您将任何整数传递到
状态集(uint)
,并且除非您自己将其放入原始数组,否则不会对其进行范围检查


这两种选择在其他方面基本相似;因此,请充分考虑您自己的想法。

听起来您是在要求我们为您的州集命名。你能详细说明一下你在寻找哪种帮助吗?如果活动的GLSL程序是你的“状态集”的一部分,那么你可以将这些“状态集”作为你的GLSL程序类的一部分:每个人都知道它需要设置什么状态以及它需要激活的程序。然后在渲染之前将每个程序的节点分组。这非常好,我完全可以用它运行。正如您所建议的,我将继续使用set()unset()和getInstance()创建一个状态集接口,管理器可以有各种各样的功能。我特别喜欢管理器在设置另一个状态集时调用unset()的功能,这样就不需要用户调用unset。再次感谢-CodyFurther的想法:从安全设计的角度来看,您可能希望将状态类的定义封装在manager类中。这样,这些类甚至不需要是单例的,因为管理器确保每个状态类只有一个静态实例,并且没有对其构造函数的公共访问。不过经理还是单身。它有点像一个日志类,其中建议使用单例并广泛使用。