[Лог #7] Класс рисовальщика.

Добавлен , опубликован

Рендер

Темой этого лога станет рендер. Расскажу немного о том как я себе представляю виртуального художника и почему лучше реализовать интерфейс IRender и класс-реализацию CRenderOGL, наример.
Хочется провести некоторые аналогии, которые по моему очень чётко вписываются.
Класс "рисовальщик" именовал я раньше как RenderDevice, но что это такое вообще? Зачем какие-то классы городить, когда есть, например, OpenGL? Конечно для удобства использования.
Класс IRenderDevice можно назвать художником, причём очень хорошо ему это имя подходит.
Можно сказать художнику "нарисуй мне вот тут квадрат", собственно это было бы эквивалентно такому вот вызову в коде "pRenderDevice->DrawQuad( .... )".
Художник не может нарисовать ничего, если у него нет холста, красок и кистей, и тут вновь можно провести аналогию.
Роль холста будет играть "контекст устройства"( как я понимаю это просто особая видеопамять, вернее её часть) - имеется у всех окон. Тут всё ясно холст - окно.
Кисти или их эквивалент - модели и геометрия, даже не имеет значения 2Д или 3Д. Возможно, кто-то знает, что геометрия(статичная?) в играх на движке Source, idTech и UnrealEngine называется Brush ( кисть ) - наверно просто совпадение, не думаю.
Кисть по холсту художник проведёт, но ничего не нарисует, поэтому нужны краски - текстуры, материалы, шейдеры (shaders). Думаю многие знают что из себя представляет шейдер - это подпрограмма для видеокарты, которая говорит как будет рисоваться 3Д модель. Экранный пост-процессы \ пост-эффекты - это тоже шейдер, например, свечение (glow, bloom), переход картинки в серость\цвет ( такой эффект был в игре Saboteur ).
Аналогии очень хорошо описывают набор классов графической библиотеки, однако, надо заметить, что нет конкретики. Это некая абстракция.
IRenderDevice - художник, а CRenderDeviceOGL - какой-нибудь Вася Пупкин, который вообще то тоже художник, но как видно уже конкретный.
Примерно вот так может выглядеть класс, вернее интерфейс для "художника":
class IRenderDevice
{
public:
	// Первичная инициализация GAPI-устройства
	virtual TResult	Init( WindowT *Window, int param ) = 0;

	// Инициализировать графический контекст для конкретного окна приложения 
	virtual TResult 	InitContextForWindow( WindowT *Window ) = 0;

	// Освободить граф.контекст окна
	virtual TResult 	CleanupContextForWindow( WindowT *Window ) = 0;

	// Сделать текущим(главным) контекст окна
	virtual TResult 	MakeCurrent( WindowT *Window ) = 0;

	// Сменить буфер изображения окна
	virtual TResult 	SwapBuffer( WindowT *Window ) = 0;

	// Финальная очистка граф.устройства
	virtual TResult 	Cleanup() = 0;

	virtual void ApplyView( GView * View ) = 0;
        virtual void SetMaterial( GMaterial * material ) = 0;
	virtual void DrawMesh( GMesh * Mesh ) = 0;
};
У методов этого класса нет реализации (конкретного кода) и они все виртуальные. Видно, что несколько первых методов - это инициализация, обновление кадра и освобождение ресурсов ( Init, SwapBuffer и Cleanup соответственно) - эти функции платформозависимые, что не есть хорошо вообще то. Но можно чуть-чуть избавится от зависимости "спрятав" зависимые от платформы типы и функции, например, тип переменной WindowT - произвольный (определённый мной, а не платформой), внутри которого есть платформозависимые переменные. Это сделано для того, чтобы не менять вызов функции, например:
Этот код не нужно будет менять для каждой платформы
pRenderDevice->Init( pMainWindow, 0 );
Для каждой ОС придётся лишь менять\написать код для класса WindowT и некоторые части от RenderDevice ( и\или других систем ).
Насколько это правильное решение - не знаю точно, возможно это вообще плохое решение.
Последние три функции интерфейса IRenderDevice - "применить вид", "установить материал" и "нарисовать модель". Конечно это не полный список функций, который пригодится, но для очень простых игр думаю даже такого хватит.
Функция ApplyView, которая принимает указатель на некий объект класса GView - особая штука, которой я можно сказать горжусь (хотя на самом деле нечем). GView - это что-то вроде виртуальной камеры, границ вида (вьюпорт \ viewport ) и матрицы проекции. Хранит в себе две матрицы - проекции (перспективная\ортогональная) и вида (положение и направление камеры).
Классы материала и сетки(mesh) вообще могут быть какими угодно, вернее информация которая в них хранится может быть различной и зависеть от "старшей" графической библиотеки (OpenGL \ DirectX).
Но не только материал и 3Д модель зависит от выбора библиотеки, собственно вся реализация интерфейса зависит от неё. Вот например если взять OpenGL, то может быть что-то вроде этого:
class GRenderDevice : public IRenderDevice
{
public:
	GRenderDevice();
	~GRenderDevice();

	// Первичная инициализация GAPI-устройства
	TResult		Init( WindowT *Window, int param );
	// Инициализировать графический контекст для конкретного окна приложения 
	TResult 	InitContextForWindow( WindowT *Window );
	// Освободить граф.контекст окна
	TResult 	CleanupContextForWindow( WindowT *Window );
	// Сделать текущим(главным) контекст окна
	TResult 	MakeCurrent( WindowT *Window );
	// Сменить буфер изображения окна
	TResult 	SwapBuffer( WindowT *Window );
	// Финальная очистка граф.устройства
	TResult 	Cleanup();

	void ApplyView( GView * View );
	void DrawMesh( GMesh * Mesh );

	void DrawVText( float x, float y, const char *strText, float size, bool bReversY = false );

	// Flags = 1 - color, 2 - depth, 4 - stencil
	enum{
		BIT_COLOR = 0x1,
		BIT_DEPTH = 0x2,
		BIT_STENCIL=0x4
	};
	void ClearScreen( int Flags = 0 );
	void ClearScreenColor( const float r, const float g, const float b, const float a );

	void BindTexture( GTexture * Texture );
	void SetWorldMatrix( const float * matrix );
	void SetWireframe( bool show = true );

	void	DrawRect( float x, float y, float xx, float yy );

protected:
	TResult		InitExtensions();
	HGLRC		m_hContext;      // На самом деле зависимая от платформы переменная
	WindowT		*m_pOwnerWindow;
};
Видно, что функций больше, да и переменные появились. (не забывайте, что это примеры, а в реальном проекте будет иначе). Можно создавать экземпляр этого класса и пользоваться всеми этими функциями, однако, если нужна независимость от OpenGL\DirectX, то нужно использовать IRenderDevice у которого функций меньше. Зачем тогда использовать этот урезанный класс? Дело в том, что реализацию художника можно загружать из DLL (если это реализовано, конечно), например, так сделано в Quake2 - рендер и механика игры загружаются из DLL.
Ну вот скажем та же функция ApplyView для OpenGL реализации будет выглядеть примерно так:
void GRenderDevice::ApplyView( GView * View )
{
	if( !View ) return;
// Задать вьюпорт
	View->SetViewport( 0, 0, GetBufferWidth(), GetBufferHeight() );

	glViewport( View->GetX(), View->GetY(), View->GetWidth(), View->GetHeight() );

// Утсановить матрицы (OpenGL-специичные функции)
	glMatrixMode( GL_PROJECTION );
	glLoadMatrixd( &View->GetProjectionMatrix().x[0][0] );
	glMatrixMode( GL_MODELVIEW );
	glLoadMatrixd( &(View->GetViewMatrix()).x[0][0] );
}
Или вот функция рисования сетки:
void GRenderDevice::DrawMesh( GMesh * Mesh )
{
	if( !Mesh ) return;

	GVertex3D * V		= Mesh->GetVertexArray();
	unsigned int Count	= Mesh->GetCountVertex();

// вновь специфичные функции
	glVertexPointer(	3,	GL_FLOAT, sizeof(GVertex3D), &(V[0].x) );
	glNormalPointer(		GL_FLOAT, sizeof(GVertex3D), &(V[0].nx) );
	glTexCoordPointer(	2,	GL_FLOAT, sizeof(GVertex3D), &(V[0].tu) );

// получить количество под-сеток
	unsigned int count_batch = Mesh->GetCountBatches();

// для каждой под-сетки ...
	for( unsigned int i=0; i< count_batch; ++i )
	{
		GBatch *b = 0;
		if( b = Mesh->GetBatch(i) )
		{

			if( b->m_MaterialID != -1 )
			{
//                             установить текстуру 
				BindTexture( FindTextureByID( b->m_MaterialID ) );
			}

//                     Нарисовать треугольники 
			glDrawElements( GL_TRIANGLES, b->GetCount(), GL_UNSIGNED_INT, b->GetPtr() );
		}
	}
}
Для DirectX реализации были бы совсем другие функции, но смыл бы не изменился.
Поэтому вызывая pRenderDevice->ApplyView( pView ) будет срабатывать специфичный код.
Честно говоря не очень хорошо получается у меня объяснить весь смысл, да что там - вообще не понятно что я тут написал :D
Конечно хотелось бы больше конкретного кода, но не учитель я :(
Да ещё и версия OpenGL, которую я ранее и использовал устарела, теперь придётся чуть обновить свои знания и подтянуть версию хотя бы OpenGL 3.3.

Что дальше ?

Думаю либо чуть увеличить разрыв между логами, либо небольшую паузу взять. Думаю начать уже конкретно проектировать само приложение (не писать код) и сделать небольшой альфа-прототип на каком-нибудь Unity (хотел вообще на GameMaker, но похоже с новым обновлением они отказались от WinXP - Steam отказывается запускать GM).
Заметьте ни одной картинки D: !
`
ОЖИДАНИЕ РЕКЛАМЫ...

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
0
24
9 лет назад
Отредактирован prog
0
Doc, а еще лень, привычка к варкрафту и отсутствие информации. Ну и главная проблема у новичков это не столько само разбиение, сколько система агентов/актеров, которые отвечают за отображение всей визуальной части и по которым практически нет внятной документации даже на англоязычных ресурсах.
DarkDes, я в редакторе ск2 делал систему пошаговой игры на доске, в которой кодом было реализовано по большому счету только отображение таймера хода в интерфейсе и пара хаков, позволяющих обойти мелкие, но неприятные недостатки возможностей движка - все остальное было сделано в редакторе данных. Причем таймер тоже был на данные завязан, код отвечал только за синхронизацию интерфейса и таймера. Потом, конечно, кода поднакопилось, когда я захотел еще дальше за пределы базовых возможностей движка выйти.
Что касается интерпретируемого языка - во-первых в идеале он тоже нужен, просто для хранения данных это избыточное решение, а во-вторых намного проще интегрировать тот-же Lua или даже джаваскрипт, чем писать что-то свое.
0
29
9 лет назад
0
Написать парсер\псевдо_язык_программирования - это конечно задачка, но вполне реализуемая.
я так и сделал у себя в одной ммошке, свой скриптовый язык, который спелы реализовывал на стороне сервера
3
29
9 лет назад
Отредактирован Doc
3
Очень влияет на гибкость, она там прекрасная. Эта гибкость и есть минус.
Послушайте, когда-то я выдумывал как бы мне сделать систему способностей для пошаговой игры и где их хранить. Вот я тоже придумывал типа различные компоненты, которые значится где-то вместе связываются в цепочку, и хранятся в базе/в файлах, а потом достаются оттуда, парсятся и каждой компоненте присваивается действие, я подумал о разных вариантах абилок, понадобятся условия, циклы, модули, что-то еще... Да это целый скриптовый язык для абилок получается... НО У МЕНЯ УЖЕ И ТАК ЕСТЬ СКРИПТОВЫЙ ЯЗЫК, ЗАЧЕМ ИЗОБРЕТАТЬ ВЕЛОСИПЕД? Точно так же и в ск2 есть скриптовый язык, на котором можно написать все что угодно, нахрена вообще редактор объектов, на котором запиливание спелла может занять больше времени? Ого, я написал этот спелл без кода, я такой крутой! Правда тут пара хаков, там пара хаков, тут мелкий баг, ну никто все равно не заметит, а потратил то я всего два дня. Вы серьезно? Кому это нужно? А потом кода поднакопилось и в итоге половина логики, а половина там.
Всё уже сделано за вас, блин. Берете любой скриптовый язык и прикручиваете, ограничиваете сендбоксом область действия, пишете внятные интерфейсы/врапперы для объектов используемых в коде - вуаля, делаете ВСЁ, ЧТО УГОДНО. Гибче кода блин, ничего не будет. Что верно заметили, данные в нем хранить не нужно.
0
29
9 лет назад
0
Doc, это такой вечный холивар между теми кто умеет программировать и теми кто привык мышкой блоки таскать
0
29
9 лет назад
0
alexprey, для таких придумали транслятор блоков в тот же самый код, гспди. А не парсер блоков и целую завязанную на нем систему билеберды и огромную архитектуру.
0
24
9 лет назад
0
Doc, не знаю откуда такая лютая ненависть к ск2 и самой идее, но лично я пользуюсь и редактором данных и скриптами, когда у меня есть настроение что-то там по делать и нет настроения хардкорно кодить в более серьезном проекте. Что касается скриптового языка - это довольно удобно, но при такой архитектуре совершенно нереально часть работы спихнуть на геймдизайнера - все приходится кодить лично, что хорошо при работе в одно сопло, но отвратительно вписывается в командную работу.
0
29
9 лет назад
0
но отвратительно вписывается в командную работу.
это еще почему?
0
24
9 лет назад
Отредактирован prog
0
alexprey, потому что команда, как правило, состоит не только из одних программистов и все доступные программисты, как правило, заняты не созданием контента и мыслями о балансе, а более важными вещами, вроде допиливания движка и багфикса.

Согласитесь, было бы странно, если бы, например, 3д-дизайнер вместо того, чтобы делать модели в 3д редакторе, прописывал бы их в коде или работал в редакторе, а потом экспоритровал результат не в сжатый бинарный формат, а прямо в програмный код.
Та-же судьба ждет и игровые данные, которых в крупных проектах становится все больше и больше - стать со временем независимыми от кода и получить отдельного специалиста для работы с ними.
0
29
9 лет назад
0
Но скрипты не обязательно должны быть прямо в коде и опять же, я писал о трансляторе, как это работает в вк3.
0
15
9 лет назад
0
Согласитесь, было бы странно, если бы, например, 3д-дизайнер вместо того, чтобы делать модели в 3д редакторе, прописывал бы их в коде
Собственно подобной мысли я держусь относительно некоторых конкретных игровых данных (это в тему псевдо-языка, вернее удобного хранения данных). Не буду говорить, что мол "вскроют блокнотом!Испортят весь баланс! ЧИТЫ!!11" - по идее можно зашифровать (а в случаи клиент-сервер вообще всё ложится на второго).
Думаю, что скриптовый язык - это жирно, правда я ещё не видел реальных примеров работы, например, lua и какой-либо игры. Не совсем понимаю, что можно хранить в скриптах отдельно от бинарного(основного) кода ?
0
29
9 лет назад
0
DarkDes, да запросто
  • AlienShooter - почти вся игровая механика на скриптах О_О сам был в шоке когда нашел
  • World of Warcraft - вся логика интерфейса написана на lua, а так же пиратские сервера, большая часть логики написано тоже на нем же (не уверен)
  • Warcraft 3 - скрипты jass2, опять же большая часть логики для карт написана на нем
  • Stracraft 2 - Galaxy
Примеров тьма на самом деле
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.