1

我正在 iPad 上制作 2d OpenGL 应用程序。我需要实现捏/缩放。

我想在(x,y)平面上移动相机并通过捏合手势控制相机x,y和值。z

方法中的每一帧update我都像这样制作视图矩阵(相机)

lookAt = GLKMatrix4MakeLookAt(view_x, view_y, view_z, view_x, view_y, 0.0f, 0.0f, 1.0f, 0.0f);

其中 view_x、view_y 和 view_z 在程序开始时定义如下: view_x = view_y = 0.0f; view_z = kStartZoom; kStartZoom 是 3000。所以相机在 (0,0,3000) 并看向 (0,0,0)

处理夹点事件的几乎可行的解决方案是

- (IBAction) handlePinch:(UIPinchGestureRecognizer*) recognizer {
switch (recognizer.state)
{
    case UIGestureRecognizerStateBegan:
    {
        if (recognizer.numberOfTouches == 2)
        {
            prevTouchOrigin1 = [recognizer locationOfTouch:0 inView:self.view];
            prevTouchOrigin2 = [recognizer locationOfTouch:1 inView:self.view];
        }
    } break;
    case UIGestureRecognizerStateChanged:
    {
        if (recognizer.numberOfTouches == 2)
        {
            CGFloat newDistance, oldDistance;

            oldDistance = distanceBetweenTwoCGPoints(&prevTouchOrigin1, &prevTouchOrigin2);
            currTouchOrigin1 = [recognizer locationOfTouch:0 inView:self.view];
            currTouchOrigin2 = [recognizer locationOfTouch:1 inView:self.view];

            newDistance = distanceBetweenTwoCGPoints(&currTouchOrigin1, &currTouchOrigin2);

            if (newDistance == 0 || oldDistance == 0)
            {
                scaleFactor = 1;
            } else {
                scaleFactor = oldDistance / newDistance;
            }

            GLfloat check = view_z * scaleFactor;
            if (check < kMinZoom || check > kMaxZoom)
                return;

            view_z *= scaleFactor;

            // translate

            // formula: newPos = currTouchOrigin + (objectOrigin - prevTouchOrigin) * scaleFactor

            static CGPoint translationDelta;
            GLfloat z_ratio = view_z_old / view_z;

            newPos1.x = currTouchOrigin1.x - ((prevTouchOrigin1.x - view_x) * scaleFactor);
            newPos1.y = currTouchOrigin1.y - ((prevTouchOrigin1.y - view_y) * scaleFactor);

            newPos2.x = currTouchOrigin2.x - ((prevTouchOrigin2.x - view_x) * scaleFactor);
            newPos2.y = currTouchOrigin2.y - ((prevTouchOrigin2.y - view_y) * scaleFactor);

            midpoint = CGPointMidpoint(&newPos1, &newPos2);

            translationDelta = CGPointMake(midpoint.x - view_x, midpoint.y - view_y);

            view_x += translationDelta.x;
            view_y -= translationDelta.y;

            prevTouchOrigin1 = currTouchOrigin1;
            prevTouchOrigin2 = currTouchOrigin2;
        }
    } break;
    case UIGestureRecognizerStateEnded:
    {
    } break;
    default :
    {
    }
}}

几乎都在工作。

我在 x,y 上有更多的运动,然后我需要让相机四处摆动。

问题是我没有应用从屏幕坐标到世界坐标的一些转换吗?

可能是什么问题?我正在研究的其他示例仅根据前一个手指位置和最后一个手指位置之间的距离修改相机位置,这就是我正在做的事情。

4

3 回答 3

2

这是我的解决方案:

我有两个类,一个负责所有 OpenGL 的东西(RenderViewController),另一个负责所有手势识别器以及 OpenGL 部分和应用程序其他部分之间的通信(EditViewController)。

这是有关手势的代码:

编辑器视图控制器

它捕获手势并将有关它们的信息发送到 RenderViewController。由于坐标系不同,您必须小心。

- (void) generateGestureRecognizers {

    //Setup gesture recognizers
    UIRotationGestureRecognizer *twoFingersRotate = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(twoFingersRotate:)];
    [self.hitView addGestureRecognizer:twoFingersRotate];

    UIPinchGestureRecognizer *twoFingersScale = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(twoFingersScale:)];
    [self.hitView addGestureRecognizer:twoFingersScale];

    UIPanGestureRecognizer *oneFingerPan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(oneFingerPan:)];
    [self.hitView addGestureRecognizer:oneFingerPan];


    [twoFingersRotate setDelegate:self];
    [twoFingersScale setDelegate:self];
    [oneFingerPan setDelegate:self];
}

- (void) oneFingerPan:(UIPanGestureRecognizer *) recognizer {    

    //Handle pan gesture
    CGPoint translation = [recognizer translationInView:self.hitView];
    CGPoint location = [recognizer locationInView:self.hitView];

    //Send info to renderViewController
    [self.renderViewController translate:traslation];

    //Reset recognizer so change doesn't accumulate
    [recognizer setTranslation:CGPointZero inView:self.hitView];    
}

- (void) twoFingersRotate:(UIRotationGestureRecognizer *) recognizer {  

    //Handle rotation gesture
    CGPoint locationInView = [recognizer locationInView:self.hitView];
    locationInView = CGPointMake(locationInView.x - self.hitView.bounds.size.width/2, locationInView.y - self.hitView.bounds.size.height/2);

    if ([recognizer state] == UIGestureRecognizerStateBegan || [recognizer state] == UIGestureRecognizerStateChanged) {

        //Send info to renderViewController
        [self.renderViewController rotate:locationInView degrees:recognizer.rotation];

        //Reset recognizer
        [recognizer setRotation:0.0];
    }
}

- (void) twoFingersScale:(UIPinchGestureRecognizer *) recognizer {

    //Handle scale gesture
    CGPoint locationInView = [recognizer locationInView:self.hitView];
    locationInView = CGPointMake(locationInView.x - self.hitView.bounds.size.width/2, locationInView.y - self.hitView.bounds.size.height/2);

    if ([recognizer state] == UIGestureRecognizerStateBegan || [recognizer state] == UIGestureRecognizerStateChanged) {

        //Send info to renderViewController
        [self.renderViewController scale:locationInView ammount:recognizer.scale];

        //reset recognizer
        [recognizer setScale:1.0];
    }

}

//This allows gestures recognizers to happen simultaniously
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    if (gestureRecognizer.view != otherGestureRecognizer.view)
        return NO;

    if ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]] || [otherGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]])
        return NO;

    return YES;
}

渲染视图控制器

对于每一帧,modelViewMatrix 是根据其他三个临时矩阵(平移、缩放和旋转)计算得出的

- (void) setup {

    //Creates the modelViewMatrix from the initial position, rotation and scale
    translatemt = GLKMatrix4Translate(GLKMatrix4Identity, initialPosition.x, initialPosition.y, 0.0);
    scalemt = GLKMatrix4Scale(GLKMatrix4Identity, initialScale, initialScale, 1.0);
    rotatemt = GLKMatrix4Rotate(GLKMatrix4Identity, initialRotation, 0.0, 0.0, 1.0);
    self.modelViewMatrix = GLKMatrix4Multiply(GLKMatrix4Multiply(GLKMatrix4Multiply(translatemt, rotatemt), scalemt), GLKMatrix4Identity);

    //set these back to identities to take further modifications (they'll update the modelViewMatrix)
    scalemt = GLKMatrix4Identity;
    rotatemt = GLKMatrix4Identity;
    translatemt = GLKMatrix4Identity;


    //rest of the OpenGL setup
    [self setupOpengGL];

}

//public interface
- (void) translate:(CGPoint) location {
    //Update the translation temporary matrix
    translatemt = GLKMatrix4Translate(translatemt, location.x, -location.y, 0.0);
}

//public interface
- (void) rotate:(CGPoint) location degrees:(CGFloat) degrees {
    //Update the rotation temporary matrix
    rotatemt = GLKMatrix4Translate(GLKMatrix4Identity, location.x, -location.y, 0.0);
    rotatemt = GLKMatrix4Rotate(rotatemt, -degrees, 0.0, 0.0, 1.0);
    rotatemt = GLKMatrix4Translate(rotatemt, -location.x, location.y, 0.0);
}

//public interface
- (void) scale:(CGPoint) location ammount:(CGFloat) ammount {
    //Update the scale temporary matrix
    scalemt = GLKMatrix4Translate(GLKMatrix4Identity, location.x, -location.y, 0.0);
    scalemt = GLKMatrix4Scale(scalemt, ammount, ammount, 1.0);
    scalemt = GLKMatrix4Translate(scalemt, -location.x, location.y, 0.0);
}

- (void)update {

    //this is done before every render update. It generates the modelViewMatrix from the temporary matrices
    self.modelViewMatrix = GLKMatrix4Multiply(GLKMatrix4Multiply(GLKMatrix4Multiply(rotatemt, translatemt), scalemt), self.modelViewMatrix);

    //And then set them back to identities
    translatemt = GLKMatrix4Identity;
    rotatemt = GLKMatrix4Identity;
    scalemt = GLKMatrix4Identity;

    //set the modelViewMatrix for the effect (this is assuming you are using OpenGL es 2.0, but it would be similar for previous versions
    self.effect.transform.modelviewMatrix = self.modelViewMatrix;
}
于 2013-04-15T08:50:44.990 回答
0

我不太确定我是否理解您的问题,但对于捏识别我使用 UIPinchGestureRecongizer:

http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIPinchGestureRecognizer_Class/Reference/Reference.html#//apple_ref/occ/cl/UIPinchGestureRecognizer

于 2013-04-10T19:20:04.613 回答
0

此解决方案使用 x,y 平面上方的移动相机。世界保持静止。我们在某个 z 位置,我们通过捏合手势控制相机移动。

所需要的是将单位从以点表示的屏幕空间转换为以离散单位表示的开放式 gl 空间。

我通过在 x,y 平面上以 opengl 单位绘制 50x50 正方形来获得这个常数,相机居中x = 0, y = 0, z = 100

然后我添加了两个UIViews 并设置平移手势来移动这两个视图。通过平移手势,我将它们的帧原点物理地居中于 opengl 正方形的右上角和右下角。点击手势设置为NSLog它们的起源。这就是我得到这个常数的方式:

static const GLfloat k = 0.125f; // 445 CG units is 50 discrete OpenGL units at Z = 100; 0.112359f

因此,对于任意相机 z 位置,我可以计算缩放我们从捏缩放手指位置获得的增量。

一些init方法中的矩阵初始化:

aspect = fabsf(self.view.bounds.size.width / self.view.bounds.size.height);
projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0f), aspect, 0.1f, 5000.0f);
lookAt = GLKMatrix4MakeLookAt(view_x, view_y, view_z, view_x, view_y, 0.0f, 0.0f, 1.0f, 0.0f);

所有伊瓦尔的:

GLfloat view_x;
GLfloat view_y;
GLfloat view_z;
GLfloat view_z_base;

CGPoint currTouchOrigin1, currTouchOrigin2;
CGPoint prevTouchOrigin1, prevTouchOrigin2;
CGPoint newPos1, newPos2;
CGPoint midpoint;
CGFloat scaleFactor;

我们通过 view_x、view_y 和 view_z ivars 移动相机。

所有常量:

static const GLfloat k              = 0.125f; // 445 CG units is 50 discrete OpenGL units at Z = 100; 0.112359f
static const GLfloat default_z      = 100.0f;
static const GLfloat kMinZoom       = 30.0f;
static const GLfloat kMaxZoom       = 4000.0f;
static const GLfloat kStartZoom     = 200.0f;

这是全捏缩放处理程序:

- (IBAction) handlePinch:(UIPinchGestureRecognizer*) recognizer {

switch (recognizer.state)
        {
            case UIGestureRecognizerStateBegan:
            {
                if (recognizer.numberOfTouches == 2)
                {
                    prevTouchOrigin1 = [recognizer locationOfTouch:0 inView:self.view];
                    prevTouchOrigin2 = [recognizer locationOfTouch:1 inView:self.view];
                }
            } break;
            case UIGestureRecognizerStateChanged:
            {
                if (recognizer.numberOfTouches == 2)
                {
                    CGFloat newDistance, oldDistance;

            oldDistance = distanceBetweenTwoCGPoints(&prevTouchOrigin1, &prevTouchOrigin2);
            currTouchOrigin1 = [recognizer locationOfTouch:0 inView:self.view];
            currTouchOrigin2 = [recognizer locationOfTouch:1 inView:self.view];

            newDistance = distanceBetweenTwoCGPoints(&currTouchOrigin1, &currTouchOrigin2);

            if (newDistance == 0 || oldDistance == 0)
            {
                scaleFactor = 1;
                //break;
            } else {
                //scaleFactor = oldDistance / newDistance;
                scaleFactor = newDistance / oldDistance;
            }

            GLfloat view_z_old = view_z;
            view_z /= scaleFactor;
            if (view_z < kMinZoom || view_z > kMaxZoom)
            {
                view_z = view_z_old;
                return;
            }

            // translate

            // formula: newPos = currTouchOrigin + (objectOrigin - prevTouchOrigin) * scaleFactor

            //static CGPoint tmp1, tmp2;
            static CGPoint translationDelta;

            newPos1.x = currTouchOrigin1.x - ((prevTouchOrigin1.x - (screenRect.size.width / 2)) * scaleFactor);
            newPos1.y = currTouchOrigin1.y - ((prevTouchOrigin1.y - (screenRect.size.height / 2)) * scaleFactor);

            newPos2.x = currTouchOrigin2.x - ((prevTouchOrigin2.x - (screenRect.size.width / 2)) * scaleFactor);
            newPos2.y = currTouchOrigin2.y - ((prevTouchOrigin2.y - (screenRect.size.height / 2)) * scaleFactor);

            midpoint = CGPointMidpoint(&newPos1, &newPos2);

            translationDelta = CGPointMake(midpoint.x - (screenRect.size.width / 2), midpoint.y - (screenRect.size.height / 2));

            static GLfloat r = 0.0f;
            static GLfloat k2 = 0.0f;

            r = view_z / default_z;
            k2 = k * r;

            // In openGL, coord sys if first quadrant based
            view_x += -translationDelta.x * k2;
            view_y +=  translationDelta.y * k2;

            // store current coords for next event
            prevTouchOrigin1 = currTouchOrigin1;
            prevTouchOrigin2 = currTouchOrigin2;
        }
    } break;
    case UIGestureRecognizerStateEnded:
    {

    } break;
    default :
    {

    }
  }
}
于 2013-04-17T15:56:44.803 回答