traslacion rotacion matrices escalamiento opengl-es calayer 3d catransform3d projection-matrix

opengl-es - rotacion - matrices opengl



¿Cómo obtener CATransform3D a partir de matrices de proyección y ModelView? (4)

todas,

Tengo un proyecto de iPhone que dibuja un modelo 3D usando OpenGL-ES para una matriz de vista de modelo dada y una matriz de proyección dada. Necesitaba reemplazar el modelo 3D con CALayer, así que puse los valores de la matriz de vista del modelo en la estructura CATransform3D y se los layer.transform a layer.transform . Funcionó bien, la capa era visible y se movía en la pantalla como se esperaba, pero después de un tiempo me di cuenta de que el comportamiento de mis capas no es lo suficientemente preciso y debería tener en cuenta la matriz de proyección. Y luego apareció un problema: cuando simplemente concateno dos matrices, mi capa parece impar (es muy pequeña, unos 2 píxeles, mientras que se supone que es aproximadamente 300, ya que está muy lejos) o no es visible en absoluto. ¿Cómo puedo resolverlo?

Aquí está la pieza de código:

- (void)adjustImageObjectWithUserInfo:(NSDictionary *)userInfo { NSNumber *objectID = [userInfo objectForKey:kObjectIDKey]; CALayer *layer = [self.imageLayers objectForKey:objectID]; if (!layer) { return; } CATransform3D transform = CATransform3DIdentity; NSArray *modelViewMatrix = [userInfo objectForKey:kModelViewMatrixKey]; // Get raw model view matrix; CGFloat *p = (CGFloat *)&transform; for (int i = 0; i < 16; ++i) { *p = [[modelViewMatrix objectAtIndex:i] floatValue]; ++p; } // Rotate around +z for Pi/2 transform = CATransform3DConcat(transform, CATransform3DMakeRotation(M_PI_2, 0, 0, 1)); // Project with projection matrix transform = CATransform3DConcat(transform, _projectionMatrix); layer.transform = transform; }

Cualquier ayuda será apreciada.


¿Tomó en cuenta que las capas se procesan en un contexto GL que ya tiene una matriz de orto proyección?

Vea el comentario introductorio en Mac; esta clase es privada en iPhone, pero los principios son los mismos.

Además, las matrices OpenGL se transponen en memoria en comparación con CATransform3D. Toma eso en cuenta, también; mientras que la mayoría de los resultados parecen iguales, algunos no lo serán.


Dos posibles problemas:

1) El orden de concatenación. La matemática matricial clásica es de derecha a izquierda. Entonces intenta

CATransform3DConcat(_projectionMatrix, transform)

y

2) El valor del coeficiente de proyección es incorrecto. ¿Cuáles son los valores que estás usando?

Espero que esto ayude.


Me deshice de la matriz de proyección y es la mejor variante que tengo:

- (void)adjustTransformationOfLayerWithMarkerId:(NSNumber *)markerId forModelViewMatrix:(NSArray *)modelViewMatrix { CALayer *layer = [self.imageLayers objectForKey:markerId]; ... CATransform3D transform = CATransform3DIdentity; CGFloat *p = (CGFloat *)&transform; for (int i = 0; i < 16; ++i) { *p = [[modelViewMatrix objectAtIndex:i] floatValue]; ++p; } transform.m44 = (transform.m43 > 0) ? transform.m43/kZDistanceWithoutDistortion : 1; CGFloat angle = -M_PI_2; if (self.delegate.interfaceOrientation == UIInterfaceOrientationLandscapeLeft) { angle = M_PI; } if (self.delegate.interfaceOrientation == UIInterfaceOrientationLandscapeRight) { angle = 0; } if (self.delegate.interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) { angle = M_PI_2; } transform = CATransform3DConcat(transform, CATransform3DMakeRotation(angle, 0, 0, -1)); transform = CATransform3DConcat(CATransform3DMakeScale(-1, 1, 1), transform); // Normalize transformation CGFloat scaleFactor = 1.0f / transform.m44; transform.m41 = transform.m41 * scaleFactor; transform.m42 = transform.m42 * scaleFactor; transform.m43 = transform.m43 * scaleFactor; transform = CATransform3DScale(transform, scaleFactor, scaleFactor, scaleFactor); transform.m44 = 1; BOOL disableAction = YES; ... [CATransaction begin]; [CATransaction setDisableActions:disableAction]; // Disable animation for layer to move faster layer.transform = transform; [CATransaction commit]; }

No era absolutamente preciso, pero era lo suficientemente preciso para mis propósitos. La deflexión se hace evidente cuando el desplazamiento x o y era aproximadamente del tamaño de la pantalla.


Recientemente me encontré con este problema exacto (también estaba usando ARToolKit) y me decepcionó ver que no habías descubierto la respuesta. Me imagino que ya te has mudado, pero lo descubrí y lo estoy publicando para cualquier otra alma perdida que pueda tener este mismo problema.

Lo más confuso para mí fue que todos hablan de hacer una transformación de perspectiva CALayer al establecer la variable m34 en un pequeño número negativo. Aunque eso funciona, no es muy informativo. Lo que finalmente me di cuenta es que la transformación funciona exactamente como cualquier otra transformación, es una matriz de transformación de columna principal para coordenadas homogéneas. El único caso especial es que debe combinar la vista del modelo y las matrices de proyección, y luego escalar al tamaño de la ventana gráfica OpenGL todo en una matriz. Empecé tratando de usar una matriz en el estilo donde m34 es un número negativo pequeño, como se explica con mucho más detalle aquí, pero finalmente cambié a las transformaciones de perspectiva abiertas del estilo GL como se explica aquí . De hecho, son equivalentes entre sí, solo representan diferentes formas de pensar acerca de la transformación.

En nuestro caso, intentamos que la transformación CALayer reproduzca exactamente una transformación GL abierta. Todo lo que se necesita es multiplicar juntas las matrices de modelos, proyecciones y escalas y voltear el eje y para tener en cuenta el hecho de que el origen de la pantalla del dispositivo se encuentra arriba a la izquierda y GL abierta está abajo a la izquierda. Siempre que el anclaje de capa esté en (.5, .5) y su posición esté exactamente en el centro de la pantalla, el resultado será idéntico al de la transformada GL abierta

void attach_CALayer_to_marker(CATransform3D* transform, Matrix4 modelView, Matrix4 openGL_projection, Vector2 GLViewportSize) { //Important: This function assumes that the CA layer has its origin in the //exact center of the screen. Matrix4 flipY = { 1, 0,0,0, 0,-1,0,0, 0, 0,1,0, 0, 0,0,1}; //instead of -1 to 1 we want our result to go from -width/2 to width/2, same //for height CGFloat ScreenScale = [[UIScreen mainScreen] scale]; float xscl = GLViewportSize.x/ScreenScale/2; float yscl = GLViewportSize.y/ScreenScale/2; //The open GL perspective matrix projects onto a 2x2x2 cube. To get it onto the //device screen it needs to be scaled to the correct size but //maintaining the aspect ratio specified by the open GL window. Matrix4 scalingMatrix = {xscl,0 ,0,0, 0, yscl,0,0, 0, 0 ,1,0, 0, 0 ,0,1}; //Open GL measures y from the bottom and CALayers measure from the top so at the //end the entire projection must be flipped over the xz plane. //When that happens the contents of the CALayer will get flipped upside down. //To correct for that they are flipped updside down at the very beginning, //they will then be flipped right side up at the end. Matrix flipped = MatrixMakeFromProduct(modelView, flipY); Matrix unscaled = MatrixMakeFromProduct(openGL_projection, flipped); Matrix scaled = MatrixMakeFromProduct(scalingMatrix, unscaled); //flip over xz plane to move origin to bottom instead of top Matrix Final = SiMatrix4MakeFromProduct(flipY, scaled); *transform = convert_your_matrix_object_to_CATransform3D(Final); }

Esta función toma la GL abierta y el tamaño de vista de OpenGL y los usa para generar la transformación correcta para el CALayer. El tamaño de CALayer debe especificarse en las unidades de la escena GL abierta. La ventana gráfica OpenGL en realidad contiene 4 variables, [xoffset, yoffset, x, y] pero las dos primeras no son relevantes porque el origen de CALayer se coloca en el centro de la pantalla para que corresponda con OpenGL 3d Origin.

Simplemente reemplace Matrix con cualquier clase de matriz principal de columna 4x4 genérica a la que tenga acceso. Todo funcionará solo asegúrate de multiplicar tus matrices en el orden correcto. Todo esto es esencialmente hacer la replicación de la tubería OpenGL (sin recorte).