Каков правильный шаблон дизайна для этой ситуации? - PullRequest
3 голосов
/ 14 января 2012

Я пытаюсь реализовать класс модели (gfx), но я не могу найти подходящий дизайн для него.

То, что у меня есть, это: [псевдокод]

class Material {
  public:
    virtual void SetPerFrameInfo(void* pData)=0;
    //[...]
}

class SpecificMaterial : public Material {
  public:
    void SetPerFrameInfo(void* pData);
    //[...]
}

void  SpecificMaterial::SetPerFrameInfo(void* pData) {
    ThisMaterialPerFrameInfoStruct* pInfo = (ThisMaterialPerFrameInfoStruct*)pData;
    //[...]
}

class Model {
  public:
    Model(Material* pMaterial){m_pMatirial = pMaterial;}
    void Draw();
  private:
    Material* m_pMaterial
    //[...]
}

void Model::Draw() {
    PerFrameInformation info;
    m_pMaterial->SetPerFrameInfo(&info);
}

Как это использовать:

Material* pMaterial = new SpecificMaterial();
Model someModel(pMaterial);

Как видите, у меня две основные проблемы:

1) Тип структуры, передаваемой в SetPerFrameInfo (), зависит от фактического материала, так как мне его заполнить?

2) Класс Model может нуждаться в некоторых других камерах данных в зависимости от фактического материала, поскольку для некоторых материалов я мог бы решить реализовать таймер, а для некоторых - нет. Было бы бесполезно тратить память на наличие камер данных в классе Model для всех материалов.

Пожалуйста, помогите, я не могу найти подходящий шаблон дизайна.

Ответы [ 4 ]

1 голос
/ 14 января 2012

Исходя из того, что вы сказали, похоже, что ваш класс Material абстрагирует ваши шейдеры, и PerFrameData различается между материалами, потому что каждый шейдер может принимать различный набор входных данных.Проблема в том, что у вас есть единственный класс хранения данных, Model, хранящий то, что может быть множеством входных перестановок.То есть, ваша модель содержит текстуры, цвет вершин и то и другое?Какой формат вершины используется?Индексируются ли треугольники как полосы или списки?Модель представляет карту нормалей?Lightmap?Spec?И т. Д.

Вам действительно необходимо определить стандартный набор компонентов, который понимает ваша система рендеринга, и интерфейс, который знает обо всех этих компонентах и ​​поэтому может быть мостом между моделью и материалом.Материал должен иметь возможность запрашивать данные модели через этот интерфейс и проверять, что он предоставляет правильные данные.Модель может создать класс и передать его в Material для рендеринга.

Еще одна вещь, которую следует учитывать, это производительность.Если ваш рендерер зацикливается на всех объектах Model, вызывающих Render, это может быть очень неэффективным, поскольку каждая модель может иметь различные материалы, и, следовательно, вы, вероятно, будете нести огромные штрафы за переключение состояний шейдера много раз за кадр.Если вместо этого вы группируете проходы рендера по материалам, вы можете сэкономить много времени.

1 голос
/ 14 января 2012

Позвольте мне предвосхитить это, сказав, что я не реализовывал такую ​​систему раньше, но вот мои мысли.

Проблема заключается в том, что Model::Draw меняет материал сам по себе, когда онне имеет сведений о конкретном классе материала.Это кричит о том, что эта «модификация материала» должна быть сделана снаружи.

void Model::Draw() {
    // do draw by using the current material
}

void Model::UpdateMaterial(Material* mat) {
    m_pMaterial = mat
}

Поскольку вы модифицируете материал где-то еще в кадре, вы должны знать его тип в этой точке.Этот измененный материал ставится в очередь для конкретного объекта и фрейма

Затем можно подготовить этап подготовки перед рендерингом фрейма, где вы ведете необходимую бухгалтерию и меняете материалы для новых.

Я бы также рекомендовал использовать что-то вроде boost::shared_ptr для управления вашими Material объектами, так как отслеживание того, какие объекты делятся материалом и нужно ли его удалять, является огромной болью васс.

1 голос
/ 14 января 2012

1.) Я думаю, что проблема, с которой вы сталкиваетесь, связана с попыткой поместить слишком много в базовый класс.Поскольку Модель должна знать, с каким конкретным типом материала она работает при вызове функции SetPerFrameInfo, почему бы просто не дать каждому производному от материала классу свою собственную функцию Set .. (), которая принимает правильный тип вместо void *?Поскольку базовый класс Material не имеет возможности взаимодействовать с информацией за кадр (это void *), то зачем обобщать его в базовый класс?

2.) Просто укажите материал, специфичный для материала.камеры данных в производные классы материалов.Я предполагаю, что каждый экземпляр Material соответствует уникальной модели, поэтому информация о материале для этой модели должна быть в классе, производном от материала.

В ответ на ваше разъяснение:

Хорошо, я неправильно понял отношения между двумя классами.По сути, в идеальном сценарии вам нужен набор классов Материалов, которые не знают деталей Модели, и набор Моделей, которые могут работать с любым Материалом, не зная деталей Материала.Поэтому вы можете захотеть применить экземпляр материала Rough к шести различным моделям.

Я не думаю, что возникает вопрос, если каждый экземпляр материала используется несколькими моделями, а каждый экземпляр модели ничего не знает о различных материалах.третий объект для каждой модели необходим для хранения таймеров или любых других данных экземпляра, необходимых для рисования.Таким образом, каждая Модель получает свой собственный объект AppliedMaterial, который указывает на совместно используемый экземпляр Material, а также на элементы данных, необходимые для применения материала к этой модели.Для каждого типа материала должен быть свой подкласс AppliedMaterial, в котором хранятся значения данных, относящиеся к каждому материалу.Пример использования третьего объекта контекста:

struct GlossMaterialContext
{
    Timer t;
    int someContextValue;
};

class GlossMaterial : Material
{
    void * NewApplicator() { return new GlossMaterialContext; }
    void FreeApplicator(void *applicator) { delete (GlossMaterialContext*)applicator; }
    void ApplyMaterial(void *applicator)
    {
        // Set up shader...
        // Apply texture...
    }
};

class 3DModel
{
    Material *material;
    void *materialContext;

    void SetMaterial(Material *m)
    {
        material = m;
        materialContext = m->NewApplicator();
    }
    void Draw()
    {
        material->ApplyMaterial(materialContext);
    }
};

Это не невероятно чистая техника.Я всегда чувствую, что иметь дело с указателями void * - это клудж.Вы также можете иметь третий объект контекста на основе класса, например:

class AppliedGlossMaterial : AppliedMaterial

и:

class GlossMaterial : Material
{
    AppliedMaterial * NewApplicator() { return new AppliedGlossMaterial; }
    ...

Если камеры данных зависят от и Модельтип и тип материала, то ваши модели и материалы слишком тесно связаны, чтобы разделить их на отдельные классы, которые, как предполагается, ничего не знают друг о друге.Вам нужно будет создать подклассы вашей модели: Glossy3DModel, Rough3DModel и т. Д.

0 голосов
/ 14 января 2012

Я думаю, что большинство движков рендеринга собирают фактический рендеринг в центральный алгоритм / семейство алгоритмов, таким образом, draw() будет реализован в центральном классе рендеринга, который будет содержать коллекцию всех объектов, которые будут отображаться в следующем кадре.Затем он вызовет методы для получения соответствующих данных низкого уровня (вершины и цвета, шейдеры и т. Д.), Которые будут доступны через интерфейсы Модель и Материал.Это потому, что материал может многое знать о том, что такое материал, но, вероятно, он не должен ничего знать о DirectX или OpenGl.

Однако, с помощью того, что вы опубликовали, вы можете реализовать что-то вроде:

class PerFrameData
{
    //Methods to get data in a standard way
}

class Material
{
    setPerFrameData(PerFrameData data)
    {
        if (data.hasVertices())
        // or whatever
    }
}

class Model
{
    PerFrameData data;
    Material material;

    draw()
    {
        material->setPerFrameData(data);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...