В этом уроке мы рассмотрим понятие «матрицы» OpenGL. Код взяи из первого урока (инициализация).
Небольшое изменение в файле init.cpp:
В функцию IntializeOpenGL() мы добавили включение теста глубины.
Это позволяет рисовать фигуры друг над другом с правильным учитыванием глубины.
То есть будет отрисован только ближайший к камере обьект.
Функция InitializeOpenGL() в файле Init.cpp теперь выглядит так:
{
g_hDC = GetDC(g_hWnd);
if (!bSetupPixelFormat(g_hDC))
PostQuitMessage (0);g_hRC = wglCreateContext(g_hDC);
wglMakeCurrent(g_hDC, g_hRC);glEnable(GL_DEPTH_TEST); // Включаем тест глубиныSizeOpenGLScreen(width, height);
}
Файл main.cpp затронули более серьезные изменения:
/////
///// Добавляем функцию, рисующую треугольник с центром в точке(X,Y,Z).
/////
///////////////////////////////// DRAW TRIANGLE \\\\*void DrawTriangle(float x, float y, float z, float width, float height)
{
//Мы можем очень легко нарисовать треугольник в заданной позиции.
//Эта ф-я очень простая, лучше сосредоточьтесь на коде матриц, который будет ниже.// Три точки, образующие 3угольник, будут вычислены из переданных в ф-ю координат, а
// размеры будут вычислятся исходя из переданных высоты и ширины.glBegin(GL_TRIANGLES); //это начало рисования.
glColor3ub(255,0,0); //Сделаем верхнюю вершину красной
glVertex3f(x, y+height, z); //Верхняя вершина
glColor3ub(0,255,0); //Зеленая вершина
glVertex3f(x+width, y—height,z); //Правая вершина
glColor3ub(0,0,255); //Синяя вершина
glVertex3f(x—width, y—height, z); //Левая вершина
glEnd(); //Закончили отрисовку
}
///////////////////////////////// RENDER SCENE \\\\*
/////
///// И изменим функцию, рендерящую сцену:
/////
///////////////////////////////// RENDER SCENE \\\\*
void RenderScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //Очистим экран и буфер глубины
glLoadIdentity(); //Сбросим матрицу
//Эта функция устанавливает позицию и направление камеры
// Позиция Направление Вертикальный вектор
gluLookAt(0,0,6, 0,0,0, 0,1,0);
//Ниже мы собственно начинаем использовать матрицы.
//Что такое матрицы? Не вдаваясь в подробности, матрицы содержат разные системы координат.
//И вы в любой момент можете изменить систему координат.
//
//Вызывая glPushMatrix, вы начинаете использовать другую систему координат, не ту, что предидущая.
//Это значит, что вызывая glRotatef/glTranslatef, вы воздействуете только на полигоны, отрисовывающиеся
//в текущей матрице. Потом вы вызываете glPopMatrix(), и текущая матрица «выдавливается» из стека, и вы возвращаетесь в
//предидущую матрицу.
//
//При инициализации мы попадаем в главную игровую матрицу.
//Если мы «push-нем» другую матрицу и сделаем преобразования в ней, эти изменения никак не
//скажутся на объектах, находящихся вне её.
//
// Ниже я более детально обьясню понятиематриц.
//Одно из преимуществ OpenGL — то, что мы не должны вникать в суть работы матриц, API делает это за нас.
//
//С другой стороны, я рекомендую создать матрицу хоть однажды самостоятельно, иначе
//вы не будете знать, что происходит внутри OpenGL.
//Рендерим треугольник нашей функцией.
DrawTriangle(0,0,0,1,1);
//Теперь нарисуем ещё 1 треугольник, но немного сдвинем его.
//Нам нужно сдвинуть только ЭТОТ треугольник, не затрагивая остальной мир
// — давайте нарисуем его в отдельной матрице.
glPushMatrix();
//теперь у нас «выделенная» матрица; нарисуем в нём такой же треугольник, немного сдвинутый в сторону.
//Устанавливаем позицию, куда будет сдвинут треугольник:
glTranslatef(1,0,-1);
//И рисуем его
DrawTriangle(0,0,0,1,1);
glPopMatrix(); //Освобождаем текущую матрицу и возвращаемся к предидущей
// Точно так же нарисуем третий треугольник, сдвинутый уже влево.
// Смотрите: отсчет начинается не от нулевых координат, а от второго треугольника,
// поэтому сдвигаем влево на «-2»
glPushMatrix();
glTranslatef(—2,0,-1);
DrawTriangle(1,0,0,1,1);
glPopMatrix();
//Это самый простой пример использования матриц в OpenGL.
//Они вам понадобятся почти во всём, что вы будете делать.
//Причина использования матриц — это намного быстрее и удобнее, чем
//вращение / перемещение каждой точки вручную.
//Теперь посмотрим, много ли вы поняли.
//Что случится, если последний нарисованный треугольник вызвать не (0,0,0,1,1), а (1,0,0,1,1) ?
//Где он будет отрисован? Ответ — в (0,0,-1). Вы спросите, почему?
//Потому что мы передвинули треугольник в (-1,0,-1). Это не перенесло треугольник
//в заданные координаты, а сдвинуло его на заданный отрезок.
//Так что вышло, что координаты мы правильно сдвинули влево, но нарисовали треугольник на единицу правее…
//И треугольник остался в центре, за первым. Попробуйте сами, если не верите мне 😉
SwapBuffers(g_hDC);
}
Матрицы — крутая штука, ага? Они позволяют вам делать многое, но с немногим объемом кода.
А это то, что нам нужно, не так ли? 😉
Вот пример гомогенной (homogeneous) матрицы 4х4:
[ 1 0 0 0 ]
[ 0 1 0 0 ]
[ 0 0 1 0 ]
[ 0 0 0 1 ]
Напомнило что-то с уроков математики? Да, это матрица идентичности (identity matrix).
Перемещения для x, y и z сохранены в этих слотах:
[ 1 0 0 x ]
[ 0 1 0 y ]
[ 0 0 1 z ]
[ 0 0 0 1 ]
А вот слоты изменения размера (scaling) для x,y,z:
[ x 0 0 0 ]
[ 0 y 0 0 ]
[ 0 0 z 0 ]
[ 0 0 0 1 ]
Если мы поместим их вместе, то получим одну матрицу, которая
осуществляет вращение и изменение размера:
[ x 0 0 x2 ] //X Y Z — scale-значения, x2 y2 z3 — translation-значения
[ 0 y 0 y2 ]
[ 0 0 z z2 ]
[ 0 0 0 1 ]
Эта матрица имеет 4 строки и 4 столбца
Так она будет выглядеть в кач-ве массива:
float matrix[16] или float matrix[4][4];
Хочу объяснить ещё одну вещь — вы можете делать с матрицами то же самое, что и с пространствами имен C/C++ {} . Пример:
glTranslate(0, 1, 0);
DrawTriangle(0, 0, 0, 1, 1);glPushMatrix();glTranslate(0, 1, 0);
DrawTriangle(0, 0, 0, 1, 1);glPopMatrix();
glPopMatrix();
Первый треугольник будет нарисован в (0,1,0) — это просто. Но где будет нарисован второй?
Раз и навсегда запомните, что glTranslatef() не устанавливает координаты отрисовки, а сдвигает обьект на указанный отрезок!
Так как мы уже сдвинули текущую матрицу на (0,1,0), а матрица второго треугольника работает ИЗ неё, второй треугольник будет сдвинут на (0,2,0).
До того, как мы сделали какие-либо вращения/перемещения, инициализированная матрица «чиста».
Это значит, что если мы передвинемся на (x,y,z) — обьект просто передвинется на этот отрезок.
Но если мы передвинем следующий объект, то он будет передвигатся уже не от начала координат, а от точки предыдущего сдвига, пока мы не вызовем glLoadIdentity().
Может, так будет проще представить: int X=0;
Потом вы указываете: x+=2;
X был «чист», пока вы не добавили к нему двойку. Теперь X изменён, и если вы укажете: X+=2; снова, X уже не будет 2, теперь он будет 4.
Хоть и не в точности, но это похоже на то, как работают матрицы.