OpenGL: scale then translate? i jak?

Mam geometrię 2D. Chcę wziąć trochę prostopadłościanu wokół mojej geometrii, a następnie renderować mniejszą wersję w innym miejscu na płaszczyźnie. Oto mniej więcej kod, który muszę zrobić skalowanie i tłumaczenie:

// 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.

Działa to dokładnie tak, jak oczekiwano dla danego wymiaru, gdy dest origin wynosi 0. Ale jeśli początek dla, powiedzmy, x, jest niezerowy, wynik jest nadal skalowany poprawnie, ale wygląda jak (?) jest tłumaczone na coś niemal zerowego na tej osi-okazuje się to nie jest dokładnie to samo, co dest.x było zerem.

Czy ktoś może wskazać coś oczywistego, co mi umyka? Dzięki!

Ostatnia aktualizacja zgodnie z odpowiedziami Bahbara i Marcusa poniżej, zrobiłem kilka dalszych eksperymentów i rozwiązałem to. Komentarz Adama Bowena był podpowiedzią. Brakowało mi dwóch krytycznych faktów:

  1. musiałem skalować się wokół środka geometrii, na której mi zależało.
  2. musiałem zastosować transformaty w odwrotnej kolejności intuicja (dla mnie).
Pierwszy jest dość oczywisty z perspektywy czasu. Ale dla tych ostatnich, dla innych dobrych programistów/złych matematyków, takich jak ja: okazuje się, że moja intuicja działała w tym, co Czerwona Księga {21]} nazywa "wielkim, stałym układem współrzędnych", w którym istnieje płaszczyzna absolutna, a twoja geometria porusza się po tej płaszczyźnie za pomocą przekształceń. To jest OK, ale biorąc pod uwagę naturę matematyki stojącej za układaniem wielu przekształceń w jedną macierz, jest przeciwieństwem tego, jak rzeczy naprawdę działa (zobacz Odpowiedzi poniżej lub Czerwona Księga więcej). Zasadniczo transformacje są "stosowane" w "odwrotnej kolejności" do sposobu, w jaki pojawiają się w kodzie. Oto ostateczne rozwiązanie robocze:
// 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.
Author: genpfault, 2010-02-13

3 answers

W OpenGL, podane macierze są mnożone na prawo od istniejącej macierzy, a wierzchołek znajduje się po prawej stronie wyrażenia.

Tak więc ostatnia operacja, którą określisz, znajduje się w układzie współrzędnych samej geometrii. (Pierwszą z nich jest zwykle transformacja widoku, tzn. odwrotność transformacji kamery do świata.)

Bahbar robi dobry punkt, że trzeba wziąć pod uwagę punkt środkowy do skalowania. (lub punkt obrotu dla obrotów.) Zazwyczaj tłumaczysz tam, obróć / przeskaluj, a następnie Przetłumacz. (lub ogólnie, zastosuj transformację bazy, operację, a następnie odwrotność). To się nazywa zmiana podstawy , którą warto przeczytać.

W każdym razie, aby uzyskać trochę intuicji na temat tego, jak to działa, spróbuj z prostymi wartościami (zero, itp.), a następnie zmodyfikuj je nieco (być może animacja) i zobacz, co się stanie z wyjściem. Wtedy o wiele łatwiej jest zobaczyć, co twoje transformaty rzeczywiście robią z Twoją geometrią.

Update

Że kolejność jest "odwrócona" w.r.t. intuicja jest dość powszechna wśród początkujących programistów OpenGL. Udzielam korepetycji z grafiki komputerowej. Wiele osób reaguje w podobny sposób. Łatwiej jest myśleć o tym, jak robi to OpenGL, jeśli rozważy się użycie pushmatrix / popmatrix podczas renderowania drzewa (Wykres sceny) przekształceń i geometrii. Wtedy obecny porządek rzeczy staje się raczej naturalny, a przeciwieństwo utrudniłoby uzyskanie wszystko, co przydatne.

 17
Author: Macke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-14 17:40:04

Scale, podobnie jak Rotate, działa od początku. jeśli więc przeskalujesz o połowę obiekt, który obejmuje segment [10: 20] (np. na osi X), otrzymasz [5: 10]. Obiekt został więc przeskalowany i przesunięty bliżej źródła. Dokładnie to, co zaobserwowałeś.

Dlatego w ogóle najpierw stosuje się skalę(ponieważ obiekty mają tendencję do definiowania wokół 0).

Więc jeśli chcesz skalować obiekt wokół środka punktu, możesz przetłumaczyć obiekt ze środka na początek, skalować tam i Przetłumacz.

Uwaga na marginesie, jeśli najpierw przetłumaczysz, a potem przeskalujesz, wtedy twoja skala zostanie zastosowana do poprzedniego tłumaczenia, dlatego prawdopodobnie miałeś problemy z tą metodą.

 6
Author: Bahbar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-13 20:37:44

Nie grałem z OpenGL ES, tylko trochę z OpenGL.

Wygląda na to, że chcesz przekształcić z innej pozycji niż pochodzenie, nie jestem pewien, ale czy możesz spróbować wykonać transformacje i narysować ten bit w glPushMatrix() i 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();

Oto prosty szkic przetwarzania, który napisałem, aby zilustrować punkt:

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();
}

Oto obraz biegnącego szkicu, narysowany jest ten sam Zielony trójkąt, z zastosowaniem tłumaczenia i skali, następnie czerwony, outsie push / pop 'block', więc nie ma na niego wpływu transformacja:

alt text

HTH, George

 1
Author: George Profenza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-08 14:21:05