OpenGL: масштаб, затем перевод? и как? - PullRequest
23 голосов
/ 13 февраля 2010

У меня есть 2D геометрия. Я хочу взять ограничивающий прямоугольник вокруг моей геометрии, а затем сделать ее уменьшенную версию где-нибудь еще на плоскости. Вот более или менее код, который я должен сделать масштабирование и перевод:

// source and dest are arbitrary rectangles.
float scaleX = dest.width / source.width;
float scaleY = dest.height / source.height;
float translateX = dest.x - source.x;
float translateY = dest.y - source.y;

glScalef(scaleX, scaleY, 0.0);
glTranslatef(translateX, translateY, 0.0);
// Draw geometry in question with its normal verts.

Это работает точно так, как ожидается для данного измерения, когда источник dest равен 0. Но если источник, скажем, x, отличен от нуля, результат по-прежнему корректно масштабируется, но выглядит (?), Что переводится во что-то близкое к нулю в любом случае на этой оси - оказывается, это не совсем то же самое, что если бы dest.x были равны нулю.

Может ли кто-то указать на очевидное, что мне не хватает?

Спасибо!

ЗАКЛЮЧИТЕЛЬНОЕ ОБНОВЛЕНИЕ Согласно ответам Бахбара и Маркуса ниже, я провел еще несколько экспериментов и решил это. Комментарий Адама Боуэна был подсказкой. Мне не хватало двух важных фактов:

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

Первый вид очевиден в ретроспективе. Но для последнего, для других хороших программистов / плохих математиков, таких как я: Оказывается, моя интуиция работала в том, что Красная книга называет "Великой фиксированной системой координат", в которой существует абсолютная плоскость, и ваша геометрия перемещается на этой плоскости, используя преобразования. Это нормально, но учитывая природу математики, лежащей в основе объединения нескольких преобразований в одну матрицу, это противоположно тому, как все работает на самом деле (подробности см. В ответах ниже или в Красной книге). По сути, преобразования «применяются» в «обратном порядке» к тому, как они появляются в коде. Вот окончательное рабочее решение:

// source and dest are arbitrary rectangles.
float scaleX = dest.width / source.width;
float scaleY = dest.height / source.height;
Point sourceCenter = centerPointOfRect(source);
Point destCenter = centerPointOfRect(dest);

glTranslatef(destCenter.x, destCenter.y, 0.0);
glScalef(scaleX, scaleY, 0.0);
glTranslatef(sourceCenter.x * -1.0, sourceCenter.y * -1.0, 0.0);
// Draw geometry in question with its normal verts.

Ответы [ 3 ]

18 голосов
/ 14 февраля 2010

В OpenGL указанные вами матрицы умножаются справа от существующей матрицы, а вершина находится в правой части выражения.

Таким образом, последняя указанная вами операция находится в системе координат самой геометрии. (Первым обычно является преобразование вида, то есть обратное преобразованию вашей камеры в мир.)

Бахбар указывает на то, что вам нужно учитывать центральную точку для масштабирования. (или точка поворота для поворотов.) Обычно вы переводите туда, поворачиваете / масштабируете, затем переводите обратно. (или вообще примените базисное преобразование, операцию, затем обратное). Это называется Изменение основы , о котором вы, возможно, захотите прочитать.

В любом случае, чтобы получить некоторую интуицию о том, как это работает, попробуйте с некоторыми простыми значениями (ноль и т. Д.), Затем слегка измените их (возможно, анимацию) и посмотрите, что происходит с выходными данными. Тогда гораздо легче увидеть, что ваши преобразования на самом деле делают с вашей геометрией.

Обновление

что заказ "обратный" с.р. Интуиция довольно распространена среди начинающих OpenGL-кодеров. Я преподавал курс компьютерной графики, и многие реагируют аналогичным образом. Становится легче думать о том, как это делает OpenGL, если рассмотреть использование pushmatrix / popmatrix при рендеринге дерева (графа сцены) преобразований и геометрий. Тогда текущий порядок вещей станет довольно естественным, а обратное затруднит выполнение чего-либо полезного.

6 голосов
/ 13 февраля 2010

Шкала, так же как и Rotate, работает с начала координат. так что если вы масштабируете наполовину объект, который охватывает сегмент [10:20] (например, по оси X), вы получите [5:10]. Поэтому объект был масштабирован и перемещен ближе к началу координат. Именно то, что вы наблюдали.

Вот почему вы сначала применяете Scale в целом (потому что объекты имеют тенденцию быть определены около 0).

Поэтому, если вы хотите масштабировать объект вокруг точки Center, вы можете перевести объект из Center в начало координат, масштабировать его и перевести обратно.

Примечание: если вы сначала переводите, а затем масштабируете, тогда ваша шкала применяется к предыдущему переводу, поэтому у вас, вероятно, возникли проблемы с этим методом.

1 голос
/ 14 февраля 2010

Я не играл с OpenGL ES, просто немного с OpenGL.

Звучит так, будто вы хотите преобразовать из другой позиции, в отличие от источника, не уверен, но можете ли вы попытаться выполнить преобразования и отрисовку этого бита в glPushMatrix () и glPopMatrix () ?

e.g.

// source and dest are arbitrary rectangles.
float scaleX = dest.width / source.width;
float scaleY = dest.height / source.height;
float translateX = dest.x - source.x;
float translateY = dest.y - source.y;

glPushMatrix();
  glScalef(scaleX, scaleY, 0.0);
  glTranslatef(translateX, translateY, 0.0);
  // Draw geometry in question with its normal verts.
  //as if it were drawn from 0,0
glPopMatrix();

Вот простой эскиз обработки, который я написал, чтобы проиллюстрировать это:

import processing.opengl.*;
import javax.media.opengl.*;


void setup() {
  size(500, 400, OPENGL);
}

void draw() {
  background(255);
  PGraphicsOpenGL pgl = (PGraphicsOpenGL) g;
  GL gl = pgl.beginGL();  


  gl.glPushMatrix();
    //transform the 'pivot'
    gl.glTranslatef(100,100,0);
    gl.glScalef(10,10,10);
    //draw something from the 'pivot'
    gl.glColor3f(0, 0.77, 0);
    drawTriangle(gl);
  gl.glPopMatrix();
  //matrix poped, we're back to orginin(0,0,0), continue as normal
  gl.glColor3f(0.77, 0, 0);
  drawTriangle(gl);
  pgl.endGL();
}

void drawTriangle(GL gl){
  gl.glBegin(GL.GL_TRIANGLES);
  gl.glVertex2i(10, 0);
  gl.glVertex2i(0, 20);
  gl.glVertex2i(20, 20);
  gl.glEnd();
}

Вот изображение бегущего эскиза, нарисован тот же зеленый треугольник с примененным переводом и масштабом, а затем красный, выходящий за рамки блока push / pop, поэтому преобразование на него не влияет:

alt text

НТН, George

...