6

这与其说是 OpenGL 问题,不如说是 C++ 组织问题。

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

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

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

我的问题是如何表达所说的状态集?当然一堆函数可以做,但我宁愿能够命名一个集合并回忆它。就像节点 A 有一个状态集,LIGHTING_AND_SHADOW节点 B 有CEL_SHADING

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

理想情况下,我希望列出所有stateSets可以轻松回忆的内容。例如,在渲染开始之前,如果能够将场景图中的所有节点按它们的stateSet.

有任何想法吗?

4

2 回答 2

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

请注意,上面的设计非常简单。正如您在问题中指出的那样,您可以有多个状态类来继承一个公共接口。关键是要有一个 Tracker(又名 Manager)类,通过该类对象设置状态。Tracker 类的工作是消除对已设置状态的不必要设置,如果当前已设置其他状态,则取消设置它们。

于 2012-12-30T20:41:36.317 回答
1

出于这个原因,创建一个名为的抽象类stateSet,它本质上是set()andunset()方法的接口,并让每个状态集都继承自它似乎是个好主意。但是,它需要创建一堆对象才能从本质上获得名称。

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

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

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

这两个选项在其他方面基本相似;所以给你自己的想法适当的考虑。

于 2012-12-30T21:03:33.733 回答