04 августа 2010

OpenGL: Перемещения и Вращения

Привет. Этот урок продолжение предыдущего (да-да, это так). На этот раз мы поговорим о перемещениях.

Рассмотрим следующие функции:

  • glLoadIdentity.
  • glTranslated и glTranslatef.
  • glRotated и glRotatef.
  • glScaled и glScalef.
  • glPushMatrix и glPopMatrix.

Заметили что в этих функциях общего? Да. Они выделены жирным. Ах да, есть еще префикс gl, а еще некоторые функции имеют постфикс. Постфикс имеет смысловую нагрузку. Как правило, постфикс указывает на тип принимаемых параметров. Подробнее лучше посмотреть в спецификации.

Любые перемещения, вращения и масштабирования изменяют текущую матрицу. О чем я? Об этом. Посмотрели? Почему вы такие бледные? Не бойтесь. В большинстве случаях нам будет достаточной стандартных функций OpenGL, хотя конечно можно работать с матрицами и на прямую.

Как использовать эти функции? Очень просто, но важно помнить, что это не совсем функции, это скорее процедуры, так как порядок вызова влияет на результат их работы. Что бы это доказать, приведу простой пример:

#if 0
run='example1'
echo "compile $0 to $run..."

g++ -o $run $0 `pkg-config --libs gl glu sdl` \
`pkg-config --cflags sdl` -Wall -Os -s

exit
#endif

#include <iostream>
#include <GL/gl.h>
#include <GL/glu.h>
#include <SDL/SDL.h>
#include "Game.hpp"

class MyGame : public Game
{
public:
  virtual void setup_opengl(int width, int height)
  {
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    gluOrtho2D(-width/4.0, width/4.0, -height/4.0, height/4.0);
    glMatrixMode(GL_MODELVIEW);
  }

  virtual void draw(int dt)
  {
    glClear(GL_COLOR_BUFFER_BIT);

    glLoadIdentity(); // Загружаем единичную матрицу, она не искажает
                      // то что мы рисуем.
    this->draw_grid();

    glPushMatrix(); // Запоминает состояние матрицы (размер стека в
                    // котором сохраняются матрицы ограничен).
    glColor3f(1.0, 1.0, 1.0);
    this->draw_quad();
    glPopMatrix(); // Восстанавливает состояние матрицы.

    glPushMatrix();
    glRotatef(45.0, 0.0, 0.0, 1.0);
    glColor3f(1.0, 1.0, 0.0);
    this->draw_quad();
    glPopMatrix();

    glPushMatrix();
    glTranslatef(0, 25, 0);
    glRotatef(45.0, 0.0, 0.0, 1.0);
    glColor3f(0.0, 1.0, 1.0);
    this->draw_quad();
    glPopMatrix();
    this->draw_point();

    glPushMatrix();
    glRotatef(45.0, 0.0, 0.0, 1.0);
    glTranslatef(0, 25, 0);
    glColor3f(1.0, 0.0, 1.0);
    this->draw_quad();
    glPopMatrix();
    this->draw_point();

    glFlush();
  }

  virtual void on_key_down(SDL_KeyboardEvent key)
  {
    if (key.keysym.sym == SDLK_ESCAPE)
      {
        this->quit();
      }
  }

  virtual ~MyGame(){}

protected:
  // Эта функция рисует точке размером 5.0.
  void draw_point()
  {
    glPointSize(5.0);
    glBegin(GL_POINTS);
    {
      glColor3f(0.0, 0.0, 1.0);
      glVertex2d( 0.0,  0.0);
    }
    glEnd();
  }

  // Тут рисуеться квадрат и точка его вращения и всего прочего.
  void draw_quad()
  {
    glBegin(GL_QUADS);
    {
      glVertex2d( 0.0,  50.0);
      glVertex2d( 0.0,  0.0);
      glVertex2d( 50.0, 0.0);
      glVertex2d( 50.0,  50.0);
    }
    glEnd();

    glPointSize(5.0);
    glBegin(GL_POINTS);
    {
      glColor3f(1.0, 0.0, 0.0);
      glVertex2d( 0.0,  0.0);
    }
    glEnd();
  }

  // А здесь рисуется сетка, что бы лучше ориентироваться как
  // движется объект.
  void draw_grid()
  {
    glLineWidth(1.0);
    glColor3f(0.0, 0.4, 0.0);

    for(int x = 0; x < 400; x += 10)
      {
        glBegin(GL_LINES);
        {
          glVertex2d(x, 400);
          glVertex2d(x, -400);
          glVertex2d(-x, 400);
          glVertex2d(-x, -400);
        }
        glEnd();
      }
    for(int y = 0; y < 400; y += 10)
      {
        glBegin(GL_LINES);
        {
          glVertex2d(-400, y);
          glVertex2d( 400, y);
          glVertex2d(-400, -y);
          glVertex2d( 400, -y);
        }
        glEnd();
      }
  }
};

int main(int argc, char *argv[])
{
  MyGame game;
  game.set_caption("(Урок 2)OpenGL: Перемещения и Вращения");
  game.run();
  return 0;
}

Этот простой пример демонстрирует, как последовательность вызова процедур влияет на общую картину. А где glScaled и glScalef? Эти функции производят масштабирование. Они увеличивают или уменьшают все что будет рисоваться после их вызова.

Вот вам несколько простых примеров функции draw:

// Пример первый.
glClear(GL_COLOR_BUFFER_BIT);

glLoadIdentity();

this->draw_grid();

glPushMatrix();
glTranslatef(0, 10, 0);
glColor3f(0.5, 0.5, 0.5);
this->draw_quad();
glRotatef(45.0, 0.0, 0.0, 1.0);
glColor3f(0.7, 0.7, 0.7);
this->draw_quad();
glScalef(0.5, 0.2, 0.8);
glColor3f(1.0, 1.0, 1.0);
this->draw_quad();
glPopMatrix();
this->draw_point();

glFlush();
// Пример второй.
glClear(GL_COLOR_BUFFER_BIT);

glLoadIdentity();

this->draw_grid();

glPushMatrix();
glRotatef(45.0, 0.0, 0.0, 1.0);
glColor3f(0.5, 0.5, 0.5);
this->draw_quad();
glTranslatef(0, 10, 0);
glColor3f(0.7, 0.7, 0.7);
this->draw_quad();
glScalef(0.5, 0.2, 0.8);
glColor3f(1.0, 1.0, 1.0);
this->draw_quad();
glPopMatrix();
this->draw_point();

glFlush();
// Пример третий.
glClear(GL_COLOR_BUFFER_BIT);

glLoadIdentity();

this->draw_grid();

glPushMatrix();
glRotatef(45.0, 0.0, 0.0, 1.0);
glColor3f(0.5, 0.5, 0.5);
this->draw_quad();
glScalef(0.5, 0.2, 0.8);
glColor3f(0.7, 0.7, 0.7);
this->draw_quad();
glTranslatef(0, 10, 0);
glColor3f(1.0, 1.0, 1.0);
this->draw_quad();
glPopMatrix();
this->draw_point();

glFlush();
// Пример четвертый.
glClear(GL_COLOR_BUFFER_BIT);

glLoadIdentity();

this->draw_grid();

glPushMatrix();
glScalef(0.5, 0.2, 0.8);
glColor3f(0.5, 0.5, 0.5);
this->draw_quad();
glRotatef(45.0, 0.0, 0.0, 1.0);
glColor3f(0.7, 0.7, 0.7);
this->draw_quad();
glTranslatef(0, 10, 0);
glColor3f(1.0, 1.0, 1.0);
this->draw_quad();
glPopMatrix();
this->draw_point();

glFlush();
// Пример пятый.
glClear(GL_COLOR_BUFFER_BIT);

glLoadIdentity();

this->draw_grid();

glPushMatrix();
glScalef(0.5, 0.2, 0.8);
glColor3f(0.5, 0.5, 0.5);
this->draw_quad();
glTranslatef(0, 10, 0);
glColor3f(0.7, 0.7, 0.7);
this->draw_quad();
glRotatef(45.0, 0.0, 0.0, 1.0);
glColor3f(1.0, 1.0, 1.0);
this->draw_quad();
glPopMatrix();
this->draw_point();

glFlush();
// Пример шестой.
glClear(GL_COLOR_BUFFER_BIT);

glLoadIdentity();

this->draw_grid();

glPushMatrix();
glTranslatef(0, 10, 0);
glColor3f(0.5, 0.5, 0.5);
this->draw_quad();
glScalef(0.5, 0.2, 0.8);
glColor3f(0.7, 0.7, 0.7);
this->draw_quad();
glRotatef(45.0, 0.0, 0.0, 1.0);
glColor3f(1.0, 1.0, 1.0);
this->draw_quad();
glPopMatrix();
this->draw_point();

glFlush();

Вот собственно и все. Экспериментируйте.

Советую посмотреть что делает функция glGetIntegerv,и что она вернет с GL_MAX_MODELVIEW_STACK_DEPTH в качестве pname. Вот такая вот вам загадка.