Ортографическая камера

Эта страница представляет OrthographicCamera класс и его использование. Ортографическая камера используется в 2D средах только там, где она реализует параллельную (ортографическую) проекцию и где нет масштабного фактора для конечного изображения, несмотря на местонахождения объекта в мире.

Описание

Класс Camera работает как очень простая камера реального мира. В отношении камеры можно:

  • Перемещать и вращать камеру вокруг
  • Увеличивать и уменьшать масштаб
  • Изменить область просмотра
  • Делать проецирование/обратное проецирование в и из координат окна/пространства мира.

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

Следующее небольшое приложение демонстрирует использование простой OrthographicCamera камеры для перемещения вокруг плоского мира. Чтобы представить мир (карту), его представляет плоская квадратная Mesh с текстурой на поверхности. Картой является большой квадрат (1024x1024 пикселей) и окно просмотра 480x320 пикселей.

Окно просмотра это прямоугольная область видимая в любой момент из игрового мира. Если увеличить масштаб, то тем менее получиться прямоугольная область. Так как размер экрана не меняется, окно просмотра масштабируется до фактического разрешения.

public class OrthographicCameraController implements ApplicationListener {

 static final int WIDTH  = 480;
 static final int HEIGHT = 320;

 private OrthographicCamera  cam;
 private Texture    texture;
 private Mesh    mesh;
 private Rectangle    glViewport;
 private float    rotationSpeed;

 @Override
 public void create() {
  rotationSpeed = 0.5f;
  mesh = new Mesh(true, 4, 6,
    new VertexAttribute(VertexAttributes.Usage.Position, 3,"attr_Position"),
    new VertexAttribute(Usage.TextureCoordinates, 2, "attr_texCoords"));
  texture = new Texture(Gdx.files.internal("data/sc_map.png"));
  mesh.setVertices(new float[] { 
     -1024f, -1024f, 0, 0, 1,
      1024f, -1024f, 0, 1, 1,
      1024f,  1024f, 0, 1, 0,
     -1024f,  1024f, 0, 0, 0
  });
  mesh.setIndices(new short[] { 0, 1, 2, 2, 3, 0 });

  cam = new OrthographicCamera(WIDTH, HEIGHT);  
  cam.position.set(WIDTH / 2, HEIGHT / 2, 0);

  glViewport = new Rectangle(0, 0, WIDTH, HEIGHT);
 
 }

 @Override
 public void render() {
  handleInput();
  GL10 gl = Gdx.graphics.getGL10();
  
  // Камера --------------------- /
  gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
  gl.glViewport((int) glViewport.x, (int) glViewport.y,
    (int) glViewport.width, (int) glViewport.height);
  
  cam.update();
  cam.apply(gl);

  // Текстурирование --------------------- /
  gl.glActiveTexture(GL10.GL_TEXTURE0);
  gl.glEnable(GL10.GL_TEXTURE_2D);
  texture.bind();
  
  mesh.render(GL10.GL_TRIANGLES);

 }

 private void handleInput() {
  if(Gdx.input.isKeyPressed(Input.Keys.A)) {
   cam.zoom += 0.02;
  }
  if(Gdx.input.isKeyPressed(Input.Keys.Q)) {
   cam.zoom -= 0.02;
  }
  if(Gdx.input.isKeyPressed(Input.Keys.LEFT)) {
   if (cam.position.x > 0)
    cam.translate(-3, 0, 0);
  }
  if(Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {
   if (cam.position.x < 1024)
    cam.translate(3, 0, 0);
  }
  if(Gdx.input.isKeyPressed(Input.Keys.DOWN)) {
   if (cam.position.y > 0)
    cam.translate(0, -3, 0);
  }
  if(Gdx.input.isKeyPressed(Input.Keys.UP)) {
   if (cam.position.y < 1024)
    cam.translate(0, 3, 0);
  }
  if(Gdx.input.isKeyPressed(Input.Keys.W)) {
   cam.rotate(-rotationSpeed, 0, 0, 1);
  }
  if(Gdx.input.isKeyPressed(Input.Keys.E)) {
   cam.rotate(rotationSpeed, 0, 0, 1);
  }
 }

 @Override
 public void resize(int width, int height) {
 }

 @Override
 public void resume() {
 }

 @Override
 public void dispose() {
 }

 @Override
 public void pause() {
 }
}

Выше описанный класс это Libgdx приложение, которое будет использовать ортографическую камеру для перемещения вокруг мира. Опять же, миром является квадрат 1024x1024 пикселей. Для его представления, использована квадратная плоскость того же размера. Ортографическая камера позволяет легко работать с пикселями, даже если OpenGL это не нравится. Потому что нет Z оси, мы можно спокойно определить на мир, как 1024 единиц в ширину и длину. Если для текстуры используется изображение 1024x1024 и полигональная сетка тоже 1024x1024 единиц в ширину и длину, то мы используем идеальную проекцию пикселя.

static final int WIDTH  = 480;
static final int HEIGHT = 320;

Для окна просмотра будут использованы жестко заданные ширина и высота.

private OrthographicCamera  cam;     // 1
private Texture   texture;    // 2 
private Mesh   mesh;     // 3
private Rectangle   glViewport;    // 4
private float   rotationSpeed;    // 5
  1. С помощью OrthographicCamera экземпляра, мы будет контролировать взгляд на мир.
  2. Текстурное изображение мы будем показывать в роли карты (мира).
  3. Полигональная сетка, простой квадрат, к которому будет применена текстура.
  4. Прямоугольник представляющие окно просмотра
  5. Скорость в углах, с которой камера вращается при каждым вызовом визуализации.
@Override
public void create() {
    rotationSpeed = 0.5f;       // 1
    mesh = new Mesh(true, 4, 6,
      new VertexAttribute(VertexAttributes.Usage.Position, 3, "attr_Position"),
      new VertexAttribute(Usage.TextureCoordinates, 2, "attr_texCoords")); // 2
    texture = new Texture(Gdx.files.internal("img/sc_map.png"));  // 3
    mesh.setVertices(new float[] { 
       -1024f, -1024f, 0, 0, 1,
        1024f, -1024f, 0, 1, 1,
        1024f,  1024f, 0, 1, 0,
       -1024f,  1024f, 0, 0, 0
    });         // 4
    mesh.setIndices(new short[] { 0, 1, 2, 2, 3, 0 });   // 5  
    cam = new OrthographicCamera(WIDTH, HEIGHT);    // 6
    cam.position.set(WIDTH / 2, HEIGHT / 2, 0);    // 7
    glViewport = new Rectangle(0, 0, WIDTH, HEIGHT);   // 8
}

Все устанавливается в onCreate методе.

  1. Устанавливает текущую скорость вращения в 0,5 градуса.
  2. Создает квадратную полигональную сетку. Первый параметр сообщает libgdx сделать сетку статической. Второй параметр сообщает, что сетка будет содержать 4 вершины (квадрат). Третий параметр представляет собой количество индексов, которые будут использоваться. Квадратных состоит из 2 треугольников и каждый треугольник имеет 3 вершины, таким образом, 2 * 3 = 6. Четвертый параметр сообщает о том, что позиция сетки описывается тремя значениями (x, y, z). Пятый параметр устанавливает значение равное 2 для атрибута позиции текстуры.
  3. Создает текстуру из sc_map.png файла. Загрузите текстуру и добавьте ее в assets/src директорию.
  4. Устанавливает массив вершин для полигональной сетки. Первые три элемента являются x, y, z координатами, а следующие два описывают текстурные координаты для этой вершины. Обратите внимание, что Z координата равна 0 , так как мы в 2D. Здесь 4 вершины.
  5. Установка индексов для вершин, что в результате был квадрат.
  6. Создание OrthographicCamera. Два параметра определяют ширину и высота окна просмотра, который будет создан. Мы будем использовать размер окна приложения.
  7. Установка исходного положения камеры в центр экрана. Помните, что (0,0) находится в левом нижнем углу, поэтому начальное положение камеры будет нижней левой часть мира.
  8. Создание прямоугольника с левой нижней точкой (0,0), имеющего ширину и высоту заданную константами (эквивалентными размер окна).

Настройка выполнена. Камера была создана и расположена (указывает) в нижнем левом углу мира.

@Override
public void render() {
    handleInput();    // 1
    GL10 gl = Gdx.graphics.getGL10(); // 2
  
    // Камера --------------------- /
    gl.glClear(GL10.GL_COLOR_BUFFER_BIT); // 3
    gl.glViewport((int) glViewport.x, (int) glViewport.y,
       (int) glViewport.width, (int) glViewport.height); // 4
  
    cam.update();   // 5
    cam.apply(gl);   // 6

    // Текстурирование --------------------- /
    gl.glActiveTexture(GL10.GL_TEXTURE0);  // 7
    gl.glEnable(GL10.GL_TEXTURE_2D); // 8
    texture.bind();    // 9
  
    mesh.render(GL10.GL_TRIANGLES);  // 10
}
  1. Управляет камерой через обновление ее позиции, масштаба, вращения, основанного на нажатие различных клавиш.
  2. Получает OpenGL экземпляре. Должна быть версия 1, так как версия 2 не поддерживается для OrthographicCamera.
  3. Очищает экран (фактически буфер цвета).
  4. Устанавливает окно просмотра для OpenGL контекста, чтобы использовать окно просмотра, которое мы определили во время создания. В данный момент мы не обрабатываем изменение размеров.
  5. Обновляет камеры, очень важный шаг. Так как матрица проекции была изменена в handleInput методе, то этим просто сообщается камере о необходимости пересчитать матрицу проекции и отсечения для камеры.
  6. Устанавливает текущею проекцию и матрицу вида модели камеры. Вызывается после обновления.
  7. Выбирается активная текстура.
  8. Включается возможность наложения текстур.
  9. Привязка текстуры к контексту.
  10. Визуализация полигональной сетки с картой.
private void handleInput() {
    if(Gdx.input.isKeyPressed(Input.Keys.A)) {
        cam.zoom += 0.02;
    }
    if(Gdx.input.isKeyPressed(Input.Keys.Q)) {
        cam.zoom -= 0.02;
    }
    if(Gdx.input.isKeyPressed(Input.Keys.LEFT)) {
        if (cam.position.x > 0)
            cam.translate(-3, 0, 0);
    }
    if(Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {
        if (cam.position.x < 1024)
            cam.translate(3, 0, 0);
    }
    if(Gdx.input.isKeyPressed(Input.Keys.DOWN)) {
        if (cam.position.y > 0)
            cam.translate(0, -3, 0);
    }
    if(Gdx.input.isKeyPressed(Input.Keys.UP)) {
        if (cam.position.y < 1024)
            cam.translate(0, 3, 0);
    }
    if(Gdx.input.isKeyPressed(Input.Keys.W)) {
 cam.rotate(-rotationSpeed, 0, 0, 1);
    }
    if(Gdx.input.isKeyPressed(Input.Keys.E)) {
        cam.rotate(rotationSpeed, 0, 0, 1);
    }
}

Метод handleInput() просто опрашивает пользовательский ввод, если были нажаты определенные клавиши, то камера изменяется соответствующим образом.

Клавиши управления перемещаю камеру, Q и A увеличивают и уменьшают масштаб, W и E вращают камеру. Здесь есть несколько механизмов защиты, которые не позволяют камере выходить за границы мира.

Основным приложением для начальной загрузки обработчика является LWJGL приложение.

public class DesktopCameraController {

    public static void main(String[] args) {
        new LwjglApplication(new OrthographicCameraController(), "2D Camera", 480, 320, false);
    }
}

В результате получается следующее приложение:

ортографическая камера

Большую часть времени нет нужны иметь доступ к свойствам камеры, так как наиболее распространенные сценарии покрыты следующими методами:

Методы и описание
lookAt(float x, float y, float z)

Пересчитывает направление камеры, чтобы камера смотрела на точку, определенную координатами по всем осям. Ось Z игнорируется для 2D.

translate(float x, float y, float z)

Перемещает камеру на заданное значение по каждой оси. Обратите внимание, что ось Z игнорируется для OrthographicCamera.

rotate(float angle, float axisX, float axisY, float axisZ)

Вращает направление и вектор камеры по заданному углу вокруг заданных осей. Направление и вектор верха не будут ортогонализированы. Угол сохраняется, так что камера будет повернута на угол по отношению к своему предыдущему вращению.

update()

Пересчитывает матрицы проекции и вида камера, а так же плоскость отсечения.

apply(GL10 gl)

Устанавливает текущую матрицу проекции и вида модели камеры. Нужно вызывать после update() метода и принимается как параметр экземпляр OpenGL 1.0 или 1.1. В настоящее время отсутствует поддержка для версии OpenGL 2.0.