如何从 Projection 和 ModelView 矩阵中获取 CATransform3D? [英] How to get CATransform3D from Projection and ModelView matrices?

查看:21
本文介绍了如何从 Projection 和 ModelView 矩阵中获取 CATransform3D?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所有,

我有一个 iphone 项目,它使用 OpenGL-ES 为给定的模型视图矩阵和给定的投影矩阵绘制 3D 模型.我需要用 CALayer 替换 3D 模型,所以我将模型视图矩阵的值放入 CATransform3D 结构并将其分配给 layer.transform.它运行良好,图层可见并按预期在屏幕上移动,但一段时间后我意识到我的图层行为不够精确,我应该考虑投影矩阵.然后出现了一个问题:当我简单地连接两个矩阵时,我的图层看起来很奇怪(它非常小,大约 2 个像素,而它应该是大约 300 个,因为它很远)或者根本不可见.我该如何解决?

这是一段代码:

- (void)adjustImageObjectWithUserInfo:(NSDictionary *)userInfo{NSNumber *objectID = [userInfo objectForKey:kObjectIDKey];CALayer *layer = [self.imageLayers objectForKey:objectID];如果(!层){ 返回;}CATransform3D 变换 = CATransform3DIdentity;NSArray *modelViewMatrix = [userInfo objectForKey:kModelViewMatrixKey];//获取原始模型视图矩阵;CGFloat *p = (CGFloat *)&transform;for (int i = 0; i <16; ++i){*p = [[modelViewMatrix objectAtIndex:i] floatValue];++p;}//为 Pi/2 围绕 +z 旋转变换 = CATransform3DConcat(变换, CATransform3DMakeRotation(M_PI_2, 0, 0, 1));//投影矩阵变换 = CATransform3DConcat(transform, _projectionMatrix);layer.transform = 变换;}

任何帮助将不胜感激.

解决方案

我最近遇到了这个确切的问题(我也在使用 ARToolKit),我很失望地看到你没有想出答案.我想你现在已经继续前进了,但我想通了,我把它发布给任何其他可能遇到同样问题的迷失的灵魂.

最令我困惑的是,每个人都在谈论通过将 m34 变量设置为一个小的负数来进行 CALayer 透视变换.虽然这确实有效,但它的信息量并不大.我最终意识到变换的工作方式与其他所有变换完全一样,它是齐次坐标的列主变换矩阵.唯一的特殊情况是它必须组合模型视图和投影矩阵,然后在一个矩阵中缩放到 openGL 视口的大小.我首先尝试使用 m34 是一个小的负数样式的矩阵,正如更详细的解释 此处,但最终切换到打开 GL 样式透视转换,如此处所述.事实上,它们彼此等效,它们只是代表了对转换的不同思考方式.>

在我们的例子中,我们试图让 CALayer 变换完全复制开放的 GL 变换.所需要的只是将模型视图、投影和缩放矩阵相乘并翻转 y 轴,以说明设备屏幕原点在左上角而打开的 GL 在左下角的事实.只要图层锚点在 (.5,.5) 并且它的位置正好在屏幕的中心,结果就会与打开的 GL 的变换相同

void attach_CALayer_to_marker(CATransform3D* transform, Matrix4 modelView, Matrix4 openGL_projection, Vector2 GLViewportSize){//重要:这个函数假设CA层在//屏幕的精确中心.Matrix4 flipY = { 1, 0,0,0,0,-1,0,0,0, 0,1,0,0, 0,0,1};//我们希望我们的结果从 -width/2 到 width/2,而不是 -1 到 1,相同//对于高度CGFloat ScreenScale = [[UIScreen mainScreen] scale];浮动 xscl = GLViewportSize.x/ScreenScale/2;浮动 yscl = GLViewportSize.y/ScreenScale/2;//打开的GL透视矩阵投影到一个2x2x2的立方体上.把它放到//设备屏幕需要缩放到正确的大小但是//保持打开的GL窗口指定的纵横比.Matrix4 scalingMatrix = {xscl,0 ,0,0,0, yscl,0,0,0, 0 ,1,0,0, 0 ,0,1};//Open GL 从底部开始测量 y,CALayers 从顶部开始测量,所以在//结束整个投影必须在xz平面上翻转.//当这种情况发生时,CALayer 的内容将被颠倒.//为了纠正它们一开始就颠倒了,//然后它们将在最后翻转右侧.矩阵翻转 = MatrixMakeFromProduct(modelView,flipY);矩阵未缩放 = MatrixMakeFromProduct(openGL_projection, 翻转);矩阵缩放 = MatrixMakeFromProduct(scalingMatrix, unscaled);//翻转xz平面以将原点移动到底部而不是顶部矩阵最终 = SiMatrix4MakeFromProduct(flipY, scaled);*transform = convert_your_matrix_object_to_CATransform3D(最终);}

该函数采用开放的 GLprojection 和 openGL 视图大小,并使用它们为 CALayer 生成正确的变换.CALayer 大小应以打开的 GL 场景的单位指定.OpenGL 视口实际上包含 4 个变量,[xoffset,yoffset,x,y] 但前两个不相关,因为 CALayer 的 Origin 位于屏幕中央以对应 OpenGL 3d Origin.

只需将 Matrix 替换为您可以访问的任何通用 4x4 列主要矩阵类.只要确保以正确的顺序乘以矩阵,任何事情都行得通.所有这些本质上都是复制 OpenGL 管道(减去剪辑).

all,

I've got an iphone project that draws a 3D model using OpenGL-ES for a given model view matrix and given projection matrix. I needed to replace 3D model with CALayer, so I put values of model view matrix into CATransform3D structure and assigned it to layer.transform. It worked well, layer was visible and moved on the screen as expected, but after some time I realized that my layers behavior is not precise enough and I should take projection matrix into account. And then a problem appeared: when I simply concatenate two matrices my layer looks odd (it is very small, about 2 pixels, while it is supposed to be about 300, as it is far far away) or is not visible at all. How can I solve it?

Here is the piece of code:

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

Any help will be appreciated.

解决方案

I recently ran into this exact problem (I was using ARToolKit as well) and I was very disappointed to see that you hadn't figured out the answer. I imagine you have moved on now but I figured it out and I am posting it for any other lost soul who might come through with this same problem.

The most confusing thing for me was that everyone talks about making a CALayer perspective transform by setting the m34 variable to a small negative number. Although that does work it is not very informative at all. What I eventually realized is that the transform works exactly like every other transform, it is a column major transformation matrix for homogenous coordinates. The only special case is that it must combine the model view and projection matrices, and then scale to the size of the openGL viewport all in one matrix. I started by trying to use a matrix in the style where m34 is a small negative number as explained in much greater detail here but eventually switched to open GL style perspective transforms as explained here. They are in fact equivalent to one another they just represent different ways of thinking about the transform.

In our case we are trying to make the CALayer transform exactly replicate an open GL transform. All that requires is multiplying together the modelview,projection,and scaling matrices and flipping the y axis to account for the fact that the device screen origin is top left and open GL is bottom left. As long as the layer anchor is at (.5,.5) and its position is exactly in the center of the screen the result will be identical to the open GL's transform

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

This function takes the open GLprojection, and openGL view size and uses them to generate the correct transform for the CALayer. The CALayer size should be specified in the units of the open GL scene. The OpenGL viewport actually contains 4 variables, [xoffset,yoffset,x,y] but the first two are not relevant because the Origin of the CALayer is put in the center of the screen to correspond to the OpenGL 3d Origin.

Just replace Matrix with whatever generic 4x4 column major matrix class you have access to. Anything will work just make sure you multiply your matrices in the right order. All this is essentially doing is replicating the OpenGL pipeline (minus clipping).

这篇关于如何从 Projection 和 ModelView 矩阵中获取 CATransform3D?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆