2010-02-13 20 views
23

Tengo algo de geometría 2D. Quiero tomar un rectángulo delimitador alrededor de mi geometría, y luego presentar una versión más pequeña de ella en otro lugar del avión. Aquí es más o menos el código que tengo que hacer escala y la traducción:OpenGL: escalar y traducir? ¿y cómo?

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

Esto funciona exactamente como se esperaba para una dimensión dada cuando el origen dest es 0. Pero si el origen para, por ejemplo, x, es distinto de cero, el resultado sigue siendo escalado correctamente pero parece (?) se traduce a algo cercano a cero en ese eje de todos modos ... resulta que no es exactamente lo mismo que si dest.x fuera cero.

¿Alguien puede indicar algo obvio que me falta?

Gracias!

ACTUALIZACIÓN FINAL Según las respuestas de Bahbar y Marcus a continuación, hice más experimentación y resolví esto. El comentario de Adam Bowen fue el consejo. Me faltaban dos hechos críticos:

  1. Necesitaba escalar el centro de la geometría que me importaba.
  2. Necesitaba aplicar las transformaciones en el orden opuesto a la intuición (para mí).

El primero es obvio en retrospectiva. Pero para este último, para otros buenos programadores/malos matemáticos como yo: Resulta que mi intuición estaba operando en lo que el Red Book llama un "Gran Sistema de Coordenadas Fijas", en el cual hay un plano absoluto, y su geometría se mueve en ese plano usando transformaciones. Esto está bien, pero dada la naturaleza de las matemáticas detrás de apilar transformaciones múltiples en una matriz, es lo opuesto a cómo funcionan realmente las cosas (ver respuestas a continuación o Libro Rojo para obtener más información). Básicamente, las transformaciones se "aplican" en "orden inverso" a la forma en que aparecen en el código. Aquí está la solución de trabajo final:

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

Respuesta

16

En OpenGL, las matrices que especifique se multiplican a la derecha de la matriz existente, y el vértice está en el extremo derecho de la expresión.

Por lo tanto, la última operación que especifique está en el sistema de coordenadas de la propia geometría. (La primera suele ser la transformación de la vista, es decir, inversa a la transformación de su cámara al mundo).

Bahbar señala que es necesario tener en cuenta el punto central para escalar. (o el punto de pivote para las rotaciones). Por lo general, traduces allí, rotas/escalas, luego vuelves a traducir. (o, en general, aplicar transformación de base, la operación, luego la inversa).Esto se llama Change of Basis, que es posible que desee leer.

De todos modos, para tener un poco de intuición sobre cómo funciona, intente con algunos valores simples (cero, etc.) y luego modifíquelos ligeramente (tal vez una animación) y vea qué pasa con la salida. Entonces es mucho más fácil ver lo que tus transformaciones realmente le están haciendo a tu geometría.

actualización

que la orden es "invertido" w.r.t. la intuición es bastante común entre los principiantes codificadores OpenGL. He estado tutorizando un curso de gráficos por computadora y muchos reaccionan de manera similar. Resulta más fácil pensar en cómo OpenGL lo hace si se tiene en cuenta el uso de pushmatrix/popmatrix mientras se renderiza un árbol (escena-gráfico) de transformaciones y geometrías. Entonces el orden actual de las cosas se vuelve más natural, y lo contrario haría bastante difícil hacer algo útil.

+1

En resumen, cambie el orden a glTranslate (dest); glScale (escala); glTranslate (fuente); –

+0

Adam: Esto no parece funcionar como sugieres, aunque estoy intrigado. ¿Puedes explicar por qué el orden sería así y quizás ofrecer una idea más explícita de cómo se vería en relación con el código existente? ? Tal vez estoy malinterpretando tu pseudocódigo. ¡Gracias! –

+0

Nevermind: el "no funciona" fue un valor engined en este caso. Después de arreglar eso, estamos en el negocio. Vea la pregunta actualizada para más. ¡Gracias! –

6

escala, al igual que Girar, opera desde el origen. así que si escalas a la mitad un objeto que abarca el segmento [10:20] (en el eje X, por ejemplo), obtienes [5:10]. Por lo tanto, el objeto fue escalado y se acercó al origen. Exactamente lo que has observado

Es por eso que primero aplica Escala en general (porque los objetos tienden a definirse alrededor de 0).

Si quiere escalar un objeto alrededor del punto central, puede trasladar el objeto desde el centro al origen, escalar allí y traducir de nuevo.

Nota al margen, si primero traduce y luego escala, su escala se aplica a la traducción anterior, por lo que probablemente haya tenido problemas con este método.

+0

Gracias por la respuesta. ¿Puedes deletrear un cálculo matricial de matemáticas qué operaciones haría, hacer la conversión a origen apropiada, luego escalar, luego traducir de nuevo? Intenté apilar estas tres cosas en sucesión (traducir por origen de fuente negativo, luego escalar por factores de escala, luego traducir por origen de destino) en tres llamadas antes de dibujar la geometría, pero no obtuve resultados visibles. –

+0

Ah, quizás necesito hacer las traducciones relativas al centro del rect en lugar del origen. Intentaré esto. –

+0

No. Sin dados con: translate-by-negative-center-of-source, luego scale, luego translate-by-positive-center-of-dest. –

1

No he jugado con OpenGL ES, solo un poco con OpenGL.

Parece que quieres transformarte desde una posición diferente a la original, no estoy seguro, pero ¿puedes intentar hacer las transformaciones y dibujar ese bit dentro de glPushMatrix() and 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(); 

He aquí un bosquejo simple de procesamiento que escribí para ilustrar el punto:

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

Aquí está una imagen de la carrera boceto, el mismo triángulo verde se dibuja, con la traducción y la escala aplicada, entonces el uno rojo, outsie el empuje/pop 'bloque', por lo que no se ve afectada por la transformación:

alt text

HTH, Ge orge

+0

Gracias George, pero el push/pop no es realmente el problema; Ya estoy haciendo push/pop fuera del código del que hice el fragmento. ¡Gracias por ilustrar con imágenes! Una buena visualización. –