Сайт борца за чистоту кода

Главная

Матричные преобразования в OpenGL

Информация о материале
Автор: gdever
Категория: OpenGL
Опубликовано: 15 ноября 2011
Просмотров: 22708

    До сих пор для осуществления преобразований мы пользовались либо только достаточно простыми механизмами (сдвиг вектора), либо механизмами, которые нельзя применять повсеместно (кватернионы). Конечно у нас есть достаточно навороченный класс cVector, с помощью которого можно сдвигать и вращать вершины. Однако все эти преобразования будут выполняться на CPU и, следовательно, существенно тормозить нашу игру. У кватернионов те же недостатки. Поэтому сейчас настало время рассмотреть способ, с помощью которого можно осуществлять все преобразования практически даром!

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

    Все эти три матрицы можно свободно изменять. Для начала нужно сообщить системе с каком матрицей мы будем работать. Сделать это можно вызвав функцию glMatrixMode:

    glMatrixMode( mode );
    // где mode это одно из следующих значений
    // GL_MODELVIEW – матрица вида
    // GL_PROJECTION – матрица проецирования
    // GL_TEXTURE – текстурная матрица

    С матрицей проецирования и текстурной матрицей все более менее понятно – они отвечают за проецирование (ортогональное, перспективное etc.) и модификацию текстурных координат. А вот матрица вида требует отдельного, более подробного объяснения. Дело в том что при рендеринге все вершины попадают на конвейер рендеринга в так называемом camera space'e. Это такое пространство, в котором ось OZ направлена в сторону противоположной направлению взгляда (ну и с помощью нормали вида достраиваются оси OX и OY), с началом координат в точке визирования (там где расположена камера). Поэтому если перед рендерингом поставить матрицу вида следующим образом:

    glMatrixMode( GL_MODELVIEW );

    // загрузка единичной матрицы
    glLoadIdentity();

    то вся выводимая геометрия будет выводиться непосредственно перед экраном а не там где ей положено быть. Для перевода координат геометрии из camera space'а в глобальные координаты, как раз и применяется матрица вида.

    После этого можно выбранную матрицу домножать справа на матрицы поворота, смещения или переноса:

    // поворот на угол angle (задается в градусах)
    // вокруг вектора ( vx , vy , vz )
    glRotatef( angle , vx , vy , vz );

    // перенос на вектор ( tx , ty , tz )
    glTranslatef( tx , ty , tz );

    // масштабирование по осям x, y, z
    // sx, sy, sz – масштабирующие коэффициенты 
    // по соответствующим осям
    glScalef( sx , sy , sz );

    К сожалению эти функции изменяют выбранную матрицу. Поэтому после окончания преобразований нужно возвращать измененную матрицу в исходное состояние. Можно было бы конечно высчитывать обратные матрицы, для выполнения обратных преобразований, но OpenGL предлагает другой, более простой и элегантный способ – стек матриц. Работа с ним осуществляется следующим образом – перед началом преобразований сохраняем матрицу в стеке, которую планируем изменять. Изменяем матрицу согласно нашим замыслам. Выполняем преобразование. Выталкиваем исходную матрицу из стека. Все просто! Работа со стеком осуществляется с помощью следующих функций:

    // помещение матрицы в стэк
    glPushMatrix();

    // выталкивание матрицы из стека
    glPopMatrix();

 

    При использовании преобразований следует помнить следующее правило: вектор домножается на матрицу справа, т.е. при перемножении справа стоит вектор, а слева – матрица. Таким образом, если у нас есть последовательность преобразований, то последовательность функций должна быть противоположной. Для большей ясности рассмотрим один пример: пускай у нас есть квадрат, центр которого расположен в начале координат. Нам надо этот квадрат поворачивать вокруг собственного центра, а затем, отодвинув его от начала координат на 3 единицы, повернуть его вокруг начала координат, ну и для большего интереса будем масштабировать этот квадрат. В качестве решения задачи имеем следующую последовательность преобразований:
1. масштабировать квадрат
2. повернуть квадрат вокруг своего центра
3. сдвинуть квадрат на 3 единицы
4. повернуть квадрат вокруг начала координат

    Теперь распишем вызовы соответствующих функций:

    glMatrixMode( GL_MODELVIEW );

    // вращение вокруг начала координат
    glRotatef( Angle , 0 , 0 , 1 );

    //сдвиг на 3 единицы
    glTranslatef( 3 , 0 , 0 );

    //вращение вокруг центра квадрата
    glRotatef( Angle++ , 0 , 0 , 1 );

    //масштабирование
    glScalef( Scale , Scale , 0 );

    Как видите, все согласно правилу.

    Вот пожалуй и все на сегодня, вроде все просто, однако если возникнут вопросы – пишите сами знаете куда )).

    ЗЫ исходные коды к данной статье прилагаются.

Камера в OpenGL (на кватернионах).

Информация о материале
Автор: gdever
Категория: OpenGL
Опубликовано: 31 октября 2011
Просмотров: 11178

    Как вы наверное могли уже понять нам понадобятся кватернионы и классы для работы с DirectInput'ом (статьи по этим темам можно посмотреть на этом сайте в разделах Алгоритмы и DirectX->DirectInput8 соответственно). Поэтому приступим сразу к реализации камеры.

    Первым делом нам надо настроить сопособ проецирования (между ортогональной проекцией и перспективной мы ни минуты не сомневаясь выберем последнюю). Делается это так:

	//выбираем матрицу которую будем устанавливать
	glMatrixMode( GL_PROJECTION );
	//загружаем единичную матрицу
	glLoadIdentity();
	//устанавливаем перспективную проекцию с углом обзора в 60 градусов
	//ближней плоскостью отсечения на расстоянии 1 и дальней на расстоянии 1000
	//здесь же 1.33 – это отношение ширины экрана к высоте (800/600)
	gluPerspective( 60 , 1.33 , 1 , 1000 );

    Как вы видите мы здесь использовали функцию gluPerspective, которая как видно про префиксу «glu» является частью библиотеки утилит. Для того чтобы компиляция прошла без ошибок, вам надо будет подключить файл glu.h и либку glu32.lib. Например так:

#pragma		comment ( lib , "glu32.lib" )

    Реализация класса камеры.

class Camera{
public:
	Vector4	target , norm , eye;
	Camera( void ){}
	Camera( Vector4 e , Vector4 n , Vector4 t ){
		eye = e;
		norm = n;
		target = t;
	}

	//basik movement functions
	void    MoveLeft( void );
	void    MoveRight( void );
	void    MoveForward( void );
	void    MoveBackward( void );
	void	MoveUp( void );
	void	MoveDown( void );

	//input functions
	void	KeyboardInput( Keyboard & );
	void	MouseInput( Mouse & , int , int );

	//init functions
	void	ReInitCamera( void );
	void	InitCamera( Vector4 , Vector4 , Vector4 );

	//camera rotation functions
	void	SpinAlongY( float );//Y - norm
};

    Здесь поля target , norm , eye - это соответственно вектор направления взгляда, вектор вертикали вида, и координаты точки визирования. Для облегчения восприятия рекомендую обратить свой взор на рисунок:

    Теперь быстренько пробежимся по методам класса. «Быстренко» потому что они до безобразия простые.

void	Camera::MoveUp( void ){
	eye += CAM_VELOSITY * norm;
}

void	Camera::MoveDown( void ){
	eye -= CAM_VELOSITY * norm; 
}

void    Camera::MoveLeft( void ){ 
	eye += CAM_VELOSITY * ( norm * target );
}

void    Camera::MoveRight( void ){
	eye += CAM_VELOSITY * ( target * norm );
}

void    Camera::MoveForward( void ){
	eye += CAM_VELOSITY * target;
}

void    Camera::MoveBackward( void ){
	eye -= CAM_VELOSITY * target;
}

void	Camera::KeyboardInput( cKeyboard &Keyboard ){
	Keyboard.GetKeyboardState();

	if( Keyboard.GetButtonState( DIK_W ) )MoveForward();
	if( Keyboard.GetButtonState( DIK_S ) )MoveBackward();
	if( Keyboard.GetButtonState( DIK_A ) )MoveLeft();
	if( Keyboard.GetButtonState( DIK_D ) )MoveRight();

	ReInitCamera();
}

void	Camera::ReInitCamera( void ){
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
	gluLookAt(	eye.x , eye.y , eye.z , 
		eye.x + target.x , eye.y + target.y , eye.z + target.z , 
		norm.x , norm.y , norm.z );
}

void	Camera::InitCamera( cVector4 EYE , cVector4 TARGET , cVector4 NORM ){
	eye	= EYE;
	target	= TARGET;
	norm	= NORM;

	ReInitCamera();
}

void	Camera::SpinAlongX( float Angle ){
	cQuaternion		ry;
	ry.fRy( Angle );
	target = ry * target;
}

void	Camera::MouseInput( cMouse &Mouse , int SCREEN_WIDTH , int SCREEN_HEIGHT ){
	float	x( Mouse.GetOffset( 0 ) );
	float	AngleX( ( ( float )  -x ) / 300 );
	SpinAlongX( AngleX );
	SetCursorPos( SCREEN_WIDTH / 2 , SCREEN_HEIGHT / 2 );
	ReInitCamera();
}

    Здесь CAM_VELOSITY – это некоторая константа, которая хранит скорость перемещения камеры. Далее:

	//сообщаем системе, что сейчас будем менять матрицу вида
	glMatrixMode( GL_MODELVIEW ); 
	glLoadIdentity();
	//передаем параметры положения камеры
	gluLookAt(	eye.x , eye.y , eye.z , 
		eye.x + target.x , eye.y + target.y , eye.z + target.z , 
		norm.x , norm.y , norm.z );

    Вот вроде и все. Просто да?

    Исходники можно скачать здесь.

Робин Гуд

Информация о материале
Автор: Labus
Категория: Разное
Опубликовано: 13 мая 2010
Просмотров: 19530

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

Подробнее: Робин Гуд

Нормальные формы баз данных ч.1

Информация о материале
Автор: gdever
Категория: Создание ПО
Опубликовано: 03 мая 2010
Просмотров: 46879
    В своё время случилась одна любопытная дискуссия с моим сотрудником. Подходит он ко мне и говорит, что типа наша БД не в первой нормальной форме. Не в какой-нибудь хитрой четвёртой или в пятой, которая практически нафиг никому не нужна. А именно первой. Т.е. подобное заявление равнозначно наезду, что, мол, структура БД полное говно, а вы, Алексей, криворукий идиот. «Нифига себе» - подумал, я. Как это такое может быть? Почти полтора десятка лет в программировании, плюс здравым смыслом, вроде, не обделён, и тут вот такое. Можно было конечно завершить недоразумение фразой «я – начальник, ты – дурак», но как-то самому стало интересно, в чём же дело. Поэтому деликатно так поинтересовался, а на чем основано данное утверждение? Сотрудник бодро открыл определение в Википедии и походу уже приготовился праздновать победу. Я, признаться, никогда не мог по памяти назвать ни одну из нормальных форм, а «рисовал» структуру БД просто на основании здравого смысла. Потом постфактум выяснялось, что базы спроектированы в полном соответствии с этими самыми формами. Так и жил. А тут, стало быть, по определению выходило совсем не так как подсказывало программерское чутьё. Поэтому с любопытством начал изучать определение. Каково же было моё удивление, что как раз по этому определению выходило, что с базой полный порядок! Я, конечно, и раньше находил формулировки отечественного сегмента Википедии чрезмерно заумными, и поэтому допускал, что со средним умом лучше их не читать. Но тут произошло что-то совсем вопиющее – мой сотрудник, прочитав определение, сделал какой-то абсолютно нелогичный вывод и даже не удосужился проверить свои выводы. А наоборот взял свою свежепридуманную глупость и решил с её помощью поучить народ. Я говорил, что опенсорс до добра не доведёт, да и другие умные люди тоже говорили. Но я не знал, что армегедец наступит так скоро. Ну да бог с этой Википедией, можно ведь и самому определения нормальных форм набросать.

    Ключ - поле записи, чьё значение уникально для всего набора записей, имеет значение отличное от NULL и неизменно на протяжении всей жизни записи.

    Первая нормальная форма – любое поле любой записи хранит только одно значение.

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

    Вторая нормальная форма – БД находится в первой нормальной форме и любое неключевое поле полностью зависит от ключа.

    Например, у нас есть запись с полями (Идентификатор, Название CD-Диска, Название группы), где ключом является поле «Идентификатор». При этом, очевидно, что поле «Название группы» зависит не только от «Идентификатора» но и от поля «Название CD-Диска». Поэтому такая БД не находится во второй нормальной форме.

    Третья нормальная форма – БД находится во второй нормальной форме и нет неключевых полей зависящих от значения других неключевых полей.

    Например, у нас в записи хранятся код региона и его название (помимо самих полей с информацией о CD-диске). Понятно, что название региона зависит от кода, и наоборот, поэтому такая БД не будет находиться в третьей нормальной фоме.

    PS Ссылки по теме:
    Нормальные формы баз данных ч.2

Отличие японского и китайского языков

Информация о материале
Автор: gdever
Категория: Разное
Опубликовано: 24 февраля 2009
Просмотров: 58623

Иногда возникает вопрос - а что, мол общего/отличного в японском и китайском языках? Про то, что кто-то у кого-то позаимствовал иероглифы наслышаны многое, но этим знания по предмету исчерпываются. Дабы исправить этот пробел решил запостить то, что известно мне.

На самом деле между этими двумя языками больше различий чем общего. Из точек сопряжения можно действительно выделить китайские иероглифы, завезенные корейцами на японский архипелаг. Плюс чтение некоторых слов в некоторых случаях в японском языке - может использоваться китайское чтение. Понятно что произноситься китайские слова в речи японцев будут несколько по-другому. Кстати, китайцы до сих пор не упускают случая попенять японцам на это заимствование. Мол, можете сколько угодно со своим роботом Асимо выкоблучиваться, иероглифы-то все равно наши ))

Фишка японского (и одновременно одна из самых больших его задниц) заключается в том что практически любой иероглиф имеет как японское чтение так и китайское, а иногда несколько китайских и японских чтений (при этом эти чтения могут спокойно являться разными частями речи и существенно отличаться по смыслу). Наличие только китайского или японского чтения достаточно редко встречается. Если иероглиф употребляется самостоятельно, то используется японское чтение, а если в составе какого-либо слова то китайское. Как правило так, однако есть слова (и достаточно много), в которых этому правилу не следуют. Например "каратэ-до", "кара" и "тэ" это японские чтения иероглифов "пустой" и "рука", а "до" это китайское чтение иероглифа "путь". И такое веселье сплошь и рядом. Китайский, понятно, таких проблем лишен.

Далее. Языки принципиально разные по фонетике. Попробуйте посмотреть фильмы без дубляжа. Сразу заметите, что тональность китайского языка более высокая, речь, если можно так сказать, "чирикающая". Японский более низкий по тональности. То что в японском практически все слоги открытые делает речь похожей на журчащий ручей (откуда-то с другого сайта утащил эту метафору). Кстати о слогах - в китайском их более 400, а в японском на порядок меньше. Поэтому китайский очень музыкальный язык, в котором большую роль играет тонизация, ударение и прочие приблуды, недоступные всем кому медведь на ухо наступил. Помнится, видел где-то пример, как из слова shi написали рассказ (пару абзацев). Вполне связный и осмысленный. В японском с их небольшим набором звуков проблема прямо противоположная - повальная омонимия. Зачастую несколько слов с абсолютно разными значениями и написаниями читаются одинаково. Какое из них используется собеседником в каждом конкретном случае, удается понять только из контекста. Ну или если собеседник подскажет. Навскидку вот такой пример - "shin". Это может быть слово "бог", а может быть "новый". Иероглифы понятно разные.

Языки принадлежат разным типам. Китайский принадлежит типу изолирующих языков. Т.е. все слова являются неизменяемыми. Японский принадлежит агглютинативным языкам, т.е. словообразование осуществляется присоединением к корню или основе аффиксов, обозначающие какое-либо конкретное грамматическое значение. Например "hana[su]" - глагол "разговаривать", "hana[shi]" - отглагольное существительное "разговор". Славянские языки кстати являются флективными языками (т.е. в них все меняется в очень широком диапазоне). Ну и так по-мелочи - в японском есть времена а в китайском нет, в японском есть падежи а в китайском нет.

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

Вот вкратце всё что мне было сказать.

  1. Unit-тесты часть 2
  2. Взлом сайта под Joomla 1.5.x
  3. Нет в Мире совершенства и быть не может. Часть 2.
  4. Песня про мандарин!

Страница 7 из 8

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8