Уроки Коммент.

Создание игр » DirectX 9, Featured, Теория » Система материалов

Система материалов

Система материаловСистема материалов это очень важный компонент любого графического движка. От гибкости системы зависит то, насколько материалы в движке будут гибкие, настраиваемые, удобные и простые в использовании. Тот метод, который мы применяем с Вами сейчас (т.е. просто включение нужного шейдера и передача параметров), довольно неудобен – для отрисовки каждого объекта приходится писать пусть и не много, но всё же несколько строчек кода. Чем больше объектов (и материалов) в сцене – тем больше кода. Потому, помимо основной своей задачи, система материалов позволяет так же уменьшить количество кода, необходимого для отображения объекта, а так же уменьшает количество ошибок (т.к. задача контроля какой шейдер сейчас включен, когда включить другой и т.д. полностью возлагается на систему).

Задачи системы материалов

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

  • Материал должен загружаться из файла. Этот файл должен иметь простую структуру, которая позволила бы без проблем создавать новые материалы в любом текстовом редакторе. Уже по привычке выберем формат XML, который будем загружать с помощью PugiXML.
  • Если материал загружается повторно – система должна понять это и просто выдать указатель на уже загруженный ранее материал.
  • Материал должен как минимум содержать данные об используемых им текстурах и шейдерах и должен уметь сам “включить” их при рендере объекта с этим материалом.

По минимуму нам этого должно хватить, а со временем разовьём систему дальше, для большей эффективности.

Класс материала

Давайте при реализации класса материалов, да и самой системы, как и всегда, двигаться по пути максимальной простоты. Реализуем лишь строго необходимый минимум функционала:

class CMaterial
{
	CShaderPtr m_pVertexShader;
	CShaderPtr m_pPixelShader;
	CTexturePtr m_pTextures[4];
 
public:
	CMaterial(void);
	virtual ~CMaterial(void);
	bool Load(const std::string& strFileName);
};

В классе графики добавим функцию загрузки материала:

CMaterialPtr Graphics::LoadMaterial( const std::string& strFileName )
{
	// ищем среди уже загруженных материалов
	for (size_t i=0; i<m_vecMaterials.size(); i++)
		if (m_vecMaterials[i]->GetFileName()==strFileName)
			return m_vecMaterials[i];
 
	// создаём новый и загружаем из файла
	CMaterialPtr pMat = new CMaterial();
	if (!pMat->Load(strFileName))
	{
		delete pMat;
		return NULL;
	}
	m_vecMaterials.push_back(pMat);
	return pMat;
}

И функции “включения” материала (для рендера) и отключения его (на всякий случай)):

void Graphics::BindMaterial( CMaterialPtr pMat )
{
	SetPixelShader(pMat->GetPS());
	SetVertexShader(pMat->GetVS());
	for (int i=0; i<pMat->GetTexSlots(); i++)
	{
		CTexturePtr pTex = pMat->GetTexture(i);
		if (pTex)
			SetTexture(i, pTex);
	}
}
 
void Graphics::UnbindMaterial()
{
	SetPixelShader(NULL);
	SetVertexShader(NULL);
}

Теперь мы можем использовать наш класс в программе, вот так мы сможем загрузить материал в функции bool CTutorial3D::Init:

	m_pMaterial = Graphics::get().LoadMaterial("Data/mat1.mtl");
	if (!m_pMaterial)
		return false;

И вот так использовать его в рендере:

gr.BindMaterial(m_pMaterial);
 
// передаём константы в шейдеры
m_pMaterial->GetVS()->SetMatrix("mViewProjection", &m_camera.GetViewProjection());
m_pMaterial->GetVS()->SetVector("vLight", &D3DXVECTOR4(250, 100, 100, 0));
m_pMaterial->GetVS()->SetVector("vCam", &m_camera.GetPos4());
 
//		gr.SetTexture(0, m_pTexDiffuse);
gr.SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_ANISOTROPIC);
gr.SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_ANISOTROPIC);
gr.SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
 
m_pMaterial->GetVS()->SetMatrix("mWorld", &m_modelTop.GetMatrix());
gr.Draw(m_modelTop.GetMesh());
m_pMaterial->GetVS()->SetMatrix("mWorld", &m_modelMiddle.GetMatrix());
gr.Draw(m_modelMiddle.GetMesh());
m_pMaterial->GetVS()->SetMatrix("mWorld", &m_modelBottom.GetMatrix());
gr.Draw(m_modelBottom.GetMesh());

Помимо этого я, как обычно, внёс в наши (старые) исходники небольшие косметические изменения. На этом класс материала можно считать готовым к использованию.

В следующих уроках мы не много доделаем и разовьём класс материала эту систему для того, что бы в будущем минимизировать наш труд по использованию материалов, шейдеров и текстур в программе. Однако, уже на данный момент класс материала позволил нам немного упростить наш код. Кроме того, у нас появилась возможность настраивать материалы (т.е. указать pixel shader, vertex shader, а так же текстуры) из конфигурационного файла материала, а значит, без компиляции приложения заново. Это очень удобно, когда над игрой работает не только программист, но и 3д-художники, т.к. они могут настраивать внешний вид без участия программиста и, следовательно, у него появляется больше времени для работы именно над программированием игры, а не правкой всякой мелочи вроде материалов, путей к текстурами и так далее.

Как обычно, прилагаются:

»crosslinked«

Ещё по этой теме:




Раздел: DirectX 9, Featured, Теория · Теги: Direct3D, Оптимизация, Создание движка

15 комментариев на "Система материалов"
  1. Redline пишет:

    Спасибо за урок!
    Можно вопрос?
    Почему именно четыре указателя на текстуру в классе материала?

    Будет ли в будущем урок по тексту – как выводить быстро рисовать текст, без GDI, в direct3d-приложении?

    1. Вячеслав пишет:

      Redline, четыре текстуры взял просто с запасом – на самом деле нам пока вполне будет хватать и двух-трёх. В дальнейшем, при необходимости, это количество можно будет без проблем увеличить.

      Если урок по тексту нужен – сделаем. Вообще, эту задачу вполне можно возложить на систему GUI. Одну из готовых реализаций (myGUI) этой системы я планировал рассмотреть в скором будущем.

      Вообще, как правило, рендер текста не является “узким местом” программы, потому особого смысла глубоко погружаться в эту тему и делать какие-то особо сложные оптимизации я не вижу, если честно.

  2. lds пишет:

    Почему материал не хранит такие данные, как ambient, diffuse, specular и shininess компоненты освещения? Ведь от этого очень зависит вид объекта. Можно было сделать отдельную структуру, хранящую эти параметры, а в классе материала хранить указатель на неё.

    1. Вячеслав пишет:

      lds, потому как оно нам сейчас не нужно. А я уже много раз писал, что я предпочитаю не делать пустую работу ;-)

  3. Antony пишет:

    Надеюсь, всё в порядке?
    Не смею торопить, просто какое-то затишье крепкое…

    1. Вячеслав пишет:

      Antony, затишье крепкое получилось, да )

      Всё в порядке, просто загруженность очень большая. С выходных, надеюсь, всё вернётся в норму и постараюсь наверстать всё упущенное время, сделав сразу несколько уроков 8-)

  4. ArchiDevil пишет:

    Есть маленькое пожелание – сделайте уроков именно по шейдерам, и, если не очень сложно, разберите тени, пожалуйста.

  5. gms пишет:

    >> Одну из готовых реализаций (myGUI) этой системы я планировал рассмотреть в скором будущем.

    Ждем с нетерпением! Я уже пробовал myGUI и самостоятельно кое с чем разобраться не получилось.

  6. Vladi-mir пишет:

    Демка с работающей системой материалов не запускается, пишет не удалось найти d3dx9_43.dll

    1. Вячеслав пишет:

      Это нормальное явление, если стоит старая версия ДХ-9, обновить надо и должно заработать.

  7. Станислав пишет:

    Давно читаю уроки, спасибо за труд…
    В демке заметил неприятный эффект, когда окно появляется модель выглядит нормально, но при растяжении окна – изображение растягивается (деформируется)…

    1. Вячеслав пишет:

      Очень рад, если они хоть чем-то помогли )

      Касательно растяжения – да, есть такая недоработка. В ближайших уроках доделаем… Найти бы только время на это – со свободным временем проблемы 3ий месяц 8-(

  8. Antony пишет:

    Привет!

    Скопилось несколько вопросов, если можно:) Сразу извиняюсь за дилетантство.

    Допустим у нас уже есть небольшой 3d мир, который представляет собой немало разнообразных объектов.
    Правильно ли я думаю, что для ренедера всех объектов их лучше сгруппировать по их материалу? Т.е. рисовать их не подряд, а сначала выводим все объекты, которые имеют материал A, потом переключаем стейты для материала B и выводим все объекты с этим материалом?
    Или это не имеет особого значения, всё равно мировая матрица у каждого объекта своя и мы её ставим в шейдер перед выводом каждого объекта?

    Далее, когда материал считается разный (в смысле по каким признакам отличать материалы для формирования грумм рендера)? Если у двух материалов одинаковые вертексные и пиксельные шейдеры, но, например, цвет разный – это будет одна группа объектов или две?

    И, наконец, у нас разные шейдеры с разными наборами глобальных данных (тех, которые устанавливаются из программы на с++). Тогда получается и на каждый шейдер у нас отдельная ф-ия, устанавливающая эти шейдерные переменные? Иначе как лучше узнать, какие установки ждёт шейдер?

    :)

    1. Вячеслав пишет:

      Вы всё верно понимаете. То, что Вы описали – это называется батчинг (batching), можете погуглить по этому слову и найти некоторое количество статей. Я через некоторое время собирался описать основы процесса батчинга, которые я использую в своём движке.

  9. Antony пишет:

    Ещё такое вопрос: стоит ли использовать файлы-эффектов или лучше обойтись без них? И если использовать, то у OpenGL есть аналоги, чтобы можно было переписать графику под MacOS, например?

Оставить комментарий

*

Вы можете использовать это HTMLтеги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>